Map full-SAP cert identity and scalar fields to EpcPropertyData 🟩

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jun-te Kim 2026-06-15 13:05:38 +00:00
parent 0079752eab
commit c3fd9a6872
3 changed files with 98 additions and 0 deletions

View file

@ -43,6 +43,7 @@ from datatypes.epc.schema.rdsap_schema_17_1 import (
RdSapSchema17_1, RdSapSchema17_1,
EnergyElement as EnergyElement_17_1, EnergyElement as EnergyElement_17_1,
) )
from datatypes.epc.schema.sap_schema_17_1 import SapSchema17_1
from datatypes.epc.schema.rdsap_schema_18_0 import ( from datatypes.epc.schema.rdsap_schema_18_0 import (
RdSapSchema18_0, RdSapSchema18_0,
EnergyElement as EnergyElement_18_0, EnergyElement as EnergyElement_18_0,
@ -619,6 +620,63 @@ class EpcPropertyDataMapper:
], ],
) )
@staticmethod
def from_sap_schema_17_1(schema: SapSchema17_1) -> EpcPropertyData:
# Built incrementally via TDD — see scripts/hyde/mapping_decisions.md.
# Tracer slice: identity + scalars are mapped; the load-bearing
# collections (walls/floors/roofs, sap_windows, sap_building_parts,
# door/room counts) are left empty here and filled by later slices
# (D1 perimeter, D2 openings, D3 living-area, D4 fabric-U).
return EpcPropertyData(
uprn=schema.uprn,
dwelling_type=(
schema.dwelling_type
if isinstance(schema.dwelling_type, str)
else schema.dwelling_type.value
),
inspection_date=date.fromisoformat(schema.inspection_date),
tenure=str(schema.tenure),
transaction_type=str(schema.transaction_type),
address_line_1=schema.address_line_1,
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_fixed_air_conditioning=schema.has_fixed_air_conditioning == "true",
solar_water_heating=False,
door_count=0,
wet_rooms_count=0,
extensions_count=0,
heated_rooms_count=0,
open_chimneys_count=0,
habitable_rooms_count=0,
insulated_door_count=0,
cfl_fixed_lighting_bulbs_count=0,
led_fixed_lighting_bulbs_count=0,
incandescent_fixed_lighting_bulbs_count=0,
roofs=[],
walls=[],
floors=[],
main_heating=[],
sap_windows=[],
sap_building_parts=[],
sap_heating=SapHeating(
instantaneous_wwhrs=InstantaneousWwhrs(),
main_heating_details=[],
has_fixed_air_conditioning=schema.has_fixed_air_conditioning == "true",
),
sap_energy_source=SapEnergySource(
mains_gas=False,
meter_type="",
pv_battery_count=0,
wind_turbines_count=0,
gas_smart_meter_present=False,
is_dwelling_export_capable=False,
wind_turbines_terrain_type="",
electricity_smart_meter_present=False,
),
)
@staticmethod @staticmethod
def from_rdsap_schema_17_1(schema: RdSapSchema17_1) -> EpcPropertyData: def from_rdsap_schema_17_1(schema: RdSapSchema17_1) -> EpcPropertyData:
es = schema.sap_energy_source es = schema.sap_energy_source

View file

@ -16,6 +16,8 @@ from typing import Any, Dict
import pytest import pytest
from datatypes.epc.domain.epc_property_data import EpcPropertyData
from datatypes.epc.domain.mapper import EpcPropertyDataMapper
from datatypes.epc.schema.sap_schema_17_1 import SapSchema17_1 from datatypes.epc.schema.sap_schema_17_1 import SapSchema17_1
from datatypes.epc.schema.tests.helpers import from_dict from datatypes.epc.schema.tests.helpers import from_dict
@ -47,3 +49,29 @@ class TestSapSchema17_1Parsing:
# Assert # Assert
assert schema.uprn == 10092973954 assert schema.uprn == 10092973954
assert schema.total_floor_area == 68 assert schema.total_floor_area == 68
class TestFromSapSchema17_1Tracer:
"""Slice 2: the mapper produces a valid EpcPropertyData from a full-SAP cert."""
@pytest.mark.parametrize("fixture", _ALL_FIXTURES)
def test_produces_epc_property_data(self, fixture: str) -> None:
# Arrange
schema = from_dict(SapSchema17_1, load(fixture))
# Act
result = EpcPropertyDataMapper.from_sap_schema_17_1(schema)
# Assert
assert isinstance(result, EpcPropertyData)
def test_maps_sample_uprn_and_floor_area(self) -> None:
# Arrange
schema = from_dict(SapSchema17_1, load("sap_17_1.json"))
# Act
result = EpcPropertyDataMapper.from_sap_schema_17_1(schema)
# Assert
assert result.uprn == 10092973954
assert result.total_floor_area_m2 == 68.0

View file

@ -12,9 +12,21 @@ raises only on a missing *required* field.
from dataclasses import dataclass from dataclasses import dataclass
from typing import Union from typing import Union
from .common import DescriptionV1
@dataclass @dataclass
class SapSchema17_1: class SapSchema17_1:
uprn: int uprn: int
schema_type: str schema_type: str
total_floor_area: Union[int, float] total_floor_area: Union[int, float]
# full SAP lodges dwelling_type as a localised object OR a plain string.
dwelling_type: Union[str, DescriptionV1]
tenure: Union[str, int]
transaction_type: int
address_line_1: str
postcode: str
post_town: str
inspection_date: str
has_hot_water_cylinder: str
has_fixed_air_conditioning: str