mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-12 13:29:04 +00:00
Map to domain from epc api objects 🟥
This commit is contained in:
parent
f875714c2b
commit
94b367873a
2 changed files with 237 additions and 25 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
from typing import List, Optional
|
from typing import List, Optional, Union
|
||||||
|
|
||||||
from datatypes.epc.domain.dwelling import (
|
from datatypes.epc.domain.dwelling import (
|
||||||
Dwelling,
|
Dwelling,
|
||||||
|
|
@ -15,29 +15,46 @@ from datatypes.epc.domain.dwelling import (
|
||||||
WallDetails,
|
WallDetails,
|
||||||
WindowDetails,
|
WindowDetails,
|
||||||
)
|
)
|
||||||
|
from datatypes.epc.schema.rdsap_schema_17_0 import RdSapSchema17_0
|
||||||
|
from datatypes.epc.schema.rdsap_schema_17_1 import RdSapSchema17_1
|
||||||
|
from datatypes.epc.schema.rdsap_schema_18_0 import RdSapSchema18_0
|
||||||
|
from datatypes.epc.schema.rdsap_schema_19_0 import RdSapSchema19_0
|
||||||
|
from datatypes.epc.schema.rdsap_schema_20_0_0 import RdSapSchema20_0_0
|
||||||
|
from datatypes.epc.schema.rdsap_schema_21_0_0 import RdSapSchema21_0_0
|
||||||
|
from datatypes.epc.schema.rdsap_schema_21_0_1 import RdSapSchema21_0_1
|
||||||
from datatypes.epc.surveys.pashub_rdsap_site_notes import (
|
from datatypes.epc.surveys.pashub_rdsap_site_notes import (
|
||||||
PasHubRdSapSiteNotes,
|
PasHubRdSapSiteNotes,
|
||||||
Window,
|
Window,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
AnyRdSapSchema = Union[
|
||||||
|
RdSapSchema17_0,
|
||||||
|
RdSapSchema17_1,
|
||||||
|
RdSapSchema18_0,
|
||||||
|
RdSapSchema19_0,
|
||||||
|
RdSapSchema20_0_0,
|
||||||
|
RdSapSchema21_0_0,
|
||||||
|
RdSapSchema21_0_1,
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class DwellingMapper:
|
class DwellingMapper:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_site_notes(survey: PasHubRdSapSiteNotes) -> Dwelling:
|
def from_site_notes(survey: PasHubRdSapSiteNotes) -> Dwelling:
|
||||||
return Dwelling(
|
return Dwelling(
|
||||||
property_details=_property_details(survey),
|
property_details=_sn_property_details(survey),
|
||||||
floor_dimensions=_floor_dimensions(survey),
|
floor_dimensions=_sn_floor_dimensions(survey),
|
||||||
walls=_walls(survey),
|
walls=_sn_walls(survey),
|
||||||
roof=_roof(survey),
|
roof=_sn_roof(survey),
|
||||||
floor=_floor(survey),
|
floor=_sn_floor(survey),
|
||||||
windows=[_window(w) for w in survey.windows],
|
windows=[_sn_window(w) for w in survey.windows],
|
||||||
main_heating=_main_heating(survey),
|
main_heating=_sn_main_heating(survey),
|
||||||
hot_water=_hot_water(survey),
|
hot_water=_sn_hot_water(survey),
|
||||||
ventilation=_ventilation(survey),
|
ventilation=_sn_ventilation(survey),
|
||||||
renewables=_renewables(survey),
|
renewables=_sn_renewables(survey),
|
||||||
lighting=_lighting(survey),
|
lighting=_sn_lighting(survey),
|
||||||
secondary_heating=_secondary_heating(survey),
|
secondary_heating=_sn_secondary_heating(survey),
|
||||||
number_of_habitable_rooms=survey.room_count_elements.number_of_habitable_rooms,
|
number_of_habitable_rooms=survey.room_count_elements.number_of_habitable_rooms,
|
||||||
number_of_external_doors=survey.room_count_elements.number_of_external_doors,
|
number_of_external_doors=survey.room_count_elements.number_of_external_doors,
|
||||||
number_of_open_chimneys=survey.room_count_elements.number_of_open_chimneys,
|
number_of_open_chimneys=survey.room_count_elements.number_of_open_chimneys,
|
||||||
|
|
@ -48,8 +65,16 @@ class DwellingMapper:
|
||||||
waste_water_heat_recovery=survey.room_count_elements.waste_water_heat_recovery,
|
waste_water_heat_recovery=survey.room_count_elements.waste_water_heat_recovery,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_rdsap_schema(_schema: AnyRdSapSchema) -> Dwelling:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
def _property_details(survey: PasHubRdSapSiteNotes) -> PropertyDetails:
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Site notes helpers
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _sn_property_details(survey: PasHubRdSapSiteNotes) -> PropertyDetails:
|
||||||
return PropertyDetails(
|
return PropertyDetails(
|
||||||
property_type=survey.general.property_type,
|
property_type=survey.general.property_type,
|
||||||
built_form=survey.general.detachment_type,
|
built_form=survey.general.detachment_type,
|
||||||
|
|
@ -64,7 +89,7 @@ def _property_details(survey: PasHubRdSapSiteNotes) -> PropertyDetails:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _floor_dimensions(survey: PasHubRdSapSiteNotes) -> List[FloorDimensions]:
|
def _sn_floor_dimensions(survey: PasHubRdSapSiteNotes) -> List[FloorDimensions]:
|
||||||
dims = [
|
dims = [
|
||||||
FloorDimensions(
|
FloorDimensions(
|
||||||
total_floor_area_m2=f.area_m2,
|
total_floor_area_m2=f.area_m2,
|
||||||
|
|
@ -87,7 +112,7 @@ def _floor_dimensions(survey: PasHubRdSapSiteNotes) -> List[FloorDimensions]:
|
||||||
return dims
|
return dims
|
||||||
|
|
||||||
|
|
||||||
def _walls(survey: PasHubRdSapSiteNotes) -> WallDetails:
|
def _sn_walls(survey: PasHubRdSapSiteNotes) -> WallDetails:
|
||||||
mb = survey.building_construction.main_building
|
mb = survey.building_construction.main_building
|
||||||
return WallDetails(
|
return WallDetails(
|
||||||
construction_type=mb.walls_construction_type,
|
construction_type=mb.walls_construction_type,
|
||||||
|
|
@ -97,7 +122,7 @@ def _walls(survey: PasHubRdSapSiteNotes) -> WallDetails:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _roof(survey: PasHubRdSapSiteNotes) -> RoofDetails:
|
def _sn_roof(survey: PasHubRdSapSiteNotes) -> RoofDetails:
|
||||||
mb = survey.roof_space.main_building
|
mb = survey.roof_space.main_building
|
||||||
return RoofDetails(
|
return RoofDetails(
|
||||||
construction_type=mb.construction_type,
|
construction_type=mb.construction_type,
|
||||||
|
|
@ -107,7 +132,7 @@ def _roof(survey: PasHubRdSapSiteNotes) -> RoofDetails:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _floor(survey: PasHubRdSapSiteNotes) -> FloorDetails:
|
def _sn_floor(survey: PasHubRdSapSiteNotes) -> FloorDetails:
|
||||||
f = survey.building_construction.floor
|
f = survey.building_construction.floor
|
||||||
return FloorDetails(
|
return FloorDetails(
|
||||||
construction_type=f.floor_construction,
|
construction_type=f.floor_construction,
|
||||||
|
|
@ -115,7 +140,7 @@ def _floor(survey: PasHubRdSapSiteNotes) -> FloorDetails:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _window(w: Window) -> WindowDetails:
|
def _sn_window(w: Window) -> WindowDetails:
|
||||||
return WindowDetails(
|
return WindowDetails(
|
||||||
glazing_type=w.glazing_type,
|
glazing_type=w.glazing_type,
|
||||||
orientation=w.orientation,
|
orientation=w.orientation,
|
||||||
|
|
@ -127,7 +152,7 @@ def _window(w: Window) -> WindowDetails:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _main_heating(survey: PasHubRdSapSiteNotes) -> MainHeatingSystem:
|
def _sn_main_heating(survey: PasHubRdSapSiteNotes) -> MainHeatingSystem:
|
||||||
mh = survey.heating_and_hot_water.main_heating
|
mh = survey.heating_and_hot_water.main_heating
|
||||||
return MainHeatingSystem(
|
return MainHeatingSystem(
|
||||||
fuel=mh.fuel,
|
fuel=mh.fuel,
|
||||||
|
|
@ -143,7 +168,7 @@ def _main_heating(survey: PasHubRdSapSiteNotes) -> MainHeatingSystem:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _hot_water(survey: PasHubRdSapSiteNotes) -> HotWaterSystem:
|
def _sn_hot_water(survey: PasHubRdSapSiteNotes) -> HotWaterSystem:
|
||||||
wh = survey.heating_and_hot_water.water_heating
|
wh = survey.heating_and_hot_water.water_heating
|
||||||
return HotWaterSystem(
|
return HotWaterSystem(
|
||||||
source=wh.system,
|
source=wh.system,
|
||||||
|
|
@ -154,14 +179,14 @@ def _hot_water(survey: PasHubRdSapSiteNotes) -> HotWaterSystem:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _secondary_heating(survey: PasHubRdSapSiteNotes) -> Optional[SecondaryHeatingSystem]:
|
def _sn_secondary_heating(survey: PasHubRdSapSiteNotes) -> Optional[SecondaryHeatingSystem]:
|
||||||
fuel = survey.heating_and_hot_water.secondary_heating.secondary_fuel
|
fuel = survey.heating_and_hot_water.secondary_heating.secondary_fuel
|
||||||
if fuel == "No Secondary Heating":
|
if fuel == "No Secondary Heating":
|
||||||
return None
|
return None
|
||||||
return SecondaryHeatingSystem(fuel=fuel)
|
return SecondaryHeatingSystem(fuel=fuel)
|
||||||
|
|
||||||
|
|
||||||
def _ventilation(survey: PasHubRdSapSiteNotes) -> VentilationDetails:
|
def _sn_ventilation(survey: PasHubRdSapSiteNotes) -> VentilationDetails:
|
||||||
v = survey.ventilation
|
v = survey.ventilation
|
||||||
return VentilationDetails(
|
return VentilationDetails(
|
||||||
ventilation_type=v.ventilation_type,
|
ventilation_type=v.ventilation_type,
|
||||||
|
|
@ -178,7 +203,7 @@ def _ventilation(survey: PasHubRdSapSiteNotes) -> VentilationDetails:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _renewables(survey: PasHubRdSapSiteNotes) -> RenewablesDetails:
|
def _sn_renewables(survey: PasHubRdSapSiteNotes) -> RenewablesDetails:
|
||||||
r = survey.renewables
|
r = survey.renewables
|
||||||
return RenewablesDetails(
|
return RenewablesDetails(
|
||||||
has_photovoltaic=r.photovoltaic_array,
|
has_photovoltaic=r.photovoltaic_array,
|
||||||
|
|
@ -189,7 +214,7 @@ def _renewables(survey: PasHubRdSapSiteNotes) -> RenewablesDetails:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _lighting(survey: PasHubRdSapSiteNotes) -> LightingDetails:
|
def _sn_lighting(survey: PasHubRdSapSiteNotes) -> LightingDetails:
|
||||||
rc = survey.room_count_elements
|
rc = survey.room_count_elements
|
||||||
return LightingDetails(
|
return LightingDetails(
|
||||||
number_of_led_bulbs=rc.number_of_fixed_led_bulbs,
|
number_of_led_bulbs=rc.number_of_fixed_led_bulbs,
|
||||||
|
|
|
||||||
187
datatypes/epc/domain/tests/test_from_rdsap_schema.py
Normal file
187
datatypes/epc/domain/tests/test_from_rdsap_schema.py
Normal file
|
|
@ -0,0 +1,187 @@
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from datatypes.epc.domain import Dwelling
|
||||||
|
from datatypes.epc.domain.mapper import DwellingMapper
|
||||||
|
from datatypes.epc.schema.rdsap_schema_21_0_1 import RdSapSchema21_0_1
|
||||||
|
from datatypes.epc.schema.tests.helpers import from_dict
|
||||||
|
|
||||||
|
FIXTURES = os.path.join(
|
||||||
|
os.path.dirname(__file__),
|
||||||
|
"../../schema/tests/fixtures",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def load(filename: str) -> Dict[str, Any]:
|
||||||
|
with open(os.path.join(FIXTURES, filename)) as f:
|
||||||
|
return json.load(f) # type: ignore[no-any-return]
|
||||||
|
|
||||||
|
|
||||||
|
class TestFromRdSapSchema21_0_1:
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def dwelling(self) -> Dwelling:
|
||||||
|
schema = from_dict(RdSapSchema21_0_1, load("21_0_1.json"))
|
||||||
|
return DwellingMapper.from_rdsap_schema(schema)
|
||||||
|
|
||||||
|
# --- property_details ---
|
||||||
|
|
||||||
|
def test_property_type(self, dwelling: Dwelling) -> None:
|
||||||
|
# property_type: 0 → House
|
||||||
|
assert dwelling.property_details.property_type == "House"
|
||||||
|
|
||||||
|
def test_built_form(self, dwelling: Dwelling) -> None:
|
||||||
|
# built_form: 2 → Semi-detached
|
||||||
|
assert dwelling.property_details.built_form == "Semi-detached"
|
||||||
|
|
||||||
|
def test_tenure(self, dwelling: Dwelling) -> None:
|
||||||
|
# tenure: 1 → Owner-occupied
|
||||||
|
assert dwelling.property_details.tenure == "Owner-occupied"
|
||||||
|
|
||||||
|
def test_construction_age_band(self, dwelling: Dwelling) -> None:
|
||||||
|
# taken directly from sap_building_parts[0].construction_age_band
|
||||||
|
assert dwelling.property_details.construction_age_band == "M"
|
||||||
|
|
||||||
|
def test_mains_gas_available(self, dwelling: Dwelling) -> None:
|
||||||
|
# sap_energy_source.mains_gas: "Y"
|
||||||
|
assert dwelling.property_details.mains_gas_available is True
|
||||||
|
|
||||||
|
def test_electricity_smart_meter(self, dwelling: Dwelling) -> None:
|
||||||
|
# sap_energy_source.electricity_smart_meter_present: "true"
|
||||||
|
assert dwelling.property_details.electricity_smart_meter is True
|
||||||
|
|
||||||
|
def test_gas_smart_meter(self, dwelling: Dwelling) -> None:
|
||||||
|
# sap_energy_source.gas_smart_meter_present: "false"
|
||||||
|
assert dwelling.property_details.gas_smart_meter is False
|
||||||
|
|
||||||
|
# --- floor_dimensions ---
|
||||||
|
|
||||||
|
def test_floor_count(self, dwelling: Dwelling) -> None:
|
||||||
|
# one SapFloorDimension in the fixture
|
||||||
|
assert len(dwelling.floor_dimensions) == 1
|
||||||
|
|
||||||
|
def test_floor_area(self, dwelling: Dwelling) -> None:
|
||||||
|
# total_floor_area.value: 45.82
|
||||||
|
assert dwelling.floor_dimensions[0].total_floor_area_m2 == 45.82
|
||||||
|
|
||||||
|
def test_floor_height(self, dwelling: Dwelling) -> None:
|
||||||
|
# room_height.value: 2.45
|
||||||
|
assert dwelling.floor_dimensions[0].height_m == 2.45
|
||||||
|
|
||||||
|
def test_heat_loss_perimeter(self, dwelling: Dwelling) -> None:
|
||||||
|
# heat_loss_perimeter.value: 19.5
|
||||||
|
assert dwelling.floor_dimensions[0].heat_loss_perimeter_m == 19.5
|
||||||
|
|
||||||
|
def test_party_wall_length(self, dwelling: Dwelling) -> None:
|
||||||
|
# party_wall_length.value: 7.9
|
||||||
|
assert dwelling.floor_dimensions[0].party_wall_length_m == 7.9
|
||||||
|
|
||||||
|
# --- walls ---
|
||||||
|
|
||||||
|
def test_wall_construction_type(self, dwelling: Dwelling) -> None:
|
||||||
|
# wall_construction: 4 → Cavity wall
|
||||||
|
assert dwelling.walls.construction_type == "Cavity wall"
|
||||||
|
|
||||||
|
def test_wall_insulation_type(self, dwelling: Dwelling) -> None:
|
||||||
|
# wall_insulation_type: 2 → As built, insulated (assumed)
|
||||||
|
assert dwelling.walls.insulation_type == "As built, insulated (assumed)"
|
||||||
|
|
||||||
|
def test_wall_thickness(self, dwelling: Dwelling) -> None:
|
||||||
|
# wall_thickness_measured: "N" → thickness unknown
|
||||||
|
assert dwelling.walls.thickness_mm is None
|
||||||
|
|
||||||
|
# --- roof ---
|
||||||
|
|
||||||
|
def test_roof_insulation_thickness(self, dwelling: Dwelling) -> None:
|
||||||
|
# roof_insulation_thickness: "200mm"
|
||||||
|
assert dwelling.roof.insulation_thickness_mm == 200
|
||||||
|
|
||||||
|
def test_roof_has_rooms_in_roof(self, dwelling: Dwelling) -> None:
|
||||||
|
# sap_room_in_roof is present in the fixture
|
||||||
|
assert dwelling.roof.has_rooms_in_roof is True
|
||||||
|
|
||||||
|
# --- windows ---
|
||||||
|
|
||||||
|
def test_window_count(self, dwelling: Dwelling) -> None:
|
||||||
|
assert len(dwelling.windows) == 1
|
||||||
|
|
||||||
|
def test_window_height(self, dwelling: Dwelling) -> None:
|
||||||
|
assert dwelling.windows[0].height_m == 2.0
|
||||||
|
|
||||||
|
def test_window_width(self, dwelling: Dwelling) -> None:
|
||||||
|
assert dwelling.windows[0].width_m == 1.2
|
||||||
|
|
||||||
|
def test_window_draught_proofed(self, dwelling: Dwelling) -> None:
|
||||||
|
# draught_proofed: "true"
|
||||||
|
assert dwelling.windows[0].draught_proofed is True
|
||||||
|
|
||||||
|
# --- main_heating ---
|
||||||
|
|
||||||
|
def test_main_heating_fuel(self, dwelling: Dwelling) -> None:
|
||||||
|
# main_fuel_type: 26 → Mains gas
|
||||||
|
assert dwelling.main_heating.fuel == "Mains gas"
|
||||||
|
|
||||||
|
def test_main_heating_flue_gas_heat_recovery(self, dwelling: Dwelling) -> None:
|
||||||
|
# has_fghrs: "N"
|
||||||
|
assert dwelling.main_heating.flue_gas_heat_recovery is False
|
||||||
|
|
||||||
|
# --- hot_water ---
|
||||||
|
|
||||||
|
def test_hot_water_cylinder_present(self, dwelling: Dwelling) -> None:
|
||||||
|
# has_hot_water_cylinder: "true"
|
||||||
|
assert dwelling.hot_water.cylinder_size is not None
|
||||||
|
|
||||||
|
# --- secondary_heating ---
|
||||||
|
|
||||||
|
def test_secondary_heating_present(self, dwelling: Dwelling) -> None:
|
||||||
|
# secondary_fuel_type: 25 → electricity
|
||||||
|
assert dwelling.secondary_heating is not None
|
||||||
|
|
||||||
|
# --- ventilation ---
|
||||||
|
|
||||||
|
def test_no_fixed_air_conditioning(self, dwelling: Dwelling) -> None:
|
||||||
|
# has_fixed_air_conditioning: "false"
|
||||||
|
assert dwelling.ventilation.has_fixed_air_conditioning is False
|
||||||
|
|
||||||
|
# --- renewables ---
|
||||||
|
|
||||||
|
def test_no_solar_hot_water(self, dwelling: Dwelling) -> None:
|
||||||
|
# solar_water_heating: "N"
|
||||||
|
assert dwelling.renewables.has_solar_hot_water is False
|
||||||
|
|
||||||
|
def test_no_wind_turbines(self, dwelling: Dwelling) -> None:
|
||||||
|
# wind_turbines_count: 0
|
||||||
|
assert dwelling.renewables.has_wind_turbines is False
|
||||||
|
|
||||||
|
def test_pv_battery_count(self, dwelling: Dwelling) -> None:
|
||||||
|
# pv_battery_count: 1
|
||||||
|
assert dwelling.renewables.number_of_pv_batteries == 1
|
||||||
|
|
||||||
|
# --- lighting ---
|
||||||
|
|
||||||
|
def test_led_bulbs(self, dwelling: Dwelling) -> None:
|
||||||
|
assert dwelling.lighting.number_of_led_bulbs == 10
|
||||||
|
|
||||||
|
def test_cfl_bulbs(self, dwelling: Dwelling) -> None:
|
||||||
|
assert dwelling.lighting.number_of_cfl_bulbs == 5
|
||||||
|
|
||||||
|
def test_incandescent_bulbs(self, dwelling: Dwelling) -> None:
|
||||||
|
assert dwelling.lighting.number_of_incandescent_bulbs == 0
|
||||||
|
|
||||||
|
# --- dwelling-level counts ---
|
||||||
|
|
||||||
|
def test_habitable_rooms(self, dwelling: Dwelling) -> None:
|
||||||
|
assert dwelling.number_of_habitable_rooms == 5
|
||||||
|
|
||||||
|
def test_external_doors(self, dwelling: Dwelling) -> None:
|
||||||
|
assert dwelling.number_of_external_doors == 3
|
||||||
|
|
||||||
|
def test_open_chimneys(self, dwelling: Dwelling) -> None:
|
||||||
|
assert dwelling.number_of_open_chimneys == 1
|
||||||
|
|
||||||
|
def test_no_conservatory(self, dwelling: Dwelling) -> None:
|
||||||
|
# conservatory_type: 1 → no conservatory
|
||||||
|
assert dwelling.has_conservatory is False
|
||||||
Loading…
Add table
Reference in a new issue