Merge pull request #1389 from Hestia-Homes/fix/rdsap-mapper-main-heating-controls

Normalize RdSAP main_heating_controls mapping on the degraded 17.0 path
This commit is contained in:
Jun-te Kim 2026-07-01 14:58:20 +01:00 committed by GitHub
commit 460d687b58
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 59 additions and 0 deletions

View file

@ -634,6 +634,17 @@ class EpcPropertyDataMapper:
main_heating=EpcPropertyDataMapper._map_energy_elements(
schema.main_heating
),
# First control system if multiple — mirrors the 19.0/21.0.1 paths.
# 17.0 lodges main_heating_controls (List[EnergyElement]) but omitted
# it, leaving main_heating_controls=None so the heating-control
# recommendations (programmer/room-stat/TRVs) had nothing to read.
main_heating_controls=(
EpcPropertyDataMapper._map_energy_element(
schema.main_heating_controls[0]
)
if schema.main_heating_controls
else None
),
window=EpcPropertyDataMapper._map_energy_element(schema.window),
lighting=EpcPropertyDataMapper._map_energy_element(schema.lighting),
hot_water=EpcPropertyDataMapper._map_energy_element(schema.hot_water),

View file

@ -0,0 +1,48 @@
"""Regression: the RdSAP-Schema-17.0 mapper path must carry the lodged
main_heating_controls, like every other API path (19.0/20.0/21.x).
PRD #1385 (mapper normalization). 17.0 lodges `main_heating_controls`
(List[EnergyElement]) but the mapper omitted it, leaving the domain field None
while the 19.0/21.0.1 paths map the first control via `_map_energy_element`. The
mapped element is the same EnergyElement type 17.0 already carries for
`main_heating` / `window`, so this is straight parity.
"""
from __future__ import annotations
import json
from pathlib import Path
from typing import Any
from datatypes.epc.domain.mapper import EpcPropertyDataMapper
# 17.0 cert lodging a main-heating control ("Programmer, TRVs and bypass").
_FIXTURE = Path("tests/fixtures/epc_prediction/PE71NT/cert-26619d8e7f8e.json")
def _load() -> dict[str, Any]:
data: dict[str, Any] = json.loads(_FIXTURE.read_text())
assert data["schema_type"] == "RdSAP-Schema-17.0"
return data
def test_17_0_maps_lodged_main_heating_controls() -> None:
data = _load()
assert data["main_heating_controls"], "fixture must lodge a control"
epc = EpcPropertyDataMapper.from_api_response(data)
# Was None on the 17.0 path; now carries the first lodged control element.
assert epc.main_heating_controls is not None
assert epc.main_heating_controls.description == "Programmer, TRVs and bypass"
assert epc.main_heating_controls.energy_efficiency_rating == 3
def test_17_0_no_controls_maps_to_none() -> None:
# An empty control list maps to None (no phantom element), like the siblings.
data = _load()
data["main_heating_controls"] = []
epc = EpcPropertyDataMapper.from_api_response(data)
assert epc.main_heating_controls is None