mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
Fall back to nested has_hot_water_cylinder for full-SAP certs
A SAP-Schema-17.0 full-SAP cohort cert (8265-7433-3220-9736-7902) omits the top-level `has_hot_water_cylinder` and lodges it only under `sap_heating`. The required top-level field on SapSchema17_1 made `from_dict` raise and skip the cert. Fix: make the top-level field Optional, and have the full-SAP mapper (`from_sap_schema_17_1`, also used by 17.0/18.0.0 via delegation) fall back to `sap_heating.has_hot_water_cylinder`. RdSAP mappers are unchanged (they keep the top-level required field; their SapHeating has no such attribute). Cert now maps + calculates (sap 76). Regression tests for the nested true/false fallback. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
22cb47a280
commit
18063a2d7f
3 changed files with 41 additions and 2 deletions
|
|
@ -735,7 +735,11 @@ class EpcPropertyDataMapper:
|
|||
postcode=schema.postcode,
|
||||
post_town=schema.post_town,
|
||||
total_floor_area_m2=float(schema.total_floor_area),
|
||||
has_hot_water_cylinder=schema.has_hot_water_cylinder == "true",
|
||||
has_hot_water_cylinder=(
|
||||
schema.has_hot_water_cylinder
|
||||
or schema.sap_heating.has_hot_water_cylinder
|
||||
)
|
||||
== "true",
|
||||
has_fixed_air_conditioning=schema.has_fixed_air_conditioning == "true",
|
||||
solar_water_heating=False,
|
||||
extensions_count=0,
|
||||
|
|
|
|||
|
|
@ -77,6 +77,39 @@ class TestFromSapSchema17_1Tracer:
|
|||
assert result.total_floor_area_m2 == 68.0
|
||||
|
||||
|
||||
class TestFullSapHasHotWaterCylinderFallback:
|
||||
"""Some full-SAP certs (e.g. SAP-Schema-17.0 cert 8265-7433-3220-9736-7902)
|
||||
omit the top-level `has_hot_water_cylinder` and lodge it only under
|
||||
`sap_heating`. The required top-level field made `from_dict` raise; it is now
|
||||
optional and the mapper falls back to the nested value."""
|
||||
|
||||
def test_top_level_omitted_falls_back_to_sap_heating(self) -> None:
|
||||
# Arrange — drop the top-level flag, keep the nested one ("true").
|
||||
data = load("sap_17_1.json")
|
||||
data.pop("has_hot_water_cylinder", None)
|
||||
data["sap_heating"]["has_hot_water_cylinder"] = "true"
|
||||
|
||||
# Act
|
||||
schema = from_dict(SapSchema17_1, data)
|
||||
result = EpcPropertyDataMapper.from_sap_schema_17_1(schema)
|
||||
|
||||
# Assert — resolved from sap_heating, not crashed
|
||||
assert result.has_hot_water_cylinder is True
|
||||
|
||||
def test_top_level_omitted_nested_false_resolves_false(self) -> None:
|
||||
# Arrange
|
||||
data = load("sap_17_1.json")
|
||||
data.pop("has_hot_water_cylinder", None)
|
||||
data["sap_heating"]["has_hot_water_cylinder"] = "false"
|
||||
|
||||
# Act
|
||||
schema = from_dict(SapSchema17_1, data)
|
||||
result = EpcPropertyDataMapper.from_sap_schema_17_1(schema)
|
||||
|
||||
# Assert
|
||||
assert result.has_hot_water_cylinder is False
|
||||
|
||||
|
||||
class TestFromSapSchema17_1FabricDescriptions:
|
||||
"""Slice 3 (D4): the measured-U fabric descriptions flow through to
|
||||
epc.walls/floors/roofs so the engine's u_wall/u_floor/u_roof can parse
|
||||
|
|
|
|||
|
|
@ -184,7 +184,6 @@ class SapSchema17_1:
|
|||
postcode: str
|
||||
post_town: str
|
||||
inspection_date: str
|
||||
has_hot_water_cylinder: str
|
||||
has_fixed_air_conditioning: str
|
||||
roofs: List[EnergyElement]
|
||||
walls: List[EnergyElement]
|
||||
|
|
@ -197,3 +196,6 @@ class SapSchema17_1:
|
|||
# measured living-room area (m²); the engine consumes it via a back-solved
|
||||
# habitable_rooms_count (Table 27). Optional — 100% present in the corpus.
|
||||
living_area: Optional[Union[int, float]] = None
|
||||
# Some 17.0 full-SAP certs omit the top-level flag and lodge it only under
|
||||
# sap_heating; the mapper falls back to sap_heating.has_hot_water_cylinder.
|
||||
has_hot_water_cylinder: Optional[str] = None
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue