Commit graph

509 commits

Author SHA1 Message Date
Khalim Conn-Kowlessar
debbbb84f2 Map mechanical_ventilation_kind in the 3 bare-SapVentilation RdSAP mappers 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 08:38:13 +00:00
Khalim Conn-Kowlessar
590a456065 Map main_heating_controls in the older RdSAP mappers for the FE panel 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 21:43:17 +00:00
Khalim Conn-Kowlessar
a975d870c9 Map main_heating_controls in the older RdSAP mappers for the FE panel 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 21:41:41 +00:00
Khalim Conn-Kowlessar
5deea0a27c Map main_heating_controls on full-SAP certs for the FE panel 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 21:38:50 +00:00
Khalim Conn-Kowlessar
0440de4965 Map main_heating_controls on full-SAP certs for the FE panel 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 21:38:06 +00:00
Khalim Conn-Kowlessar
95701d03e4 Map display EnergyElements on full-SAP certs for the FE panel 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 20:55:51 +00:00
Khalim Conn-Kowlessar
4c65640e4e Map display EnergyElements on full-SAP certs for the FE panel 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 20:54:46 +00:00
Khalim Conn-Kowlessar
304e8afb00 Map assessment_type on full-SAP certs 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 20:53:22 +00:00
Khalim Conn-Kowlessar
33f9988ac5 Map assessment_type on full-SAP certs 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 20:52:07 +00:00
Khalim Conn-Kowlessar
5149400f96 Map secondary_heating on full-SAP certs for the calculator 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 20:40:59 +00:00
Khalim Conn-Kowlessar
7b06d9dbab Map secondary_heating on full-SAP certs for the calculator 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 20:40:30 +00:00
Khalim Conn-Kowlessar
bc394ce5f5 Map built_form on full-SAP certs 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 20:38:50 +00:00
Khalim Conn-Kowlessar
d2ede36b41 Map built_form on full-SAP certs 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 20:38:16 +00:00
Khalim Conn-Kowlessar
5c0460cee2 Map property_type on full-SAP certs for correct heat-transmission 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 20:37:34 +00:00
Khalim Conn-Kowlessar
951bee3115 Map property_type on full-SAP certs for correct heat-transmission 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 20:36:38 +00:00
Khalim Conn-Kowlessar
cff5b061a4 Map sap_version on full-SAP certs so pre-SAP10 rebaseline fires 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 20:36:04 +00:00
Khalim Conn-Kowlessar
14c777f37b Map sap_version on full-SAP certs so pre-SAP10 rebaseline fires 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 20:34:35 +00:00
Daniel Roth
c89ab7692d
Merge pull request #1329 from Hestia-Homes/feature/modelling-e2e-bugs
Fix: synthesise SapFloorDimension for SAP-16.0 flats with missing floor dimensions to unblock roof recommendations
2026-06-25 16:12:46 +01:00
Daniel Roth
5a4285e1b7 Include address and postcode in floor-dims synthesis ValueError for triage
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 15:07:07 +00:00
Daniel Roth
8024065d04 Raise ValueError when floor dims absent and synthesis guards cannot fire 🟩
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 15:03:03 +00:00
Daniel Roth
e62e5fbfe4 Add comment explaining floor-dim synthesis guard and bungalow coverage gap
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 14:24:59 +00:00
Daniel Roth
76d07dba39 Pin synthesised SapFloorDimension total area and heat-loss perimeter as regression tests 🟩
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 14:18:27 +00:00
Daniel Roth
8a909dc15b Mapper synthesises SapFloorDimension for SAP-16.0 flats with empty floor dims 🟩
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 14:16:36 +00:00
Daniel Roth
c1e754057b Mapper synthesises SapFloorDimension for SAP-16.0 flats with empty floor dims 🟥
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 14:14:03 +00:00
Khalim Conn-Kowlessar
48760971a7 Coerce banded flat_location string to its floor integer 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 12:01:44 +00:00
Khalim Conn-Kowlessar
862a7ccc61 Coerce banded flat_location string to its floor integer 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 12:00:00 +00:00
Daniel Roth
a6752c2654
Merge pull request #1306 from Hestia-Homes/fix/full-sap-16x-cert-mapping
Route full-SAP certs lodged under SAP-Schema-16.x to the full-SAP mapper
2026-06-24 17:22:03 +01:00
Daniel Roth
1a37caa44d
Merge pull request #1308 from Hestia-Homes/fix/conservatory-age-band-sibling-mappers
Map non-separated conservatory on the 17.0/17.1/18.0/20.0.0 gov paths 🟩
2026-06-24 17:19:23 +01:00
Daniel Roth
8244d3f116
Merge pull request #1307 from Hestia-Homes/feature/unmapped-ventilation-type-10
Map SAP ventilation_type codes 7/8/9/10 correctly in full-SAP schema path 🟩
2026-06-24 17:05:08 +01:00
Daniel Roth
3e8e29b6d2 Map SAP ventilation_type codes 7/8/9/10 correctly in full-SAP schema path 🟩
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 15:58:46 +00:00
Khalim Conn-Kowlessar
700f44cbbc Map non-separated conservatory on the 17.0/17.1/18.0/20.0.0 gov paths 🟩
Ports the §6.1 conservatory split (declare the four glazed fields; exclude the
glazed BP from the fabric loop; carry it as sap_conservatory) to the remaining
gov mappers that lacked it. Fixes the modelling_e2e construction_age_band
NOT-NULL failures: 5 type-4 conservatory certs on RdSAP-Schema-20.0.0 (e.g.
property 725213) mapped the conservatory to a null-age fabric part that crashed
on persist. 17.0/17.1/18.0 ported preventively (no-op for non-conservatory
certs). 21.0.0 deferred — it never adopted the ADR-0027 all-Optional
SapBuildingPart, so a sparse conservatory part needs that broader change first.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 15:58:14 +00:00
Jun-te Kim
e312aae95d Generalise to a 'broken schema type' gate keyed on assessment_type
Per review (Khalim): lift the full-SAP detection out of the SAP-Schema-16.x
branch into a single top-level gate in from_api_response, and key it on the
gov-API's own assessment_type declaration rather than the structural
sap_opening_types proxy.

- _is_full_sap_assessment(data): assessment_type == "SAP" — the authoritative
  SAP-vs-RdSAP classification. Verified to separate the entire fixture corpus:
  every full-SAP schema (SAP-Schema-17.x/18.x + the broken LIG 16.x) is "SAP";
  every reduced cert (RdSAP-Schema-* and reduced SAP-Schema-16.x, incl.
  sap_16_0.json) is "RdSAP". data_type / sap_opening_types agree but are derived
  shape artifacts; assessment_type is the meaning.
- A cert that is full-SAP by assessment_type but whose schema_type LABEL is not a
  recognised full-SAP schema is a *broken schema type* (label disagrees with the
  assessment). _record_broken_schema_type logs it — visible, not silently
  rerouted — so unreliable labels surface and coverage grows as new mislabels
  appear. Generalises beyond 16.x to any future mislabel.
- _from_full_sap maps it via the full-SAP 17.1 mapper (real measured openings,
  no defaulting; only `tenure` defaulted). Correctly-labelled full-SAP certs keep
  their dedicated branches (one-mapper-per-schema convention); reduced certs are
  unchanged.

Tests: broken cert routed AND recorded; correctly-labelled full-SAP not recorded.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 15:39:16 +00:00
KhalimCK
422c943748
Merge pull request #1305 from Hestia-Homes/feature/e2e-runs
Feature/e2e runs
2026-06-24 16:25:22 +01:00
Jun-te Kim
5e0963593a Route full-SAP certs lodged under SAP-Schema-16.x to the full-SAP mapper
A full-SAP cert (assessment_type=SAP, the as-designed LIG-* new-builds) lodged
under a SAP-Schema-16.x version failed to map with:

  ValueError: RdSapSchema17_1: missing required field 'door_count'

— crashing the property's modelling_e2e subtask. 89 distinct properties in the
2026-06-24 run hit this; ~22 of every 30 sampled are this full-SAP shape.

Root cause: from_api_response dispatches on the schema_type STRING only, and the
SAP-Schema-16.x branch assumed a single shape — "reduced-field (RdSAP-shaped)" —
sending every 16.x cert through _normalize_sap_schema_16_x -> RdSapSchema17_1.
But the SAP-Schema-16.x name covers two structurally different certs:

  * reduced RdSAP (assessment_type=RdSAP): top-level door_count / glazed_area
    band / construction-code building parts.
  * full SAP    (assessment_type=SAP): measured shape — sap_opening_types +
    structured sap_building_parts carrying measured U-values and door/window
    OPENINGS, with NO top-level door_count (the door is an opening). These omit
    the reduced-only count fields, so the reduced normaliser failed loud on the
    first one it checks (door_count) — and 14 others behind it.

Force-fitting a full-SAP cert to the reduced RdSAP schema was the bug: the data
was never missing, it was being validated against the wrong schema.

Fix: discriminate on shape (_is_full_sap_cert: assessment_type=SAP AND
sap_opening_types present) and route full-SAP 16.x certs to the existing
full-SAP 17.1 mapper, which reads the real measured openings (the lodged
0.9x2.1m door -> door_count 1) — no reduced-field defaulting. The only field the
16.x full-SAP shape omits that SapSchema17_1 needs is `tenure` (register
metadata, no SAP effect), defaulted. Reduced 16.x certs (assessment_type=RdSAP,
no opening types) are untouched — all pinned reduced 16.x fixtures stay on the
RdSAP path.

Regression test pins a real full-SAP-16.0 cert (0293-...-8795, lodged 83) mapping
via the full-SAP path + the reduced 16.2 cert staying on the RdSAP path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 15:16:51 +00:00
Khalim Conn-Kowlessar
56add97bd9 Map non-separated conservatory on the gov-API RdSAP-19.0 path 🟩
Mirror the merged 21.0.1 fix (d501535c): declare the four glazed conservatory
fields on the 19.0 SapBuildingPart so from_dict stops dropping them, exclude the
glazed BP from the fabric loop, and carry it as EpcPropertyData.sap_conservatory
(§6.1). Fixes cert 718138 (conservatory_type=4) mis-scoring as a fabric part and
failing to persist on the NOT-NULL construction_age_band.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 13:56:03 +00:00
Daniel Roth
40a079e464 21.0.1 mapper coalesces omitted led_fixed_lighting_bulbs_count to 0 🟩
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 13:19:10 +00:00
Daniel Roth
e906bbecf8 21.0.1 mapper coalesces omitted led_fixed_lighting_bulbs_count to 0 🟥
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 13:17:19 +00:00
Daniel Roth
71269637ef 21.0.1 mapper coalesces omitted cfl_fixed_lighting_bulbs_count to 0 🟩
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 13:16:29 +00:00
Daniel Roth
d36fa2b5cd 21.0.1 mapper coalesces omitted cfl_fixed_lighting_bulbs_count to 0 🟥
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 13:15:37 +00:00
Daniel Roth
a01a940df2 21.0.1 mapper coalesces omitted open_chimneys_count to 0 🟩
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 13:14:36 +00:00
Daniel Roth
6efbd4649d 21.0.1 mapper coalesces omitted open_chimneys_count to 0 🟥
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 13:13:43 +00:00
KhalimCK
f7f74ea72b
Merge pull request #1288 from Hestia-Homes/feature/per-cert-mapper-validation
Feature/per cert mapper validation
2026-06-24 12:04:00 +01:00
Jun-te Kim
e9a8ad9cd9
Merge pull request #1290 from Hestia-Homes/fix/building-part-int-identifier
Tolerate non-string building-part identifier (fix TypeError, cohort cert)
2026-06-24 11:59:31 +01:00
Jun-te Kim
170fd1bf71
Merge pull request #1291 from Hestia-Homes/fix/full-sap-has-hot-water-cylinder
Fall back to nested has_hot_water_cylinder for full-SAP certs (cohort cert)
2026-06-24 11:59:03 +01:00
Jun-te Kim
e4a79648ab
Merge pull request #1292 from Hestia-Homes/fix/sap-opening-type-6
Map full-SAP opening-type 6 (rooflight) as a roof window (5 cohort certs)
2026-06-24 11:58:53 +01:00
Jun-te Kim
789103af27 Map full-SAP opening-type 6 (rooflight) as a roof window
5 modelling_e2e cohort certs (full-SAP 19.1.0) were skipped with
"unmapped API sap_opening_type code: 6". Code 6 is "Ext Rooflight"; SAP 10.2
treats roof windows and rooflights as the same inclined-glazing family.

Fix: add 6 to the known opening-type taxonomy and route it onto the roof-window
path (`sap_roof_windows`) alongside code 5, via a `_SAP_ROOF_WINDOW_TYPES`
{5, 6} set. Genuinely-unknown codes (e.g. 99) still raise.

Cert 9878-3908-6309-6714-8200 now maps + calculates (sap 81). Regression test:
a type-6 opening maps onto sap_roof_windows.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 10:47:33 +00:00
Jun-te Kim
18063a2d7f Fall back to nested has_hot_water_cylinder for full-SAP certs
A SAP-Schema-17.0 full-SAP cohort cert (8265-7433-3220-9736-7902) omits the
top-level `has_hot_water_cylinder` and lodges it only under `sap_heating`. The
required top-level field on SapSchema17_1 made `from_dict` raise and skip the
cert.

Fix: make the top-level field Optional, and have the full-SAP mapper
(`from_sap_schema_17_1`, also used by 17.0/18.0.0 via delegation) fall back to
`sap_heating.has_hot_water_cylinder`. RdSAP mappers are unchanged (they keep the
top-level required field; their SapHeating has no such attribute).

Cert now maps + calculates (sap 76). Regression tests for the nested true/false
fallback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 10:43:18 +00:00
Jun-te Kim
6d98ecf375 Tolerate non-string building-part identifier (fix TypeError)
A SAP-16.x cohort cert (9258-4062-7265-2844-7954) lodges a bare int building-
part identifier (the second part as `1` after "Main Dwelling").
`BuildingPartIdentifier.from_api_string` regex-matched it assuming a string and
raised "TypeError: expected string or bytes-like object, got 'int'", failing the
whole property.

Fix: guard the match on `isinstance(api_identifier, str)` so a non-string
identifier falls to OTHER, matching the documented "anything unrecognised ->
OTHER" contract. The baseline SAP fabric sums all building parts regardless of
identifier, so OTHER is SAP-neutral; the identifier only labels parts for
measure targeting. Fixes every mapper (all route through from_api_string).

Cert now maps + calculates (sap 63). Regression test added.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 10:33:23 +00:00
Jun-te Kim
eefeccf0bc
Merge pull request #1283 from Hestia-Homes/fix/rdsap-21-0-0-cohort-cert-skips
Align RdSAP 21.0.0 schema optionality with 21.0.1 (unblock ~35 cohort certs)
2026-06-24 09:43:17 +01:00
Jun-te Kim
7489e5ac89 Scope 21.0.0 widening to fields the skipped certs actually omit
Review follow-up (Khalim): the first pass made far more optional than needed —
notably the whole SapBuildingPart block — and a buggy 21.0.0↔21.0.1 diff also
MISSED open_chimneys_count / cfl_/led_fixed_lighting_bulbs_count / suggested_
improvements, so the original change actually mapped only 3 of the 33 skipped
certs (the rest still failed on open_chimneys_count).

Re-derived the exact set empirically from all 33 skipped cohort certs:
widen only fields that are (a) required in 21.0.0, (b) already optional in
21.0.1, AND (c) genuinely omitted by ≥1 of those certs. Result:

- KEEP optional: the 4 SapWindow refinements, the top-level vent/lighting/
  door/pressure-test block (incl. the 3 previously-missed fields), 2
  SapEnergySource fields, Addendum.addendum_numbers, PhotovoltaicSupply.
  none_or_no_details, and exactly ONE building-part field
  (SapBuildingPart.roof_insulation_thickness — omitted by 7 certs).
- REVERT to required: the other 12 SapBuildingPart fields (construction_age_
  band, wall_construction, …), MainHeatingDetail.emitter_temperature,
  PvBatteries.pv_battery, ShowerOutlets.shower_outlet — none of the 33 certs
  omit these, so they stay strict.

Mapper: coalesce the count fields (wet_rooms_count, open_chimneys_count,
cfl_/led_fixed_lighting_bulbs_count) to 0 like every other mapper, so the now-
optional values can't reach a NOT-NULL column (also drops 4 pyright ignores).

Now maps 32/33 (up from 3); the last cert hits a pre-existing pv_batteries-
shape AttributeError and degrades via the ADR-0031 skip path. pyright net
unchanged (43, no new errors); regression test rewritten to the real omitted set.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 08:35:36 +00:00