Merge pull request #1525 from Hestia-Homes/fix/rdsap-optional-built-form

Make RdSapSchema21_0_1.built_form optional — some real certs omit it entirely
This commit is contained in:
Jun-te Kim 2026-07-09 13:10:28 +01:00 committed by GitHub
commit 84c259c17e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 24 additions and 4 deletions

View file

@ -2420,7 +2420,7 @@ class EpcPropertyDataMapper:
sap_version=schema.sap_version,
dwelling_type=schema.dwelling_type,
property_type=str(schema.property_type),
built_form=str(schema.built_form),
built_form=None if schema.built_form is None else str(schema.built_form),
address_line_1=schema.address_line_1,
address_line_2=schema.address_line_2,
postcode=schema.postcode,

View file

@ -460,7 +460,6 @@ class RdSapSchema21_0_1:
postcode: str
hot_water: EnergyElement
post_town: str
built_form: int
door_count: int
region_code: int
report_type: int
@ -512,9 +511,16 @@ class RdSapSchema21_0_1:
renewable_heat_incentive: RenewableHeatIncentive
draughtproofed_door_count: int
energy_consumption_current: int
# kw_only on just this field: the class isn't kw_only overall, and several
# required fields (calculation_software_version etc.) already follow it.
# kw_only on just these fields: the class isn't kw_only overall, and several
# required fields (calculation_software_version etc.) already follow them.
has_fixed_air_conditioning: Optional[str] = field(default=None, kw_only=True)
# Some real-API certs omit `built_form` entirely (e.g. cert
# 2461-5385-9264-6821-5357, property_id=750232, portfolio 824) rather than
# sending a code — `from_dict` otherwise hard-fails the whole cert. The
# mapper/SAP cascade (`_api_sheltered_sides`) already treats a non-int
# built_form as "no lodging" and falls back to the documented default, so
# None here is an already-safe degradation, not a new failure mode.
built_form: Optional[int] = field(default=None, kw_only=True)
calculation_software_version: str
energy_consumption_potential: int
environmental_impact_current: int

View file

@ -400,3 +400,17 @@ class TestRdSapSchema21_0_1AgainstRealApiCert:
assert epc.schema_type == "RdSAP-Schema-21.0.1"
assert epc.sap_heating is not None
assert len(epc.sap_windows) > 0
def test_cert_with_no_built_form_key_parses_as_none(self) -> None:
# Arrange — some real gov-API certs omit `built_form` entirely (not
# null: absent). property_id=750232 (portfolio 824) hit exactly this
# on cert 2461-5385-9264-6821-5357 and failed to parse at all.
real_doc = load("21_0_1_real.json")
del real_doc["built_form"]
# Act
epc = from_dict(RdSapSchema21_0_1, real_doc)
# Assert — parsing no longer hard-fails; the field degrades to None,
# same as the mapper/SAP-cascade already do for a non-int built_form.
assert epc.built_form is None