mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
S0380.221: default a missing API post_town so the cert stays mappable
A 2026-register cert (4519-9056-4002-0222-4802) omits the top-level post_town entirely (its town sits only in address_line_3 "BARNSTAPLE"). RdSapSchema21_0_x declares post_town as a required no-default field, so from_dict raised "missing required field 'post_town'" and blocked the whole cert from computing. post_town is address metadata the SAP cascade never reads (no consumer in domain/sap10_calculator/), so default an absent post_town to "" in a from_api_response pre-processor (mirroring _normalize_shower_outlets) — inert for the calculation, keeps the cert mappable. The schema dataclass can't simply give post_town a default: it is a plain (non-kw_only) dataclass with 57 required fields after post_town, so a mid-list default would break field ordering. Validated: cert 4519 now maps (post_town="") and computes SAP cont 74.68 vs lodged 75. §4 suite 2392 passed; mapper.py pyright unchanged at 32; new tests suppress reportPrivateUsage (net-zero). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
d164850dd3
commit
aac3f0690a
2 changed files with 51 additions and 0 deletions
|
|
@ -1911,6 +1911,7 @@ class EpcPropertyDataMapper:
|
|||
"""
|
||||
|
||||
data = _normalize_shower_outlets(data)
|
||||
data = _default_missing_post_town(data)
|
||||
schema = data.get("schema_type", "")
|
||||
if schema == "RdSAP-Schema-21.0.1":
|
||||
from datatypes.epc.schema.rdsap_schema_21_0_1 import RdSapSchema21_0_1
|
||||
|
|
@ -2081,6 +2082,26 @@ def _normalize_shower_outlets(data: Dict[str, Any]) -> Dict[str, Any]:
|
|||
return {**data, "sap_heating": new_sap_heating}
|
||||
|
||||
|
||||
def _default_missing_post_town(data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Default an absent top-level `post_town` to "" before `from_dict`.
|
||||
|
||||
`RdSapSchema21_0_x.post_town` is a required (no-default) field, so a
|
||||
real-API cert that omits it (observed on a 2026-register cert whose
|
||||
town sits only in `address_line_3`) makes `from_dict` raise
|
||||
"missing required field 'post_town'", blocking the whole cert.
|
||||
`post_town` is address metadata that the SAP cascade never reads, so
|
||||
defaulting it to "" is inert for the calculation while keeping the
|
||||
cert mappable. The schema dataclass can't simply give the field a
|
||||
default — it is a plain (non-kw_only) dataclass with 57 required
|
||||
fields after `post_town`, so a mid-list default would break field
|
||||
ordering; pre-processing here mirrors `_normalize_shower_outlets`.
|
||||
|
||||
Mutates a shallow copy so the caller's dict is untouched."""
|
||||
if "post_town" in data:
|
||||
return data
|
||||
return {**data, "post_town": ""}
|
||||
|
||||
|
||||
def _count_shower_outlets_by_type(
|
||||
schema_shower_outlets: Any, target_type: int,
|
||||
) -> Optional[int]:
|
||||
|
|
|
|||
|
|
@ -849,3 +849,33 @@ class TestApiFloorConstructionCode:
|
|||
|
||||
# Assert — no raise; None defers to the cascade's Table 19 default.
|
||||
assert result is None
|
||||
|
||||
|
||||
class TestDefaultMissingPostTown:
|
||||
"""`_default_missing_post_town` keeps a cert mappable when the
|
||||
register omits the required `post_town` field (observed on a 2026
|
||||
cert whose town sits only in address_line_3). post_town is address
|
||||
metadata the SAP cascade never reads, so defaulting it to "" before
|
||||
`from_dict` is inert for the calculation."""
|
||||
|
||||
def test_absent_post_town_is_defaulted_to_empty_string(self) -> None:
|
||||
# Arrange
|
||||
from datatypes.epc.domain.mapper import _default_missing_post_town # pyright: ignore[reportPrivateUsage]
|
||||
doc: dict[str, object] = {"postcode": "EX31 2LE"}
|
||||
|
||||
# Act
|
||||
result = _default_missing_post_town(doc)
|
||||
|
||||
# Assert
|
||||
assert result["post_town"] == ""
|
||||
|
||||
def test_present_post_town_is_untouched(self) -> None:
|
||||
# Arrange — regression guard: a lodged town passes through.
|
||||
from datatypes.epc.domain.mapper import _default_missing_post_town # pyright: ignore[reportPrivateUsage]
|
||||
doc: dict[str, object] = {"post_town": "BARNSTAPLE"}
|
||||
|
||||
# Act
|
||||
result = _default_missing_post_town(doc)
|
||||
|
||||
# Assert
|
||||
assert result["post_town"] == "BARNSTAPLE"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue