Set roof_construction_type and country_code on the PasHub site-notes path

roof_construction_type was left None in from_site_notes, so the
calculator's "flat"/"sloping ceiling" substring routing in
heat_transmission couldn't detect flat roofs and every roof (flat or
pitched) fell through the pitched default. country_code was never set
on this path either, so the England/Wales/Scotland/NI U-value cascade
(u_floor, u_basement_floor, u_door) had no country to key off.

Sets roof_construction_type from the surveyed roof_space construction_type
(main building + extensions), and country_code="ENG" for this all-English
cohort, matching the existing from_elmhurst_site_notes path.

Fixes #1566

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Jun-te Kim 2026-07-14 17:44:38 +00:00
parent f7e6bf1184
commit 18065ae010
2 changed files with 69 additions and 0 deletions

View file

@ -343,6 +343,13 @@ class EpcPropertyDataMapper:
inspection_date=general.inspection_date,
tenure=general.tenure,
transaction_type=general.transaction_type,
# PAS Hub site notes don't lodge a country field, but the
# cascade reads `country_code` to pick the England (vs Welsh /
# Scottish / NI) U-value tables. Set 'ENG' explicitly, matching
# the Elmhurst path (`from_elmhurst_site_notes`) and this
# cohort's postcodes; a general fix should read the surveyed
# country once PAS Hub lodges one.
country_code="ENG",
roofs=[],
walls=[],
floors=[],
@ -5985,6 +5992,7 @@ def _map_main_building_part(
),
sap_floor_dimensions=_map_floor_dimensions(measurements.main_building.floors),
wall_thickness_mm=main.wall_thickness_mm,
roof_construction_type=roof.construction_type or None,
roof_insulation_location=roof_location,
roof_insulation_thickness=roof_thickness,
floor_type=floor.floor_type,
@ -6015,6 +6023,9 @@ def _map_extension_building_part(
),
sap_floor_dimensions=_map_floor_dimensions(ext_m.floors),
wall_thickness_mm=ext_c.wall_thickness_mm,
roof_construction_type=(
roof.construction_type or None if roof is not None else None
),
roof_insulation_location=roof_location,
roof_insulation_thickness=roof_thickness,
)

View file

@ -34,6 +34,62 @@ def load(filename: str) -> Dict[str, Any]:
return json.load(f) # type: ignore[no-any-return]
class TestRoofConstructionTypeCoding:
"""`from_site_notes` must set `roof_construction_type` from the surveyed
`roof_space.*.construction_type` label, not leave it `None`. The
calculator's `heat_transmission` reads this field for its "flat" /
"sloping ceiling" substring routing (flat-roof U path vs the pitched-
roof cos(30°) inclined-surface factor); leaving it `None` silently
routes every roof flat or pitched through the pitched default
(Guinness cohort, issue #1566).
"""
@pytest.mark.parametrize(
"label, expected",
[
("Pitched roof (Slates or tiles), Access to loft", "Pitched roof (Slates or tiles), Access to loft"),
("Flat", "Flat"), # routes to the flat-roof U path (heat_transmission "flat" substring)
("", None), # no lodging
],
)
def test_main_building_label_passes_through(
self, label: str, expected: Optional[str]
) -> None:
data = load("pashub_rdsap_site_notes_example1.json")
data["roof_space"]["main_building"]["construction_type"] = label
survey = from_dict(PasHubRdSapSiteNotes, data)
result = EpcPropertyDataMapper.from_site_notes(survey)
assert result.sap_building_parts[0].roof_construction_type == expected
def test_extension_label_passes_through(self) -> None:
data = load("pashub_rdsap_site_notes_example2.json")
data["roof_space"]["extensions"][0]["construction_type"] = "Flat"
survey = from_dict(PasHubRdSapSiteNotes, data)
result = EpcPropertyDataMapper.from_site_notes(survey)
extension_part = next(
bp
for bp in result.sap_building_parts
if bp.identifier != BuildingPartIdentifier.MAIN
)
assert extension_part.roof_construction_type == "Flat"
class TestCountryCode:
"""`from_site_notes` must set `country_code` so the cascade's England /
Wales / Scotland / NI U-value tables (`u_floor`, `u_basement_floor`,
`u_door`) resolve correctly. PAS Hub site notes don't lodge a country
field; default to 'ENG' for this all-English cohort (Guinness cohort,
issue #1566), matching the Elmhurst site-notes path.
"""
def test_country_code_is_england(self) -> None:
survey = from_dict(
PasHubRdSapSiteNotes, load("pashub_rdsap_site_notes_example1.json")
)
result = EpcPropertyDataMapper.from_site_notes(survey)
assert result.country_code == "ENG"
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
@ -517,6 +573,7 @@ class TestFromSiteNotesExample1:
status=None,
tenure="Rented Social",
transaction_type="None of the Above",
country_code="ENG",
# Elements (not available from site notes)
roofs=[],
walls=[],
@ -644,6 +701,7 @@ class TestFromSiteNotesExample1:
),
],
wall_thickness_mm=280,
roof_construction_type="Pitched roof (Slates or tiles), Access to loft",
roof_insulation_location="Joists",
roof_insulation_thickness=100,
floor_type="Ground Floor",