From 361abc1202d3ff3f009e0d5757287992c1cb9e50 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Mon, 15 Jun 2026 06:43:55 +0000 Subject: [PATCH] fix(mapper): handle 'ND' multiple_glazing_type on RdSAP-Schema-20.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `_synthesise_20_0_0_sap_windows` passed `schema.multiple_glazing_type` straight into `_api_cascade_glazing_type`, which raised UnmappedApiCode on the "ND" (Not Defined) string that the 20.0.0 corpus lodges alongside the 1-8 integer codes — failing the mapper-coverage guard on every ND-glazed 20.0.0 cert. Mirror the existing 18.0/19.0/17.x seams: route integer codes through the cascade, fall the "ND" string back to the DG-modal default (cascade code 2 → daylight g_L 0.80). Also corrects the 20.0.0 schema field type `int` → `Union[int, str]` to match the data (as 18.0 already does), which keeps the isinstance guard pyright-clean. Pre-existing failure (present before this branch's recent commits), not in the handover regression gate. Fixes all 15 RdSAP-Schema-20.0.0 ND certs; test_mapper_corpus 6002/6002 pass. pyright net-zero. Co-Authored-By: Claude Opus 4.8 --- datatypes/epc/domain/mapper.py | 18 ++++++++++++++++-- datatypes/epc/schema/rdsap_schema_20_0_0.py | 4 +++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/datatypes/epc/domain/mapper.py b/datatypes/epc/domain/mapper.py index 7e3add21..06771a7e 100644 --- a/datatypes/epc/domain/mapper.py +++ b/datatypes/epc/domain/mapper.py @@ -3319,14 +3319,28 @@ def _synthesise_reduced_field_windows( ] +# ADR-0028: multiple_glazing_type "ND" (Not Defined) → the DG-modal +# default (cascade code 2 → daylight g_L 0.80), as for 18.0/19.0/17.x. +_RDSAP20_ND_GLAZING_TYPE: int = 2 + + def _synthesise_20_0_0_sap_windows(schema: RdSapSchema20_0_0) -> List[SapWindow]: """ADR-0027/0028 seam: 20.0.0 glazed_type codes 1-8+ND are identical to 21.0.1's, so route multiple_glazing_type through the verified cascade (fixes - code 1 "DG pre-2002" read as single), then call the shared core.""" + code 1 "DG pre-2002" read as single), then call the shared core. The "ND" + string (no cascade mapping) falls back to the DG-modal default, mirroring + `_synthesise_18_0_sap_windows` — without this the cascade raised + UnmappedApiCode on every ND-glazed 20.0.0 cert.""" + mgt = schema.multiple_glazing_type + glazing_type = ( + _api_cascade_glazing_type(mgt) + if isinstance(mgt, int) + else _RDSAP20_ND_GLAZING_TYPE + ) return _synthesise_reduced_field_windows( schema.glazed_area, schema.total_floor_area, - _api_cascade_glazing_type(schema.multiple_glazing_type), + glazing_type, ) diff --git a/datatypes/epc/schema/rdsap_schema_20_0_0.py b/datatypes/epc/schema/rdsap_schema_20_0_0.py index dbd5feef..9a4b78f2 100644 --- a/datatypes/epc/schema/rdsap_schema_20_0_0.py +++ b/datatypes/epc/schema/rdsap_schema_20_0_0.py @@ -267,7 +267,9 @@ class RdSapSchema20_0_0: energy_rating_current: int lighting_cost_current: float main_heating_controls: List[EnergyElement] - multiple_glazing_type: int + # "ND" (Not Defined) appears in the corpus alongside the 1-8 integer + # codes — type as Union to match the data (mirrors RdSAP-Schema-18.0). + multiple_glazing_type: Union[int, str] open_fireplaces_count: int heating_cost_potential: float hot_water_cost_current: float