mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
fix(elmhurst-mapper): map "Bottled gas" main fuel to bottled LPG, not mains gas
An LPG-boiler dwelling on the Summary → from_elmhurst_site_notes path mapped to main_fuel_type=26 (mains gas), making it indistinguishable from a mains-gas boiler downstream — wrong Table 12/32 cost / CO2 / PE (bottled LPG is ~10.30 p/kWh vs mains gas 3.48), and it defeats any "non-gas → gas only with a mains-gas connection" gate (an LPG dwelling looks already-gas). Root cause: the recommendation worksheets lodge the boiler carrier as §15.0 "Water Heating Fuel Type: Bottled gas" (§14.0 carries only SAP code 115, a Table 4b gas-family row, + "Main gas: Yes" in §14.2 — a mains-gas CONNECTION, not the heating fuel). "Bottled gas" was absent from `_ELMHURST_MAIN_FUEL_TO_SAP10`, so the §15.0 fuel resolved to None and `_elmhurst_gas_boiler_main_fuel` fell through priority-1 to the mains-gas meter flag → 26. Map "Bottled gas" → 3 (bottled LPG MAIN heating): code 3 routes via `API_FUEL_TO_TABLE_32`/`API_FUEL_TO_TABLE_12` → Table-code 3 (10.30 / 9.46 p/kWh). NOT the legacy "LPG bottled": 5 entry — API code 5 = anthracite, and `canonical_fuel_code` resolves the same-valued Table-32 code 5 to anthracite (3.64 p/kWh), so a 5 here mis-prices the dwelling as cheap solid fuel (verified: a 5 mapping moved SAP the WRONG way, 42.33 → 45.11; code 3 moves it to -6.40 vs the worksheet's -6.6499). Also add 3 to `_GAS_LPG_MAIN_FUEL_CODES` so the §15.0-lodged bottled-LPG water fuel is adopted as the boiler's space-heating carrier (priority 1) instead of the meter flag. Effect: main_fuel_type=3 (bottled LPG) and water_heating_fuel=3 (was None). Mains-gas certs still → 26 (full regression suite green bar the 3 pre-existing unrelated fails); the MissingMainFuelType tripwire still fires for genuinely-undeterminable carriers. Spec: SAP 10.2 Table 12 / RdSAP 10 Table 32 (PDF p.95) — bottled LPG main heating fuel code 3. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
b473f6a1ec
commit
90de1fc976
3 changed files with 39 additions and 1 deletions
BIN
backend/documents_parser/tests/fixtures/Summary_001431_lpg_boiler.pdf
vendored
Normal file
BIN
backend/documents_parser/tests/fixtures/Summary_001431_lpg_boiler.pdf
vendored
Normal file
Binary file not shown.
|
|
@ -84,6 +84,7 @@ _SUMMARY_000890_PDF = _FIXTURES / "Summary_000890.pdf" # cert 7800 (two electri
|
|||
_SUMMARY_000565_PDF = _FIXTURES / "Summary_000565.pdf" # cert 000565 (5-bp Elmhurst-only)
|
||||
_SUMMARY_001431_CASE20_PDF = _FIXTURES / "Summary_001431_case20.pdf" # sim case 20 (storage heaters + RR type-2 + wrapped "Double between 2002 and 2021" glazing)
|
||||
_SUMMARY_001431_TOPFLOOR_PDF = _FIXTURES / "Summary_001431_topfloor_flat.pdf" # gas-boiler-upgrade recommendation "after" — top-floor flat, PS sloping roof; exercises the Date-Built age-band + flat-position layout regressions
|
||||
_SUMMARY_001431_LPG_PDF = _FIXTURES / "Summary_001431_lpg_boiler.pdf" # lpg-boiler recommendation "before" — §14 SAP code 115, §15 "Bottled gas"; exercises the bottled-LPG main-fuel mapping
|
||||
|
||||
# GOV.UK EPB API JSON for cert 001479 — the API-path counterpart of the
|
||||
# Summary_001479.pdf fixture. Together they drive the API ≡ Summary
|
||||
|
|
@ -180,6 +181,27 @@ def test_summary_001431_topfloor_extracts_main_property_age_band() -> None:
|
|||
assert survey.construction_age_band == "C 1930-1949"
|
||||
|
||||
|
||||
def test_summary_001431_lpg_boiler_maps_main_fuel_to_bottled_lpg() -> None:
|
||||
# Arrange — the lpg-boiler recommendation "before" Summary lodges
|
||||
# §14.0 SAP code 115 (a Table 4b gas-family boiler row), §15.0
|
||||
# "Water Heating Fuel Type: Bottled gas", and §14.2 "Main gas: Yes".
|
||||
# The boiler burns bottled LPG, not mains gas; the mapper must
|
||||
# resolve the carrier from the "Bottled gas" label, NOT default to
|
||||
# mains gas via the (contradictory) meter flag. Table-route code 3 =
|
||||
# bottled LPG main heating (Table 32/12 10.30/9.46 p/kWh) — NOT code
|
||||
# 5, which collides with anthracite (`canonical_fuel_code`).
|
||||
pages = _summary_pdf_to_textract_style_pages(_SUMMARY_001431_LPG_PDF)
|
||||
site_notes = ElmhurstSiteNotesExtractor(pages).extract()
|
||||
|
||||
# Act
|
||||
epc = EpcPropertyDataMapper.from_elmhurst_site_notes(site_notes)
|
||||
|
||||
# Assert
|
||||
main = epc.sap_heating.main_heating_details[0]
|
||||
assert main.main_fuel_type == 3
|
||||
assert epc.sap_heating.water_heating_fuel == 3
|
||||
|
||||
|
||||
def test_summary_001431_topfloor_flat_classified_as_top_floor() -> None:
|
||||
# Arrange — the recommendation "after" Summary lodges §6.0 "Position
|
||||
# of flat in block of flats: Top Floor": floor "A Another dwelling
|
||||
|
|
|
|||
|
|
@ -4537,6 +4537,17 @@ _ELMHURST_MAIN_FUEL_TO_SAP10: Dict[str, int] = {
|
|||
# existing oddity as "Oil" → 8; both labels are unused by any live
|
||||
# fixture). Live form on Elmhurst worksheets is "Bulk LPG".
|
||||
"Bulk LPG": 27,
|
||||
# Elmhurst Summary §14.0 / §15.0 lodging form for BOTTLED LPG
|
||||
# (cylinders) — the recommendation worksheets lodge "Bottled gas" as
|
||||
# the §15.0 "Water Heating Fuel Type" for an SAP-code-115 boiler.
|
||||
# 3 = API/epc-codes `main_fuel` code for bottled LPG main heating,
|
||||
# which routes via `API_FUEL_TO_TABLE_32`/`API_FUEL_TO_TABLE_12` →
|
||||
# Table-code 3 (bottled LPG main heating, 10.30 / 9.46 p/kWh). NOT
|
||||
# the legacy "LPG bottled": 5 above — API code 5 = anthracite, and
|
||||
# `canonical_fuel_code` resolves the same-valued Table-32 code 5 to
|
||||
# anthracite (3.64 p/kWh), so a 5 here would mis-price the dwelling
|
||||
# as cheap solid fuel (the cohort-2100 -61-SAP collision class).
|
||||
"Bottled gas": 3,
|
||||
# Elmhurst Summary §15.0 "Water Heating Fuel Type" labels for the
|
||||
# bio-liquid fuels added to the EES dict above. Values are Table 32
|
||||
# codes verbatim (no API enum collision). Spec: SAP 10.2 Table 12
|
||||
|
|
@ -4873,7 +4884,12 @@ _GAS_BOILER_SAP_MAIN_HEATING_CODES: Final[frozenset[int]] = (
|
|||
# of these so it can't mis-assign electricity from a separate immersion
|
||||
# (where §15.0 lodges the immersion's fuel, not the boiler's) — that
|
||||
# case still strict-raises `MissingMainFuelType` to force a mapper fix.
|
||||
_GAS_LPG_MAIN_FUEL_CODES: Final[frozenset[int]] = frozenset({1, 5, 6, 7, 26, 27})
|
||||
# 3 = bottled LPG main heating ("Bottled gas" label); the other LPG
|
||||
# carriers are the legacy API LPG codes (5/6/7) + the live "Bulk LPG"
|
||||
# (27). All count as a gas/LPG carrier so `_elmhurst_gas_boiler_main_fuel`
|
||||
# adopts a §15.0-lodged bottled-LPG water fuel for the boiler's space-
|
||||
# heating carrier instead of falling through to the mains-gas meter flag.
|
||||
_GAS_LPG_MAIN_FUEL_CODES: Final[frozenset[int]] = frozenset({1, 3, 5, 6, 7, 26, 27})
|
||||
|
||||
# SAP10 main-fuel code for mains gas (`_ELMHURST_MAIN_FUEL_TO_SAP10`
|
||||
# "Mains gas"). Used when a Table 4b gas boiler's carrier can't be read
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue