diff --git a/backend/documents_parser/tests/test_summary_pdf_mapper_chain.py b/backend/documents_parser/tests/test_summary_pdf_mapper_chain.py index 88ab7f14..b1966d40 100644 --- a/backend/documents_parser/tests/test_summary_pdf_mapper_chain.py +++ b/backend/documents_parser/tests/test_summary_pdf_mapper_chain.py @@ -42,6 +42,7 @@ from datatypes.epc.domain.mapper import ( EpcPropertyDataMapper, UnmappedApiCode, UnmappedElmhurstLabel, + _elmhurst_glazing_type_code, # pyright: ignore[reportPrivateUsage] ) from domain.sap10_calculator.calculator import calculate_sap_from_inputs from domain.sap10_calculator.rdsap.cert_to_inputs import SAP_10_2_SPEC_PRICES, cert_to_inputs @@ -1471,6 +1472,36 @@ def test_summary_000474_double_glazed_windows_route_to_code_3() -> None: ) +def test_elmhurst_glazing_label_full_coverage_per_sap10_table_6b() -> None: + # Arrange — the double_glazing recommendation fixture (Summary_001431) + # exercises every RdSAP-21 §11 glazing-type lodging in one cert. Each + # label must resolve to the SAP 10.2 Table 6b cascade code whose + # `_G_LIGHT_BY_GLAZING_CODE` daylight factor g_L is correct for the + # glazing family: single 0.90, double / secondary 0.80, triple 0.70 + # (the lodged manufacturer U/g drive §3/§6; the code only sets g_L). + expected: dict[str, int] = { + "Single glazing": 1, + "Single glazing, known data": 15, + "Double pre 2002": 2, + "Double between 2002 and 2021": 3, + "Double with unknown install date": 3, + "Double glazing, known data": 3, + "Double post or during 2022": 5, + "Secondary glazing": 7, + "Secondary glazing - Normal emissivity": 11, + "Triple pre 2002": 10, + "Triple between 2002 and 2021": 9, + "Triple post or during 2022": 6, + "Triple with unknown install date": 6, + } + + # Act / Assert + for label, code in expected.items(): + assert _elmhurst_glazing_type_code(label) == code, ( + f"{label!r} should map to SAP 10.2 Table 6b code {code}" + ) + + def test_summary_mapper_raises_on_unmapped_glazing_type_label() -> None: # Arrange — same strict-coverage gate as the cylinder-size helper # (Slice S0380.15 + S0380.16): silently routing an unknown glazing diff --git a/datatypes/epc/domain/mapper.py b/datatypes/epc/domain/mapper.py index 9bc5e8e3..71df85c0 100644 --- a/datatypes/epc/domain/mapper.py +++ b/datatypes/epc/domain/mapper.py @@ -5073,6 +5073,30 @@ _ELMHURST_GLAZING_LABEL_TO_SAP10: Dict[str, int] = { # solar_transmittance=0.72 overrides per worksheet-pinned value. "Triple between 2002 and 2021": 9, "Secondary": 7, + # Elmhurst §11 lodges the full "Secondary glazing" phrase as well as + # the bare "Secondary" form — both are SAP 10.2 Table 6b "window with + # secondary glazing" (cascade code 7, g_L=0.80, g⊥=0.76). The + # RdSAP-21 schema splits secondary glazing by pane emissivity; the + # "Normal emissivity" variant is its own code 11 (g_L=0.80, identical + # cascade output to 7, kept distinct for the strict-raise audit + # trail). Surfaced on the double_glazing/before recommendation + # fixture (Summary_001431 §11 Window 9). + "Secondary glazing": 7, + "Secondary glazing - Normal emissivity": 11, + # RdSAP-21 row "triple glazing, installed pre-2002" (cascade code 10, + # g_L=0.70, g⊥=0.68 — same triple-glazed daylight/solar bucket as the + # other triple variants {6, 8, 9, 14}). The lodged manufacturer + # U-value / solar_transmittance drive §6; the code only sets g_L. + "Triple pre 2002": 10, + # Triple glazing of unknown install date → the generic SAP 10.2 + # Table 6b "triple glazed" row (cascade code 6, g_L=0.70). No + # dedicated "triple, unknown date" enum exists; 6 is the correct + # triple-glazed bucket. + "Triple with unknown install date": 6, + # RdSAP-21 row "single glazing, known data" (cascade code 15, + # g_L=0.90, g⊥=0.85 — same as plain single glazing). Manufacturer + # U/g lodged on WindowTransmissionDetails drive §6. + "Single glazing, known data": 15, } _ELMHURST_GLAZING_LABEL_NOISE_PREFIX_RE: Final[re.Pattern[str]] = re.compile(