mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-27 23:35:01 +00:00
Merge branch 'main' into feature/pashub-map-main-heating-control-1557
This commit is contained in:
commit
328887c4d3
2 changed files with 104 additions and 0 deletions
|
|
@ -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,
|
||||
)
|
||||
|
|
@ -6095,6 +6106,11 @@ def _map_sap_heating(
|
|||
condensing=main.condensing,
|
||||
weather_compensator=main.weather_compensator,
|
||||
central_heating_pump_age_str=main.central_heating_pump_age,
|
||||
# PCDB product id → the appliance's SEDBUK efficiency source
|
||||
# (`gas_oil_boiler_record`); 0 means no product lodged (#1563).
|
||||
main_heating_index_number=(
|
||||
main.product_id if main.product_id > 0 else None
|
||||
),
|
||||
)
|
||||
],
|
||||
has_fixed_air_conditioning=ventilation.has_fixed_air_conditioning,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -314,6 +370,35 @@ class TestWaterHeatingCoding:
|
|||
)
|
||||
|
||||
|
||||
class TestMainHeatingEfficiencySource:
|
||||
"""`from_site_notes` must carry the surveyed PCDB `product_id` onto
|
||||
`main_heating_index_number`, so the calculator reads the actual appliance's
|
||||
SEDBUK efficiency (`gas_oil_boiler_record`) instead of a generic Table-4b
|
||||
default. The site note lodges the boiler by PCDB id (`summer_efficiency` is
|
||||
always 0), so the index IS the efficiency source (issue #1563).
|
||||
"""
|
||||
|
||||
def _survey(self, **main: object) -> PasHubRdSapSiteNotes:
|
||||
data = load("pashub_rdsap_site_notes_example1.json")
|
||||
data["heating_and_hot_water"]["main_heating"].update(main)
|
||||
return from_dict(PasHubRdSapSiteNotes, data)
|
||||
|
||||
def test_product_id_maps_to_index_number(self) -> None:
|
||||
result = EpcPropertyDataMapper.from_site_notes(self._survey(product_id=18400))
|
||||
assert (
|
||||
result.sap_heating.main_heating_details[0].main_heating_index_number
|
||||
== 18400
|
||||
)
|
||||
|
||||
def test_zero_product_id_leaves_index_none(self) -> None:
|
||||
# No PCDB product lodged (e.g. selected by efficiency) → no index.
|
||||
result = EpcPropertyDataMapper.from_site_notes(self._survey(product_id=0))
|
||||
assert (
|
||||
result.sap_heating.main_heating_details[0].main_heating_index_number
|
||||
is None
|
||||
)
|
||||
|
||||
|
||||
class TestFromSiteNotesExample1:
|
||||
"""
|
||||
Fixture: pashub_rdsap_site_notes_example1.json
|
||||
|
|
@ -650,6 +735,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=[],
|
||||
|
|
@ -673,6 +759,7 @@ class TestFromSiteNotesExample1:
|
|||
condensing=True,
|
||||
weather_compensator=False,
|
||||
central_heating_pump_age_str="Unknown",
|
||||
main_heating_index_number=18400, # PCDB product id → efficiency source
|
||||
)
|
||||
],
|
||||
has_fixed_air_conditioning=False,
|
||||
|
|
@ -778,6 +865,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",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue