MIDAS entity coercion: raw API responses → idiomatic Clojure maps.
Two-layer data model: Raw layer — direct JSON→EDN, PascalCase keys, string values Coerced layer — namespaced keywords, native types (java.time, BigDecimal)
Time handling: every coerced timestamp is a ZonedDateTime in the zone
configured on the client (per-instance via :zone, default
America/Los_Angeles). MIDAS mixes Z-suffixed UTC fields with bare
wall-clock fields on the wire; both are normalised to ZonedDateTime in
the configured zone — the instant is preserved for Z-suffixed values
and assumed-zone-local for bare values. See
https://github.com/grid-coordination/midas-api-specs/blob/main/doc/datetime-and-timezone.md
All coerced entities carry the original raw data as :midas/raw metadata.
MIDAS entity coercion: raw API responses → idiomatic Clojure maps. Two-layer data model: Raw layer — direct JSON→EDN, PascalCase keys, string values Coerced layer — namespaced keywords, native types (java.time, BigDecimal) Time handling: every coerced timestamp is a `ZonedDateTime` in the zone configured on the client (per-instance via `:zone`, default `America/Los_Angeles`). MIDAS mixes Z-suffixed UTC fields with bare wall-clock fields on the wire; both are normalised to ZonedDateTime in the configured zone — the instant is preserved for Z-suffixed values and assumed-zone-local for bare values. See https://github.com/grid-coordination/midas-api-specs/blob/main/doc/datetime-and-timezone.md All coerced entities carry the original raw data as :midas/raw metadata.
(->lookup-entry raw)Coerce a raw LookupEntry.
Raw shape: {:UploadCode "PG" :Description "Pacific Gas and Electric"}
Coerced shape: {:midas.lookup/code "PG" :midas.lookup/description "Pacific Gas and Electric"}
Coerce a raw LookupEntry.
Raw shape:
{:UploadCode "PG" :Description "Pacific Gas and Electric"}
Coerced shape:
{:midas.lookup/code "PG"
:midas.lookup/description "Pacific Gas and Electric"}(->rate-info raw zone)Coerce a raw RateInfo response to an idiomatic Clojure map.
zone is the ZoneId for ZonedDateTime composition. Z-suffixed fields
(SystemTime_UTC, SignupCloseDate) preserve their UTC instant and are
re-expressed in zone; bare wall-clock fields are attached to zone.
Raw shape: {:RateID "USCA-TSTS-TTOU-TEST" :RateName "CEC TEST24HTOU" :ValueInformation [...] ...}
Coerced shape: {:midas.rate/id "USCA-TSTS-TTOU-TEST" :midas.rate/name "CEC TEST24HTOU" :midas.rate/system-time #zoned-date-time ... :midas.rate/values [<ValueData> ...] ...}
Coerce a raw RateInfo response to an idiomatic Clojure map.
`zone` is the `ZoneId` for ZonedDateTime composition. Z-suffixed fields
(`SystemTime_UTC`, `SignupCloseDate`) preserve their UTC instant and are
re-expressed in `zone`; bare wall-clock fields are attached to `zone`.
Raw shape:
{:RateID "USCA-TSTS-TTOU-TEST" :RateName "CEC TEST24HTOU"
:ValueInformation [...] ...}
Coerced shape:
{:midas.rate/id "USCA-TSTS-TTOU-TEST"
:midas.rate/name "CEC TEST24HTOU"
:midas.rate/system-time #zoned-date-time ...
:midas.rate/values [<ValueData> ...]
...}(->rin-list-entry raw zone)Coerce a raw RIN list entry.
zone is the ZoneId for ZonedDateTime composition. The bare
LastUpdated wire field is interpreted as wall-clock in zone.
Raw shape: {:RateID "USCA-TSTS-TTOU-TEST" :SignalType "Rates" :Description "..." :LastUpdated "2023-06-07T15:57:48.023"}
Coerced shape: {:midas.rin/id "USCA-TSTS-TTOU-TEST" :midas.rin/signal-type :midas.signal-type/rates :midas.rin/description "..." :midas.rin/last-updated #zoned-date-time ...}
Coerce a raw RIN list entry.
`zone` is the `ZoneId` for ZonedDateTime composition. The bare
`LastUpdated` wire field is interpreted as wall-clock in `zone`.
Raw shape:
{:RateID "USCA-TSTS-TTOU-TEST" :SignalType "Rates"
:Description "..." :LastUpdated "2023-06-07T15:57:48.023"}
Coerced shape:
{:midas.rin/id "USCA-TSTS-TTOU-TEST"
:midas.rin/signal-type :midas.signal-type/rates
:midas.rin/description "..."
:midas.rin/last-updated #zoned-date-time ...}(->value-data raw zone)Coerce a raw ValueData interval to an idiomatic Clojure map.
zone is the ZoneId in which the boundary moments are expressed. Pass the
zone configured on the client (default America/Los_Angeles — MIDAS's native
zone); a consumer can convert to any other zone via .withZoneSameInstant.
Raw shape: {:ValueName "winter off peak" :DateStart "2023-05-01" :TimeStart ...}
Coerced shape: {:midas.value/name "winter off peak" :midas.value/day-start :midas.day/monday :midas.value/price 0.1006M :midas.value/unit :midas.unit/dollar-per-kwh :tick/beginning #zoned-date-time ... :tick/end #zoned-date-time ...}
The interval boundaries are exposed only as :tick/beginning and
:tick/end — zone-aware ZonedDateTime values in zone (present when both
the date and time wire fields exist), making the map directly usable as a
tick interval. No zone-naive LocalDate/LocalTime boundary fields are emitted:
in v2.0 the bare DateStart/TimeStart/DateEnd/TimeEnd wire fields are
UTC for every signal type, so each boundary is composed in UTC and
re-expressed in zone (instant preserved, DST-correct). The exact wire
strings remain available under :midas/raw metadata.
Coerce a raw ValueData interval to an idiomatic Clojure map.
`zone` is the `ZoneId` in which the boundary moments are expressed. Pass the
zone configured on the client (default `America/Los_Angeles` — MIDAS's native
zone); a consumer can convert to any other zone via `.withZoneSameInstant`.
Raw shape:
{:ValueName "winter off peak" :DateStart "2023-05-01" :TimeStart ...}
Coerced shape:
{:midas.value/name "winter off peak"
:midas.value/day-start :midas.day/monday
:midas.value/price 0.1006M
:midas.value/unit :midas.unit/dollar-per-kwh
:tick/beginning #zoned-date-time ...
:tick/end #zoned-date-time ...}
The interval boundaries are exposed only as `:tick/beginning` and
`:tick/end` — zone-aware `ZonedDateTime` values in `zone` (present when both
the date and time wire fields exist), making the map directly usable as a
tick interval. No zone-naive LocalDate/LocalTime boundary fields are emitted:
in v2.0 the bare `DateStart`/`TimeStart`/`DateEnd`/`TimeEnd` wire fields are
UTC for every signal type, so each boundary is composed in UTC and
re-expressed in `zone` (instant preserved, DST-correct). The exact wire
strings remain available under `:midas/raw` metadata.(annotate-rin parsed-rin lookup-tables)Add human-readable labels to a parsed RIN map.
lookup-tables is a map of lookup table name to a sequence of coerced
LookupEntry maps (as returned by (entities/lookup-table response)).
Recognized keys: "Distribution" and "Energy".
Example: (annotate-rin (parse-rin "USCA-PGPG-TOU4-0000") {"Distribution" dist-entries "Energy" energy-entries}) ;=> {:midas.rin/country "US" ; :midas.rin/state "CA" ; :midas.rin/distribution "PG" ; :midas.rin/distribution-name "Pacific Gas and Electric" ; :midas.rin/energy "PG" ; :midas.rin/energy-name "Pacific Gas and Electric" ; :midas.rin/rate "TOU4" ; :midas.rin/location "0000"}
Add human-readable labels to a parsed RIN map.
lookup-tables is a map of lookup table name to a sequence of coerced
LookupEntry maps (as returned by `(entities/lookup-table response)`).
Recognized keys: "Distribution" and "Energy".
Example:
(annotate-rin (parse-rin "USCA-PGPG-TOU4-0000")
{"Distribution" dist-entries
"Energy" energy-entries})
;=> {:midas.rin/country "US"
; :midas.rin/state "CA"
; :midas.rin/distribution "PG"
; :midas.rin/distribution-name "Pacific Gas and Electric"
; :midas.rin/energy "PG"
; :midas.rin/energy-name "Pacific Gas and Electric"
; :midas.rin/rate "TOU4"
; :midas.rin/location "0000"}(flex-alert-active? rate)True if the Flex Alert rate-info indicates an active alert. Active when any value interval has a non-zero value.
True if the Flex Alert rate-info indicates an active alert. Active when any value interval has a non-zero value.
(flex-alert? rate)True if rate-info represents a Flex Alert signal.
True if rate-info represents a Flex Alert signal.
(ghg? rate)True if rate-info represents a GHG (greenhouse gas emissions) signal.
Recognises both the v2.0 gram unit and the v1.0 kilogram unit so the classifier works on live and historical-archive responses alike.
True if rate-info represents a GHG (greenhouse gas emissions) signal. Recognises both the v2.0 gram unit and the v1.0 kilogram unit so the classifier works on live and historical-archive responses alike.
(historical-data response)Extract and coerce historical rate data from a get-historical-data response.
Extract and coerce historical rate data from a get-historical-data response.
(lookup-table response)Extract and coerce lookup table entries from a get-lookup-table response.
v2.0 wraps the table in a keyed object — {:table_name "Unit" :data [...]}
— rather than v1.0's bare array. The :data array is peeled so callers get
a uniform vector of coerced entries. A bare vector is accepted as a
fallback.
Extract and coerce lookup table entries from a get-lookup-table response.
v2.0 wraps the table in a keyed object — `{:table_name "Unit" :data [...]}`
— rather than v1.0's bare array. The `:data` array is peeled so callers get
a uniform vector of coerced entries. A bare vector is accepted as a
fallback.(parse-rin rin)Parse a RIN string into its component fields.
Example: (parse-rin "USCA-PGPG-TOU4-0000") ;=> {:midas.rin/country "US" ; :midas.rin/state "CA" ; :midas.rin/distribution "PG" ; :midas.rin/energy "PG" ; :midas.rin/rate "TOU4" ; :midas.rin/location "0000"}
Returns nil if the string does not match the RIN format.
Parse a RIN string into its component fields.
Example:
(parse-rin "USCA-PGPG-TOU4-0000")
;=> {:midas.rin/country "US"
; :midas.rin/state "CA"
; :midas.rin/distribution "PG"
; :midas.rin/energy "PG"
; :midas.rin/rate "TOU4"
; :midas.rin/location "0000"}
Returns nil if the string does not match the RIN format.(rate-info response)Extract and coerce rate info from a get-rate-values response.
Extract and coerce rate info from a get-rate-values response.
RateType wire labels → namespaced keywords.
v2.0 is INCONSISTENT about which Ratetype lookup column it puts on the
wire: electricity rates return the short UploadCode (e.g. "TOU"),
while GHG and Flex Alert return the long-form Description (e.g.
"Greenhouse Gas emissions", "Flex Alert"). Both forms are keyed here
so coercion succeeds regardless. UploadCode values are from the live
Ratetype lookup table; -D suffixes denote demand-charge variants.
`RateType` wire labels → namespaced keywords. v2.0 is INCONSISTENT about which `Ratetype` lookup column it puts on the wire: electricity rates return the short `UploadCode` (e.g. `"TOU"`), while GHG and Flex Alert return the long-form `Description` (e.g. `"Greenhouse Gas emissions"`, `"Flex Alert"`). Both forms are keyed here so coercion succeeds regardless. `UploadCode` values are from the live `Ratetype` lookup table; `-D` suffixes denote demand-charge variants.
(rin-list response)Extract and coerce RIN list entries from a get-rin-list response.
v2.0 returns a single-keyed object — {Rates|GHGEmissions|FlexAlerts|All: [...]} keyed by the requested SignalType — rather than v1.0's bare array.
The single key is peeled so callers get a uniform vector regardless of which
key the server used. A bare vector is accepted as a fallback.
Extract and coerce RIN list entries from a get-rin-list response.
v2.0 returns a single-keyed object — `{Rates|GHGEmissions|FlexAlerts|All:
[...]}` keyed by the requested SignalType — rather than v1.0's bare array.
The single key is peeled so callers get a uniform vector regardless of which
key the server used. A bare vector is accepted as a fallback.RIN-list SignalType wire labels → namespaced keywords (v2.0).
v2.0 always populates this field with long-form labels (v1.0 returned null for GHG/Flex Alert entries). MIDAS v1.0 disappears from the live API at the v2.0 release, so only the v2.0 labels are recognised.
RIN-list `SignalType` wire labels → namespaced keywords (v2.0). v2.0 always populates this field with long-form labels (v1.0 returned null for GHG/Flex Alert entries). MIDAS v1.0 disappears from the live API at the v2.0 release, so only the v2.0 labels are recognised.
Unit wire labels → namespaced keywords.
v2.0 reports GHG emissions in grams (g/kWh CO2) rather than v1.0's
kilograms (kg/kWh CO2) — values are 1000× larger for the same physical
reading. The kilogram unit is retained because the live Unit lookup table
still lists it (and pre-migration archives may carry it); consumers
comparing across the v2.0 boundary must account for the unit difference.
`Unit` wire labels → namespaced keywords. v2.0 reports GHG emissions in grams (`g/kWh CO2`) rather than v1.0's kilograms (`kg/kWh CO2`) — values are 1000× larger for the same physical reading. The kilogram unit is retained because the live `Unit` lookup table still lists it (and pre-migration archives may carry it); consumers comparing across the v2.0 boundary must account for the unit difference.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |