mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
Fail loud on unmapped full-SAP opening-type codes 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
dde98fb684
commit
8746eabb70
2 changed files with 35 additions and 1 deletions
|
|
@ -53,6 +53,7 @@ from datatypes.epc.schema.sap_schema_17_1 import (
|
|||
_SAP_OPENING_TYPE_WINDOW: Final[int] = 4
|
||||
_SAP_OPENING_TYPE_ROOF_WINDOW: Final[int] = 5
|
||||
_SAP_OPENING_TYPE_DOORS: Final[frozenset[int]] = frozenset({1, 2, 3})
|
||||
_SAP_KNOWN_OPENING_TYPES: Final[frozenset[int]] = frozenset({1, 2, 3, 4, 5})
|
||||
# SAP-typical glazing solar transmittance when an opening-type omits it.
|
||||
_SAP_DEFAULT_SOLAR_TRANSMITTANCE: Final[float] = 0.63
|
||||
# SAP-typical window frame factor when an opening-type omits it.
|
||||
|
|
@ -639,6 +640,10 @@ class EpcPropertyDataMapper:
|
|||
# Identity + scalars + fabric descriptions (D4) + openings (D2) are
|
||||
# mapped; sap_building_parts (D1 perimeter) and habitable_rooms_count
|
||||
# (D3 living-area) are still filled by later slices.
|
||||
# D2: fail loud on any placed opening whose joined type is outside the
|
||||
# known taxonomy {1/2/3 door, 4 window, 5 roof window}, rather than
|
||||
# silently dropping it from the envelope.
|
||||
_sap_assert_known_opening_types(schema)
|
||||
door_count, insulated_door_u = _sap_door_aggregates(schema)
|
||||
return EpcPropertyData(
|
||||
uprn=schema.uprn,
|
||||
|
|
@ -2433,6 +2438,20 @@ class EpcPropertyDataMapper:
|
|||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _sap_assert_known_opening_types(schema: SapSchema17_1) -> None:
|
||||
"""D2: raise `UnmappedApiCode` if any placed opening joins to an
|
||||
opening-type whose `type` code is outside the known taxonomy. Keeps a
|
||||
new opening variant from silently vanishing from the envelope."""
|
||||
types: Dict[Union[str, int], SapOpeningType_SAP_17_1] = {
|
||||
ot.name: ot for ot in schema.sap_opening_types
|
||||
}
|
||||
for bp in schema.sap_building_parts:
|
||||
for op in bp.sap_openings:
|
||||
ot = types.get(op.type)
|
||||
if ot is not None and ot.type not in _SAP_KNOWN_OPENING_TYPES:
|
||||
raise UnmappedApiCode("sap_opening_type", ot.type)
|
||||
|
||||
|
||||
def _sap_door_aggregates(schema: SapSchema17_1) -> Tuple[int, Optional[float]]:
|
||||
"""D2: collapse door openings (opening-type 1/2/3) onto the engine's door
|
||||
model — a count and a single U-value. When a cert lodges doors with
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ from typing import Any, Dict
|
|||
import pytest
|
||||
|
||||
from datatypes.epc.domain.epc_property_data import EpcPropertyData
|
||||
from datatypes.epc.domain.mapper import EpcPropertyDataMapper
|
||||
from datatypes.epc.domain.mapper import EpcPropertyDataMapper, UnmappedApiCode
|
||||
from datatypes.epc.schema.sap_schema_17_1 import SapSchema17_1
|
||||
from datatypes.epc.schema.tests.helpers import from_dict
|
||||
|
||||
|
|
@ -192,3 +192,18 @@ class TestFromSapSchema17_1RoofWindows:
|
|||
# A ground-floor flat lodges no roof windows.
|
||||
result = self._map("sap_17_1.json")
|
||||
assert not result.sap_roof_windows
|
||||
|
||||
|
||||
class TestFromSapSchema17_1UnknownOpeningType:
|
||||
"""Slice 4d (D2): an opening-type code outside {1,2,3 door, 4 window,
|
||||
5 roof window} must fail loud rather than silently drop the opening."""
|
||||
|
||||
def test_unmapped_opening_type_raises(self) -> None:
|
||||
# Arrange — corrupt the placed window opening-type to an unknown code
|
||||
data = load("sap_17_1.json")
|
||||
data["sap_opening_types"][1]["type"] = 99 # was 4 (window), now unmapped
|
||||
schema = from_dict(SapSchema17_1, data)
|
||||
|
||||
# Act / Assert
|
||||
with pytest.raises(UnmappedApiCode):
|
||||
EpcPropertyDataMapper.from_sap_schema_17_1(schema)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue