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 a9ad62b0..2b7cd2e6 100644 --- a/backend/documents_parser/tests/test_summary_pdf_mapper_chain.py +++ b/backend/documents_parser/tests/test_summary_pdf_mapper_chain.py @@ -64,6 +64,7 @@ _SUMMARY_000901_PDF = _FIXTURES / "Summary_000901.pdf" # cert 3800 _SUMMARY_000904_PDF = _FIXTURES / "Summary_000904.pdf" # cert 9285 _SUMMARY_000900_PDF = _FIXTURES / "Summary_000900.pdf" # cert 2225 _SUMMARY_000898_PDF = _FIXTURES / "Summary_000898.pdf" # cert 2636 +_SUMMARY_000902_PDF = _FIXTURES / "Summary_000902.pdf" # cert 9418 # GOV.UK EPB API JSON for cert 001479 — the API-path counterpart of the # Summary_001479.pdf fixture. Together they drive the API ≡ Summary @@ -816,6 +817,50 @@ def test_summary_2636_full_chain_sap_within_spec_floor_of_worksheet() -> None: assert abs(result.sap_score_continuous - worksheet_unrounded_sap) < _ASHP_COHORT_CHAIN_TOLERANCE +def test_summary_9418_large_cylinder_routes_to_code_4() -> None: + # Arrange — cert 9418-3062-8205-3566-7200's Summary §15.1 lodges + # "Cylinder Size: Large". The dr87 worksheet lodges "Cylinder + # Volume 210.00" L, and the cascade lookup + # `_CYLINDER_SIZE_CODE_TO_LITRES = {3: 160.0, 4: 210.0}` maps code + # 4 → 210 L. Cert 9418 is the first cohort cert to exercise the + # "Large" cylinder lodging (every other cohort cert is "Medium"). + pages = _summary_pdf_to_textract_style_pages(_SUMMARY_000902_PDF) + site_notes = ElmhurstSiteNotesExtractor(pages).extract() + + # Act + epc = EpcPropertyDataMapper.from_elmhurst_site_notes(site_notes) + + # Assert + assert epc.sap_heating.cylinder_size == 4 + + +def test_summary_9418_full_chain_sap_within_spec_floor_of_worksheet() -> None: + # Arrange — cert 9418-3062-8205-3566-7200 (Summary_000902.pdf): + # **Daikin EDLQ05CAV3 ASHP** (PCDB index 102421 — distinct from + # the rest of the cohort's Mitsubishi 104568), end-terrace house + # with TWO 1.64 kWp PV arrays (N + S), 210 L cylinder. + # `heating_duration_code='24'` per Table N4 (continuous heating). + # Worksheet "SAP value" lodges 84.6305. + # + # Closes the cohort: the final ASHP cert. The only Summary-mapper + # gap was the missing "Large" → 4 mapping in + # `_ELMHURST_CYLINDER_SIZE_LABEL_TO_SAP10` (Slice S0380.14, this + # commit) — multi-array PV + Large-cylinder were the variants + # cert 9418 uniquely exercises. + pages = _summary_pdf_to_textract_style_pages(_SUMMARY_000902_PDF) + site_notes = ElmhurstSiteNotesExtractor(pages).extract() + epc = EpcPropertyDataMapper.from_elmhurst_site_notes(site_notes) + + # Act + result = calculate_sap_from_inputs( + cert_to_inputs(epc, prices=SAP_10_2_SPEC_PRICES) + ) + + # Assert — ±0.07 ASHP-cohort spec-floor tolerance. + worksheet_unrounded_sap = 84.6305 + assert abs(result.sap_score_continuous - worksheet_unrounded_sap) < _ASHP_COHORT_CHAIN_TOLERANCE + + def test_summary_3800_full_chain_sap_within_spec_floor_of_worksheet() -> None: # Arrange — cert 3800-8515-0922-3398-3563 (Summary_000901.pdf / # dr87-0001-000901.pdf) is the third ASHP cohort cert to close on diff --git a/datatypes/epc/domain/mapper.py b/datatypes/epc/domain/mapper.py index 743551d6..3b6869c0 100644 --- a/datatypes/epc/domain/mapper.py +++ b/datatypes/epc/domain/mapper.py @@ -3375,12 +3375,13 @@ _ELMHURST_GAS_BOILER_FUEL_TYPES: frozenset[str] = frozenset({ # Elmhurst Summary §15.1 "Cylinder Size" labels mapped to the SAP10 # cascade enum that `domain/sap10_calculator/rdsap/cert_to_inputs.py` -# `_CYLINDER_SIZE_CODE_TO_LITRES` keys ({3: 160.0, 4: 210.0}). Only the -# "Medium" lodging is exercised by the cohort (cert 0380); other size -# labels (Small / Large / Very Large) are deferred until a fixture -# exercises them. +# `_CYLINDER_SIZE_CODE_TO_LITRES` keys ({3: 160.0, 4: 210.0}). Exercised +# by the cohort: "Medium" (cert 0380 et al — 160 L) and "Large" (cert +# 9418 — 210 L). "Small" and "Very Large" labels are deferred until a +# fixture exercises them. _ELMHURST_CYLINDER_SIZE_LABEL_TO_SAP10: Dict[str, int] = { "Medium": 3, + "Large": 4, }