Skip to content

feat(integrator): generate battery templates per category - #237

Open
LKSNDRTMLKV wants to merge 2 commits into
fix/scope-gate-and-webhook-pinningfrom
feat/battery-import-templates
Open

feat(integrator): generate battery templates per category#237
LKSNDRTMLKV wants to merge 2 commits into
fix/scope-gate-and-webhook-pinningfrom
feat/battery-import-templates

Conversation

@LKSNDRTMLKV

Copy link
Copy Markdown
Member

Stacked on fix/scope-gate-and-webhook-pinning — review that first, or read this diff alone (one commit).

The gap

GET /integrator/api/v1/templates/battery served a fifteen-column CSV. Its own
two example rows imported cleanly and then failed publish on ~40 missing
mandatory data points, none of which the template had a column for. The
bulk-import path — the documented way to onboard at volume, and the only one
with a first-party template — could therefore only ever produce records that
were dead on arrival.

The publish gate was right; the template was the gap, and so was the importer:
it hardcoded ~30 BatteryData fields to None under a comment reading "Left
None until the template gains columns for them."
The template never did.

Three templates, not one — and not five

battery is gone. What a battery owes is decided per category: 46 mandatory
data points for EV, 45 for LMT, 38 for industrial. The clearest case is
StateOfHealth, which is a sum type because Annex VII Part A is two disjoint
lists — an EV battery reports state of certified energy alone, stationary and
LMT batteries report five parameters. One file would have to offer both and
accept a combination the annex does not permit.

Three rather than five because Art. 77(1) gives a passport only to "each LMT
battery, each industrial battery with a capacity greater than 2 kWh and each
electric vehicle battery". Portable and SLI bear no passport obligation, which
is also why the rules table answers Unknown for them rather than guessing.

The bare battery key now 404s with that explanation rather than a bare list.

Generated, not committed

A committed CSV drifts the moment a data point moves, and nothing notices —
which is exactly how a fifteen-column template survived the arrival of the
content gate. domain::battery_template is a column contract; the templates are
rendered from it, and tests assert:

  • every data point mandatory_fields demands for a category has a column
  • no column offers a data point the guidance marks not to be filled/displayed
    (permits_presence, so Conditional is allowed and NotApplicable is not)
  • header and example row agree on width, and headers are unique
  • the EV and stationary state-of-health parameter sets stay disjoint

The test that defines this as closed

every_battery_template_row_satisfies_the_publish_content_gate drives the whole
operator path: render the template, import its own example row, and ask the
publish gate's own question of the result. Verified live as well — for all three
categories, template → import → POST /publish200, with the right
per-category shape in the stored record (EV gets electricVehicle state of
health and a dynamic-performance block; industrial gets stationaryOrLmt and no
dynamic-performance block, which is correct — it is Conditional there).

Flat-file conventions

Four, all following the material_N_* convention the bill of materials already
used: repeating groups (cathode_1_name, …), nested blocks
(dynamicPerformance_ratedCapacityAh, …), a two-column range, and a
semicolon-delimited list. Absence is never zero — a blank column parses to
None, because an invented 0.0 for recycled cobalt content is a false
declaration rather than a placeholder.

Two things found on the way

  • The import handler held its own product-group allowlist and rejected
    battery-ev before the dispatch that understood it was ever reached. Both now
    go through one predicate, so an operator can post battery-ev back to the
    name they downloaded.
  • TemperatureRange and HazardousSubstance are #[non_exhaustive] in
    dpp-domain with neither a constructor nor Default, so this crate cannot
    build one with a struct expression. Constructed through their own
    Deserialize for now — the sanctioned path the attribute leaves open —
    rather than blocking this behind a core release and a repin. Constructors in
    core are the better fix.

just check green (964 tests), just openapi-check green.

The shipped battery template had fifteen columns and its own example rows imported cleanly then failed publish on around forty missing mandatory data points, because what a battery owes is decided per category and one file cannot carry three obligations; the templates are now generated from the same rules table the publish gate reads, the importer fills the thirty-odd fields it used to hardcode as None, and a round-trip test drives template to import to content gate for all three categories. Three and not five because Art. 77(1) gives a passport only to LMT, industrial above 2 kWh and electric-vehicle batteries.
@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 84 complexity · -2 duplication

Metric Results
Complexity 84
Duplication -2

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant