Normalize PasHub site-notes emitter label to its SAP10 code 🟩

_map_sap_heating resolves the raw emitter label (e.g. Radiators) to its
SAP10 emitter code via _pashub_heat_emitter_code, reusing the Elmhurst
emitter map. Blank passes through; an unrecognised label strict-raises
UnmappedPasHubLabel at the mapper boundary (ADR-0015) instead of
resurfacing as the calculator's UnmappedSapCode: heat_emitter_type.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Daniel Roth 2026-07-14 15:17:06 +00:00
parent 8d45fcb544
commit 5b0f25847c

View file

@ -6028,7 +6028,7 @@ def _map_sap_heating(
MainHeatingDetail(
has_fghrs=main.flue_gas_heat_recovery_system,
main_fuel_type=_pashub_main_fuel_code(fuel_type),
heat_emitter_type=main.emitter,
heat_emitter_type=_pashub_heat_emitter_code(main.emitter),
emitter_temperature=main.emitter_temperature,
fan_flue_present=main.fan_assist,
main_heating_control=main.controls,
@ -7081,6 +7081,20 @@ def _pashub_main_fuel_code(fuel_label: str) -> Union[int, str]:
return code
def _pashub_heat_emitter_code(emitter_label: str) -> Union[int, str]:
"""Resolve a PasHub surveyed main-heating emitter label to its SAP10 emitter
code at the mapper boundary (ADR-0015), reusing the Elmhurst emitter map
(`Radiators -> 1`). A blank label passes through unchanged; a non-empty label
the map does not cover strict-raises `UnmappedPasHubLabel` here rather than
resurfacing downstream as the calculator's `UnmappedSapCode: heat_emitter_type`."""
if not emitter_label:
return emitter_label
code = _ELMHURST_HEAT_EMITTER_TO_SAP10.get(emitter_label)
if code is None:
raise UnmappedPasHubLabel("heat emitter", emitter_label)
return code
def _resolve_elmhurst_underfloor_subtype(
main_floor: ElmhurstFloorDetails,
main_age_band: str,