mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-27 23:35:01 +00:00
Merge branch 'main' into feature/pashub-heat-emitter-code-1556
This commit is contained in:
commit
abab5bf9ac
2 changed files with 76 additions and 5 deletions
|
|
@ -5939,7 +5939,9 @@ def _map_main_building_part(
|
|||
wall_construction=main.walls_construction_type,
|
||||
wall_insulation_type=main.walls_insulation_type,
|
||||
wall_thickness_measured=main.wall_thickness_mm is not None,
|
||||
party_wall_construction=main.party_wall_construction_type,
|
||||
party_wall_construction=_pashub_party_wall_construction_int(
|
||||
main.party_wall_construction_type
|
||||
),
|
||||
sap_floor_dimensions=_map_floor_dimensions(measurements.main_building.floors),
|
||||
wall_thickness_mm=main.wall_thickness_mm,
|
||||
roof_insulation_location=roof_location,
|
||||
|
|
@ -5963,7 +5965,9 @@ def _map_extension_building_part(
|
|||
wall_construction=ext_c.walls_construction_type,
|
||||
wall_insulation_type=ext_c.walls_insulation_type,
|
||||
wall_thickness_measured=ext_c.wall_thickness_mm is not None,
|
||||
party_wall_construction=ext_c.party_wall_construction_type,
|
||||
party_wall_construction=_pashub_party_wall_construction_int(
|
||||
ext_c.party_wall_construction_type
|
||||
),
|
||||
sap_floor_dimensions=_map_floor_dimensions(ext_m.floors),
|
||||
wall_thickness_mm=ext_c.wall_thickness_mm,
|
||||
roof_insulation_location=roof_location,
|
||||
|
|
@ -7093,6 +7097,33 @@ def _pashub_heat_emitter_code(emitter_label: str) -> Union[int, str]:
|
|||
if code is None:
|
||||
raise UnmappedPasHubLabel("heat emitter", emitter_label)
|
||||
return code
|
||||
# PasHub surveyed `party_wall_construction_type` label → SAP10 party-wall code
|
||||
# (RdSAP 10 §5.10 Table 15) that `u_party_wall` consumes. Same target codes as
|
||||
# the GOV.UK-API (`_API_PARTY_WALL_CONSTRUCTION_TO_SAP10`) and Elmhurst
|
||||
# (`_ELMHURST_PARTY_WALL_CODE_TO_SAP10`) siblings — PasHub just lodges the full
|
||||
# human label. `0` is the explicit "unknown" sentinel (u_party_wall → 0.25 house
|
||||
# / 0.0 flat); `None` is "no party wall / no lodging".
|
||||
_PASHUB_PARTY_WALL_CONSTRUCTION_TO_SAP10: Dict[str, Optional[int]] = {
|
||||
"Solid Masonry, Timber Frame, or System Built": 3, # U=0.0
|
||||
"Cavity Masonry, Unfilled": 4, # U=0.5
|
||||
"Cavity Masonry, Filled": 11, # U=0.2 (WALL_CAVITY_FILLED_PARTY)
|
||||
"Unable to determine": 0, # unknown sentinel → u_party_wall default
|
||||
"Not applicable": None, # no party wall
|
||||
}
|
||||
|
||||
|
||||
def _pashub_party_wall_construction_int(label: Optional[str]) -> Optional[int]:
|
||||
"""Resolve a PasHub surveyed `party_wall_construction_type` label to its SAP10
|
||||
party-wall code at the mapper boundary. A blank label is "no lodging" and
|
||||
returns None; a non-empty label the lookup does not cover strict-raises
|
||||
`UnmappedPasHubLabel` so the coverage gap is fixed here rather than silently
|
||||
defaulting every party wall to the U=0.25 house value downstream (#1559)."""
|
||||
if label is None or not label.strip():
|
||||
return None
|
||||
key = label.strip()
|
||||
if key not in _PASHUB_PARTY_WALL_CONSTRUCTION_TO_SAP10:
|
||||
raise UnmappedPasHubLabel("party wall construction", label)
|
||||
return _PASHUB_PARTY_WALL_CONSTRUCTION_TO_SAP10[key]
|
||||
|
||||
|
||||
def _resolve_elmhurst_underfloor_subtype(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import json
|
||||
import os
|
||||
from datetime import date
|
||||
from typing import Any, Dict
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
import pytest
|
||||
|
||||
|
|
@ -19,7 +19,7 @@ from datatypes.epc.domain.epc_property_data import (
|
|||
ShowerOutlet,
|
||||
ShowerOutlets,
|
||||
)
|
||||
from datatypes.epc.domain.mapper import EpcPropertyDataMapper
|
||||
from datatypes.epc.domain.mapper import EpcPropertyDataMapper, UnmappedPasHubLabel
|
||||
from datatypes.epc.schema.tests.helpers import from_dict
|
||||
from datatypes.epc.surveys.pashub_rdsap_site_notes import PasHubRdSapSiteNotes
|
||||
|
||||
|
|
@ -34,6 +34,46 @@ def load(filename: str) -> Dict[str, Any]:
|
|||
return json.load(f) # type: ignore[no-any-return]
|
||||
|
||||
|
||||
class TestPartyWallConstructionCoding:
|
||||
"""`from_site_notes` must int-code the PAS Hub `party_wall_construction_type`
|
||||
label to the SAP10 code `u_party_wall` consumes (RdSAP 10 Table 15), not copy
|
||||
the raw label. A raw label reads as `None` and every party wall silently takes
|
||||
the U=0.25 house default — phantom heat loss on the ~165/205 "solid" party
|
||||
walls that should be U=0.0 (Guinness cohort, issue #1559).
|
||||
"""
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"label, expected_code",
|
||||
[
|
||||
("Solid Masonry, Timber Frame, or System Built", 3), # U=0.0
|
||||
("Cavity Masonry, Unfilled", 4), # U=0.5
|
||||
("Cavity Masonry, Filled", 11), # U=0.2 (party-wall-only code)
|
||||
("Unable to determine", 0), # unknown sentinel → u_party_wall 0.25 (house)
|
||||
("Not applicable", None), # no party wall
|
||||
("", None), # no lodging
|
||||
],
|
||||
)
|
||||
def test_label_maps_to_sap_code(
|
||||
self, label: str, expected_code: Optional[int]
|
||||
) -> None:
|
||||
data = load("pashub_rdsap_site_notes_example1.json")
|
||||
data["building_construction"]["main_building"][
|
||||
"party_wall_construction_type"
|
||||
] = label
|
||||
survey = from_dict(PasHubRdSapSiteNotes, data)
|
||||
result = EpcPropertyDataMapper.from_site_notes(survey)
|
||||
assert result.sap_building_parts[0].party_wall_construction == expected_code
|
||||
|
||||
def test_unknown_label_strict_raises(self) -> None:
|
||||
data = load("pashub_rdsap_site_notes_example1.json")
|
||||
data["building_construction"]["main_building"][
|
||||
"party_wall_construction_type"
|
||||
] = "Reinforced Adamantium"
|
||||
survey = from_dict(PasHubRdSapSiteNotes, data)
|
||||
with pytest.raises(UnmappedPasHubLabel):
|
||||
EpcPropertyDataMapper.from_site_notes(survey)
|
||||
|
||||
|
||||
class TestFromSiteNotesExample1:
|
||||
"""
|
||||
Fixture: pashub_rdsap_site_notes_example1.json
|
||||
|
|
@ -472,7 +512,7 @@ class TestFromSiteNotesExample1:
|
|||
wall_construction="Cavity",
|
||||
wall_insulation_type="As built",
|
||||
wall_thickness_measured=True,
|
||||
party_wall_construction="Cavity Masonry, Unfilled",
|
||||
party_wall_construction=4, # "Cavity Masonry, Unfilled" → SAP10 code 4 (U=0.5)
|
||||
sap_floor_dimensions=[
|
||||
SapFloorDimension(
|
||||
room_height_m=2.37,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue