From 15b3df17786faaa2b3629896d101f0b9b2642cea Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Thu, 28 May 2026 07:16:32 +0000 Subject: [PATCH] Slice S0380.19: count Elmhurst shower outlets by type (no more hardcoded 1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Surfaces the lodged shower multiplicity from the Elmhurst Summary §16 on the EPC. Previously `_map_elmhurst_sap_heating` hardcoded: electric_shower_count = 1 if has_electric_shower else None mixer_shower_count = 0 if has_electric_shower else None losing the count for any cert with ≥ 2 outlets. Cert 7800-1501-0922-7127-3563 lodges TWO instantaneous electric showers ("Shower 01" + "Shower 11") but the mapper produced `electric_shower_count=1`. After this slice: electric_shower_count = Σ(s for s in showers if s.outlet_type == "Electric shower") mixer_shower_count = Σ(s for s in showers if s.outlet_type != "Electric shower") **Cascade SAP effect:** None on cert 7800. Appendix J's eq J16 (`N_ES,per_outlet = N_shower / N_outlets`) and eq J18 (Σ_j E_ES,j) are symmetric in N_electric_showers when there are no mixer outlets, so the lodged (64a) kWh and (247a) cost are unchanged. The fix is correctness-by-construction, not a delta-closer for the negative-band certs (their +0.69 GBP total-cost gap traces to the gas hot-water kWh path — separate slice). **Hand-built fixture updates (5):** the cohort-1 hand-builts at `domain/sap10_calculator/worksheet/tests/_elmhurst_worksheet_*.py` previously omitted `electric_shower_count` / `mixer_shower_count` (implicitly None), which matched the mapper's pre-slice None sentinel. Updated each to the lodged counts the mapper now surfaces: 000474: 1 mixer → (0, 1) 000477: 1 mixer → (0, 1) 000480: 1 mixer → (0, 1) 000490: 1 mixer → (0, 1) 000516: 1 mixer → (0, 1) 000487 (already at (1, 0) for an electric-shower lodging) unchanged. Tests: - `test_summary_7800_two_electric_showers_count_as_two_not_one` — pins the multi-shower mapping for cert 7800 (Summary_000890.pdf). - 5 hand-built field-parity tests (`test_from_elmhurst_site_notes_matches_hand_built_*`) now pass at the new integer counts instead of None. Pyright net-zero per file: - datatypes/epc/domain/mapper.py: 32 (baseline 32) - backend/documents_parser/tests/test_summary_pdf_mapper_chain.py: 0 Regression baseline: 699 pass + 10 fail (= prior 698 + 10 + 1 new). Spec refs: - SAP 10.2 Appendix J §1a — outlet counting drives `N_outlets` used in eq J6/J7 (mixer shower water draw) and eq J16/J17/J18 (electric shower energy). - Cert 7800-1501-0922-7127-3563 Summary §16 "Showers" lodgement. Co-Authored-By: Claude Opus 4.7 --- .../tests/fixtures/Summary_000890.pdf | Bin 0 -> 80529 bytes .../tests/test_summary_pdf_mapper_chain.py | 23 ++++++++++ datatypes/epc/domain/mapper.py | 43 ++++++++---------- .../tests/_elmhurst_worksheet_000474.py | 6 +++ .../tests/_elmhurst_worksheet_000477.py | 3 ++ .../tests/_elmhurst_worksheet_000480.py | 3 ++ .../tests/_elmhurst_worksheet_000490.py | 3 ++ .../tests/_elmhurst_worksheet_000516.py | 3 ++ 8 files changed, 60 insertions(+), 24 deletions(-) create mode 100644 backend/documents_parser/tests/fixtures/Summary_000890.pdf diff --git a/backend/documents_parser/tests/fixtures/Summary_000890.pdf b/backend/documents_parser/tests/fixtures/Summary_000890.pdf new file mode 100644 index 0000000000000000000000000000000000000000..9d5afceac5f4f91ec796cff3f23b5cc6e8c82c55 GIT binary patch literal 80529 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%0DPT|7GdPt4F>Y;k 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 None: assert excinfo.value.value == "Quintuple glazed with helium" +def test_summary_7800_two_electric_showers_count_as_two_not_one() -> None: + # Arrange — cert 7800-1501-0922-7127-3563's Summary §16 lodges TWO + # instantaneous electric showers ("Shower 01" + "Shower 11", both + # `outlet_type='Electric shower'`). Pre-Slice S0380.19 the mapper + # hardcoded `electric_shower_count = 1 if has_electric_shower else + # None`, losing the multiplicity. Cascade-equivalent on this cert: + # Appendix J eq J16 (N_ES,per_outlet = N_shower / N_outlets) and + # eq J18 (Σ_j E_ES,j) yield the same (64a) value for 1 vs 2 outlets + # when there are no mixer outlets, so the SAP delta is unchanged + # — but the lodged multiplicity is now surfaced for any future + # cascade consumer that needs it. + pages = _summary_pdf_to_textract_style_pages(_SUMMARY_000890_PDF) + site_notes = ElmhurstSiteNotesExtractor(pages).extract() + + # Act + epc = EpcPropertyDataMapper.from_elmhurst_site_notes(site_notes) + + # Assert — both lodged electric showers surface on the EPC. + assert epc.sap_heating.electric_shower_count == 2 + assert epc.sap_heating.mixer_shower_count == 0 + + def test_summary_0036_flat_unknown_party_wall_routes_to_u_zero() -> None: # Arrange — cert 0036-6325-1100-0063-1226 is a "Flat, Mid-Terrace" # whose Summary lodges party_wall_type='U Unable to determine'. diff --git a/datatypes/epc/domain/mapper.py b/datatypes/epc/domain/mapper.py index edb814e0..68818ee6 100644 --- a/datatypes/epc/domain/mapper.py +++ b/datatypes/epc/domain/mapper.py @@ -3564,12 +3564,19 @@ def _map_elmhurst_sap_heating(survey: ElmhurstSiteNotes) -> SapHeating: # Shower-outlet classification: SAP10.2 Appendix J routes electric # showers via §J line 64a (their own kWh stream) and treats mixer # showers as drawing from the HW system. The Summary PDF lodges - # outlet_type as 'Electric shower' or 'Non-electric shower' — set - # the explicit counts so the cascade doesn't default mixer=1 on - # electric-only dwellings (000487). - has_electric_shower = any( - s.outlet_type == "Electric shower" - for s in survey.baths_and_showers.showers + # outlet_type as 'Electric shower' or 'Non-electric shower' — count + # each outlet by type so the cascade sees the actual lodged number + # (cert 7800-1501-0922-7127-3563 has 2 electric showers; the + # previous hardcoded `1 if has_electric_shower else None` lost the + # multiplicity, and `None` left the cascade defaulting to mixer=1 + # on electric-only dwellings). + electric_shower_count_from_survey = sum( + 1 for s in survey.baths_and_showers.showers + if s.outlet_type == "Electric shower" + ) + mixer_shower_count_from_survey = sum( + 1 for s in survey.baths_and_showers.showers + if s.outlet_type != "Electric shower" ) # Water heating fuel: Summary §15 "Water Heating Fuel Type" lodges # the fuel name as a string ("Mains gas", "Electricity", ...). Map @@ -3635,24 +3642,12 @@ def _map_elmhurst_sap_heating(survey: ElmhurstSiteNotes) -> SapHeating: mh.secondary_heating_sap_code, ), number_baths=survey.baths_and_showers.number_of_baths, - # Zero-shower lodgings resolve to explicit 0 (not None) so the - # cascade doesn't default-assume a mixer — same disposition - # the API path received in slice 102f-prep.8 ("API mapper - # resolves shower_outlets=None → 0 mixers") on cohort cert - # 2225. Non-zero shower lodgings keep the hand-built-fixture - # convention (None for non-electric → cascade derives count - # from `shower_outlets` instead) so the boiler-cohort parity - # tests in this file stay GREEN. - electric_shower_count=( - 0 - if not survey.baths_and_showers.showers - else (1 if has_electric_shower else None) - ), - mixer_shower_count=( - 0 - if not survey.baths_and_showers.showers - else (0 if has_electric_shower else None) - ), + # Both counts derived by tallying the lodged shower list. Zero- + # shower lodgings resolve to (0, 0) — the API path's slice 102f- + # prep.8 disposition for cert 2225. Multi-shower lodgings surface + # the lodged multiplicity (cert 7800: 2 electric showers). + electric_shower_count=electric_shower_count_from_survey, + mixer_shower_count=mixer_shower_count_from_survey, ) diff --git a/domain/sap10_calculator/worksheet/tests/_elmhurst_worksheet_000474.py b/domain/sap10_calculator/worksheet/tests/_elmhurst_worksheet_000474.py index dca08d79..c53c9625 100644 --- a/domain/sap10_calculator/worksheet/tests/_elmhurst_worksheet_000474.py +++ b/domain/sap10_calculator/worksheet/tests/_elmhurst_worksheet_000474.py @@ -221,6 +221,12 @@ def build_epc() -> EpcPropertyData: epc.sap_heating.shower_outlets = ShowerOutlets( shower_outlet=ShowerOutlet(shower_outlet_type="Non-electric shower"), ) + # Slice S0380.19: Elmhurst mapper now counts shower outlets by + # type (electric vs mixer) instead of the previous hardcoded + # 0/1/None sentinels. Cohort cert 000474 lodges 1 non-electric + # (mixer) outlet → electric=0, mixer=1. + epc.sap_heating.electric_shower_count = 0 + epc.sap_heating.mixer_shower_count = 1 # Summary §14 "Heat pump age: Unknown" — surfaced by the Elmhurst # mapper as the str dual-encoding that internal_gains.py reads. # `make_main_heating_detail` doesn't expose the str kwarg, so set diff --git a/domain/sap10_calculator/worksheet/tests/_elmhurst_worksheet_000477.py b/domain/sap10_calculator/worksheet/tests/_elmhurst_worksheet_000477.py index b366b0d3..95a5d25f 100644 --- a/domain/sap10_calculator/worksheet/tests/_elmhurst_worksheet_000477.py +++ b/domain/sap10_calculator/worksheet/tests/_elmhurst_worksheet_000477.py @@ -188,6 +188,9 @@ def build_epc() -> EpcPropertyData: epc.sap_heating.shower_outlets = ShowerOutlets( shower_outlet=ShowerOutlet(shower_outlet_type="Non-electric shower"), ) + # Slice S0380.19: counted shower outlets (was: None/None sentinels). + epc.sap_heating.electric_shower_count = 0 + epc.sap_heating.mixer_shower_count = 1 # Summary §14 "Heat pump age: Unknown" — surfaced by the Elmhurst # mapper as the str dual-encoding that internal_gains.py reads. # `make_main_heating_detail` doesn't expose the str kwarg, so set diff --git a/domain/sap10_calculator/worksheet/tests/_elmhurst_worksheet_000480.py b/domain/sap10_calculator/worksheet/tests/_elmhurst_worksheet_000480.py index 59ebb8f9..bf0462df 100644 --- a/domain/sap10_calculator/worksheet/tests/_elmhurst_worksheet_000480.py +++ b/domain/sap10_calculator/worksheet/tests/_elmhurst_worksheet_000480.py @@ -238,6 +238,9 @@ def build_epc() -> EpcPropertyData: epc.sap_heating.shower_outlets = ShowerOutlets( shower_outlet=ShowerOutlet(shower_outlet_type="Non-electric shower"), ) + # Slice S0380.19: counted shower outlets (was: None/None sentinels). + epc.sap_heating.electric_shower_count = 0 + epc.sap_heating.mixer_shower_count = 1 # Summary §14 "Heat pump age: Unknown" — surfaced by the Elmhurst # mapper as the str dual-encoding that internal_gains.py reads. epc.sap_heating.main_heating_details[0].central_heating_pump_age_str = "Unknown" diff --git a/domain/sap10_calculator/worksheet/tests/_elmhurst_worksheet_000490.py b/domain/sap10_calculator/worksheet/tests/_elmhurst_worksheet_000490.py index 50a37631..80980f7c 100644 --- a/domain/sap10_calculator/worksheet/tests/_elmhurst_worksheet_000490.py +++ b/domain/sap10_calculator/worksheet/tests/_elmhurst_worksheet_000490.py @@ -190,6 +190,9 @@ def build_epc() -> EpcPropertyData: epc.sap_heating.shower_outlets = ShowerOutlets( shower_outlet=ShowerOutlet(shower_outlet_type="Non-electric shower"), ) + # Slice S0380.19: counted shower outlets (was: None/None sentinels). + epc.sap_heating.electric_shower_count = 0 + epc.sap_heating.mixer_shower_count = 1 epc.sap_heating.main_heating_details[0].central_heating_pump_age_str = "Unknown" return epc diff --git a/domain/sap10_calculator/worksheet/tests/_elmhurst_worksheet_000516.py b/domain/sap10_calculator/worksheet/tests/_elmhurst_worksheet_000516.py index 15533dbf..732ddbde 100644 --- a/domain/sap10_calculator/worksheet/tests/_elmhurst_worksheet_000516.py +++ b/domain/sap10_calculator/worksheet/tests/_elmhurst_worksheet_000516.py @@ -214,6 +214,9 @@ def build_epc() -> EpcPropertyData: epc.sap_heating.shower_outlets = ShowerOutlets( shower_outlet=ShowerOutlet(shower_outlet_type="Non-electric shower"), ) + # Slice S0380.19: counted shower outlets (was: None/None sentinels). + epc.sap_heating.electric_shower_count = 0 + epc.sap_heating.mixer_shower_count = 1 epc.sap_heating.main_heating_details[0].central_heating_pump_age_str = "Unknown" return epc