diff --git a/datatypes/epc/domain/mapper.py b/datatypes/epc/domain/mapper.py index 3f3fa003..85d2b022 100644 --- a/datatypes/epc/domain/mapper.py +++ b/datatypes/epc/domain/mapper.py @@ -1,9 +1,198 @@ -from datatypes.epc.domain.dwelling import Dwelling -from datatypes.epc.surveys.pashub_rdsap_site_notes import PasHubRdSapSiteNotes +from typing import List, Optional + +from datatypes.epc.domain.dwelling import ( + Dwelling, + FloorDimensions, + FloorDetails, + HotWaterSystem, + LightingDetails, + MainHeatingSystem, + PropertyDetails, + RenewablesDetails, + RoofDetails, + SecondaryHeatingSystem, + VentilationDetails, + WallDetails, + WindowDetails, +) +from datatypes.epc.surveys.pashub_rdsap_site_notes import ( + PasHubRdSapSiteNotes, + Window, +) class DwellingMapper: @staticmethod def from_site_notes(survey: PasHubRdSapSiteNotes) -> Dwelling: - raise NotImplementedError + return Dwelling( + property_details=_property_details(survey), + floor_dimensions=_floor_dimensions(survey), + walls=_walls(survey), + roof=_roof(survey), + floor=_floor(survey), + windows=[_window(w) for w in survey.windows], + main_heating=_main_heating(survey), + hot_water=_hot_water(survey), + ventilation=_ventilation(survey), + renewables=_renewables(survey), + lighting=_lighting(survey), + secondary_heating=_secondary_heating(survey), + 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_open_chimneys=survey.room_count_elements.number_of_open_chimneys, + number_of_blocked_chimneys=survey.room_count_elements.number_of_blocked_chimneys, + has_conservatory=survey.conservatories.has_conservatory, + number_of_baths=survey.water_use.number_of_baths, + number_of_showers=len(survey.water_use.showers), + waste_water_heat_recovery=survey.room_count_elements.waste_water_heat_recovery, + ) + + +def _property_details(survey: PasHubRdSapSiteNotes) -> PropertyDetails: + return PropertyDetails( + property_type=survey.general.property_type, + built_form=survey.general.detachment_type, + tenure=survey.general.tenure, + number_of_storeys=survey.general.number_of_storeys, + construction_age_band=survey.building_construction.main_building.age_range, + transaction_type=survey.general.transaction_type, + terrain_type=survey.general.terrain_type, + mains_gas_available=survey.general.mains_gas_available, + electricity_smart_meter=survey.general.electricity_smart_meter, + gas_smart_meter=survey.general.gas_smart_meter, + ) + + +def _floor_dimensions(survey: PasHubRdSapSiteNotes) -> List[FloorDimensions]: + dims = [ + FloorDimensions( + total_floor_area_m2=f.area_m2, + height_m=f.height_m, + heat_loss_perimeter_m=f.heat_loss_perimeter_m, + party_wall_length_m=f.pwl_m, + ) + for f in survey.building_measurements.main_building.floors + ] + for ext in survey.building_measurements.extensions or []: + dims.extend( + FloorDimensions( + total_floor_area_m2=f.area_m2, + height_m=f.height_m, + heat_loss_perimeter_m=f.heat_loss_perimeter_m, + party_wall_length_m=f.pwl_m, + ) + for f in ext.floors + ) + return dims + + +def _walls(survey: PasHubRdSapSiteNotes) -> WallDetails: + mb = survey.building_construction.main_building + return WallDetails( + construction_type=mb.walls_construction_type, + insulation_type=mb.walls_insulation_type, + thickness_mm=mb.wall_thickness_mm, + party_wall_construction_type=mb.party_wall_construction_type, + ) + + +def _roof(survey: PasHubRdSapSiteNotes) -> RoofDetails: + mb = survey.roof_space.main_building + return RoofDetails( + construction_type=mb.construction_type, + insulation_at=mb.insulation_at, + insulation_thickness_mm=mb.insulation_thickness_mm, + has_rooms_in_roof=mb.rooms_in_roof, + ) + + +def _floor(survey: PasHubRdSapSiteNotes) -> FloorDetails: + f = survey.building_construction.floor + return FloorDetails( + construction_type=f.floor_construction, + insulation_type=f.floor_insulation_type, + ) + + +def _window(w: Window) -> WindowDetails: + return WindowDetails( + glazing_type=w.glazing_type, + orientation=w.orientation, + frame_type=w.frame_type, + glazing_gap=w.glazing_gap, + draught_proofed=w.draught_proofed, + height_m=w.height_m, + width_m=w.width_m, + ) + + +def _main_heating(survey: PasHubRdSapSiteNotes) -> MainHeatingSystem: + mh = survey.heating_and_hot_water.main_heating + return MainHeatingSystem( + fuel=mh.fuel, + system_type=mh.system_type, + boiler_type=mh.type, + manufacturer=mh.manufacturer, + model=mh.model, + condensing=mh.condensing, + controls=mh.controls, + flue_gas_heat_recovery=mh.flue_gas_heat_recovery_system, + weather_compensator=mh.weather_compensator, + emitter=mh.emitter, + ) + + +def _hot_water(survey: PasHubRdSapSiteNotes) -> HotWaterSystem: + wh = survey.heating_and_hot_water.water_heating + return HotWaterSystem( + source=wh.system, + cylinder_size=wh.cylinder_size, + insulation_type=wh.insulation_type, + insulation_thickness_mm=wh.insulation_thickness_mm, + has_thermostat=wh.has_thermostat, + ) + + +def _secondary_heating(survey: PasHubRdSapSiteNotes) -> Optional[SecondaryHeatingSystem]: + fuel = survey.heating_and_hot_water.secondary_heating.secondary_fuel + if fuel == "No Secondary Heating": + return None + return SecondaryHeatingSystem(fuel=fuel) + + +def _ventilation(survey: PasHubRdSapSiteNotes) -> VentilationDetails: + v = survey.ventilation + return VentilationDetails( + ventilation_type=v.ventilation_type, + number_of_open_flues=v.number_of_open_flues, + number_of_closed_flues=v.number_of_closed_flues, + number_of_boiler_flues=v.number_of_boiler_flues, + number_of_other_flues=v.number_of_other_flues, + number_of_extract_fans=v.number_of_extract_fans, + number_of_passive_vents=v.number_of_passive_vents, + number_of_flueless_gas_fires=v.number_of_flueless_gas_fires, + has_fixed_air_conditioning=v.has_fixed_air_conditioning, + pressure_test=v.pressure_test, + draught_lobby=v.draught_lobby, + ) + + +def _renewables(survey: PasHubRdSapSiteNotes) -> RenewablesDetails: + r = survey.renewables + return RenewablesDetails( + has_photovoltaic=r.photovoltaic_array, + has_solar_hot_water=r.solar_hot_water, + has_wind_turbines=r.wind_turbines, + has_hydro=r.hydro, + number_of_pv_batteries=r.number_of_pv_batteries, + ) + + +def _lighting(survey: PasHubRdSapSiteNotes) -> LightingDetails: + rc = survey.room_count_elements + return LightingDetails( + number_of_led_bulbs=rc.number_of_fixed_led_bulbs, + number_of_cfl_bulbs=rc.number_of_fixed_cfl_bulbs, + number_of_incandescent_bulbs=rc.number_of_fixed_incandescent_bulbs, + ) diff --git a/datatypes/epc/domain/tests/test_from_site_notes.py b/datatypes/epc/domain/tests/test_from_site_notes.py index 3714dc22..43417dd9 100644 --- a/datatypes/epc/domain/tests/test_from_site_notes.py +++ b/datatypes/epc/domain/tests/test_from_site_notes.py @@ -29,7 +29,7 @@ class TestFromExample1: @pytest.fixture def dwelling(self) -> Dwelling: - return DwellingMapper.from_site_notes(survey("example1.json")) + return DwellingMapper.from_site_notes(survey("pashub_rdsap_site_notes_example1.json")) # --- property_details --- @@ -270,7 +270,7 @@ class TestFromExample2: @pytest.fixture def dwelling(self) -> Dwelling: - return DwellingMapper.from_site_notes(survey("example2.json")) + return DwellingMapper.from_site_notes(survey("pashub_rdsap_site_notes_example2.json")) # --- floor_dimensions: main building + extension floors flattened --- diff --git a/pytest.ini b/pytest.ini index c22c8296..55c2873a 100644 --- a/pytest.ini +++ b/pytest.ini @@ -3,6 +3,6 @@ pythonpath = . log_cli = true log_cli_level = INFO addopts = --cov-report term-missing --cov=etl/epc --cov=recommendations --cov=backend --cov=etl/epc_clean --cov=etl/spatial -testpaths = recommendations/tests backend/tests etl/epc/tests etl/epc_clean/tests etl/spatial/tests backend/condition/tests backend/address2UPRN/tests backend/onboarders/tests backend/categorisation/tests backend/export/tests etl/hubspot/tests backend/hubspot_trigger_orchestrator/tests datatypes/epc/schema/tests datatypes/epc/surveys/tests +testpaths = recommendations/tests backend/tests etl/epc/tests etl/epc_clean/tests etl/spatial/tests backend/condition/tests backend/address2UPRN/tests backend/onboarders/tests backend/categorisation/tests backend/export/tests etl/hubspot/tests backend/hubspot_trigger_orchestrator/tests datatypes/epc/schema/tests datatypes/epc/surveys/tests datatypes/epc/domain/tests markers = integration: mark a test as an integration test