Int-code the surveyed window orientation to its SAP10 octant code 🟥

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-07-15 08:45:22 +00:00
parent b208234e62
commit 5276b45836

View file

@ -405,6 +405,55 @@ class TestCylinderInsulationTypeCoding:
EpcPropertyDataMapper.from_site_notes(survey)
class TestWindowOrientationCoding:
"""`from_site_notes` must int-code the PAS Hub window `orientation` label
to the SAP 10.2 octant code (1..8) the solar-gains cascade consumes.
`_orientation` in `solar_gains.py` returns None for a non-int and the
window is then skipped for solar gain entirely so a raw string zeroes
EVERY window's solar contribution (100% of the Guinness cohort; the
single largest accuracy lever). PasHub lodges it space-separated
("South East") where the shared octant map keys hyphenated.
"""
@pytest.mark.parametrize(
"label, expected_code",
[
("North", 1),
("North East", 2),
("East", 3),
("South East", 4),
("South", 5),
("South West", 6),
("West", 7),
("North West", 8),
],
)
def test_label_maps_to_octant(
self, label: str, expected_code: int
) -> None:
data = load("pashub_rdsap_site_notes_example1.json")
data["windows"][0]["orientation"] = label
survey = from_dict(PasHubRdSapSiteNotes, data)
result = EpcPropertyDataMapper.from_site_notes(survey)
assert result.sap_windows[0].orientation == expected_code
def test_blank_orientation_stays_none(self) -> None:
# A window with no lodged orientation degrades to None (the cascade
# skips it) — NOT a strict-raise, NOT a default octant.
data = load("pashub_rdsap_site_notes_example1.json")
data["windows"][0]["orientation"] = ""
survey = from_dict(PasHubRdSapSiteNotes, data)
result = EpcPropertyDataMapper.from_site_notes(survey)
assert result.sap_windows[0].orientation is None
def test_unknown_orientation_strict_raises(self) -> None:
data = load("pashub_rdsap_site_notes_example1.json")
data["windows"][0]["orientation"] = "Sou'-sou'-west"
survey = from_dict(PasHubRdSapSiteNotes, data)
with pytest.raises(UnmappedPasHubLabel):
EpcPropertyDataMapper.from_site_notes(survey)
class TestWallDryLinedMapping:
"""A surveyed "Wall Dry-Lined? Yes" must reach
`sap_building_parts[*].wall_dry_lined` the flag the wall-U cascade
@ -1260,7 +1309,9 @@ class TestFromSiteNotesExample1:
assert result.sap_windows[0].draught_proofed is True
def test_window_orientation(self, result: EpcPropertyData) -> None:
assert result.sap_windows[0].orientation == "South East"
# "South East" → SAP10 octant code 4 (the solar-gains cascade reads
# orientation as an int; a raw string silently zeroes the gain).
assert result.sap_windows[0].orientation == 4
def test_window_glazing_type(self, result: EpcPropertyData) -> None:
# windows[].glazing_type: "Double glazing, Unknown install date"
@ -1468,7 +1519,7 @@ class TestFromSiteNotesExample1:
SapWindow(
frame_material="Wooden or PVC",
glazing_gap="16 mm or more",
orientation="South East",
orientation=4, # "South East" → SAP10 octant 4
window_type="Window",
glazing_type=3, # "Double glazing, Unknown install date" → cascade code 3
window_width=1.0,
@ -1481,7 +1532,7 @@ class TestFromSiteNotesExample1:
SapWindow(
frame_material="Wooden or PVC",
glazing_gap="16 mm or more",
orientation="South East",
orientation=4, # "South East" → SAP10 octant 4
window_type="Window",
glazing_type=3, # "Double glazing, Unknown install date" → cascade code 3
window_width=0.96,
@ -1494,7 +1545,7 @@ class TestFromSiteNotesExample1:
SapWindow(
frame_material="Wooden or PVC",
glazing_gap="16 mm or more",
orientation="North West",
orientation=8, # "North West" → SAP10 octant 8
window_type="Window",
glazing_type=3, # "Double glazing, Unknown install date" → cascade code 3
window_width=0.96,
@ -1507,7 +1558,7 @@ class TestFromSiteNotesExample1:
SapWindow(
frame_material="Wooden or PVC",
glazing_gap="16 mm or more",
orientation="North West",
orientation=8, # "North West" → SAP10 octant 8
window_type="Window",
glazing_type=3, # "Double glazing, Unknown install date" → cascade code 3
window_width=0.97,