Map Elmhurst site notes to EpcPropertyData 🟩

This commit is contained in:
Daniel Roth 2026-04-24 13:52:02 +00:00
parent 15ae46ec92
commit 1105491141

View file

@ -51,6 +51,11 @@ from datatypes.epc.schema.rdsap_schema_21_0_1 import (
RdSapSchema21_0_1,
EnergyElement as EnergyElement_21_0_1,
)
from datatypes.epc.surveys.elmhurst_site_notes import (
ElmhurstSiteNotes,
VentilationAndCooling as ElmhurstVentilation,
Window as ElmhurstWindow,
)
from datatypes.epc.surveys.pashub_rdsap_site_notes import (
BuildingConstruction,
BuildingMeasurements,
@ -200,6 +205,67 @@ class EpcPropertyDataMapper:
sap_ventilation=_map_sap_ventilation(ventilation),
)
@staticmethod
def from_elmhurst_site_notes(survey: ElmhurstSiteNotes) -> EpcPropertyData:
pd = survey.property_details
built_form = _strip_code(survey.attachment)
property_type = _strip_code(survey.property_type)
prefix = pd.house_number or pd.house_name or ""
address_line_1 = f"{prefix}, {pd.street}" if prefix else pd.street
return EpcPropertyData(
dwelling_type=f"{built_form} {property_type.lower()}",
inspection_date=pd.inspection_date,
tenure=pd.tenure,
transaction_type=pd.transaction_type,
address_line_1=address_line_1,
post_town=pd.town,
postcode=pd.postcode,
report_reference=pd.reference_number,
roofs=[],
walls=[],
floors=[],
main_heating=[],
door_count=survey.door_count,
sap_heating=_map_elmhurst_sap_heating(survey),
sap_windows=[_map_elmhurst_window(w) for w in survey.windows],
sap_energy_source=SapEnergySource(
mains_gas=survey.meters.main_gas,
meter_type=survey.meters.electricity_meter_type,
pv_battery_count=0,
wind_turbines_count=1 if survey.renewables.wind_turbine_present else 0,
gas_smart_meter_present=survey.meters.gas_smart_meter,
is_dwelling_export_capable=survey.renewables.export_capable_meter,
wind_turbines_terrain_type=survey.renewables.wind_turbines_terrain_type,
electricity_smart_meter_present=survey.meters.electricity_smart_meter,
),
sap_building_parts=[_map_elmhurst_building_part(survey)],
solar_water_heating=survey.renewables.solar_water_heating,
has_hot_water_cylinder=survey.water_heating.hot_water_cylinder_present,
has_fixed_air_conditioning=survey.ventilation.fixed_space_cooling,
wet_rooms_count=0,
extensions_count=0,
heated_rooms_count=survey.heated_habitable_rooms,
open_chimneys_count=survey.ventilation.open_chimneys_count,
habitable_rooms_count=survey.habitable_rooms,
insulated_door_count=survey.insulated_door_count,
cfl_fixed_lighting_bulbs_count=survey.lighting.cfl_count,
led_fixed_lighting_bulbs_count=survey.lighting.led_count,
incandescent_fixed_lighting_bulbs_count=survey.lighting.incandescent_count,
total_floor_area_m2=round(
sum(f.area_m2 for f in survey.dimensions.floors), 2
),
built_form=built_form,
property_type=property_type,
has_conservatory=survey.has_conservatory,
blocked_chimneys_count=survey.ventilation.blocked_chimneys_count,
number_of_storeys=survey.number_of_storeys,
hydro=survey.renewables.hydro_electricity_generated_kwh > 0,
photovoltaic_array=survey.renewables.photovoltaic_panel != "None",
sap_ventilation=_map_elmhurst_ventilation(survey.ventilation),
)
@staticmethod
def from_rdsap_schema_17_0(schema: RdSapSchema17_0) -> EpcPropertyData:
es = schema.sap_energy_source
@ -1453,6 +1519,12 @@ class EpcPropertyDataMapper:
# ---------------------------------------------------------------------------
def _strip_code(value: str) -> str:
"""Strip leading uppercase code from Elmhurst coded strings, e.g. 'CA Cavity''Cavity'."""
parts = value.split(" ", 1)
return parts[1] if len(parts) > 1 else value
def _extract_age_band(age_range: str) -> str:
"""Return the letter code from a site-notes age range, e.g. 'I: 1996 - 2002''I'."""
return age_range.split(":")[0].strip()
@ -1617,3 +1689,101 @@ def _map_sap_ventilation(ventilation: Ventilation) -> SapVentilation:
flueless_gas_fires_count=ventilation.number_of_flueless_gas_fires,
ventilation_in_pcdf_database=ventilation.ventilation_in_pcdf_database,
)
def _map_elmhurst_building_part(survey: ElmhurstSiteNotes) -> SapBuildingPart:
dims = survey.dimensions
floor_dims = [
SapFloorDimension(
room_height_m=f.room_height_m,
total_floor_area_m2=f.area_m2,
party_wall_length_m=f.party_wall_length_m,
heat_loss_perimeter_m=f.heat_loss_perimeter_m,
floor=i,
)
for i, f in enumerate(dims.floors)
]
return SapBuildingPart(
identifier="main",
construction_age_band=_strip_code(survey.construction_age_band),
wall_construction=_strip_code(survey.walls.wall_type),
wall_insulation_type=_strip_code(survey.walls.insulation),
wall_thickness_measured=not survey.walls.thickness_unknown,
party_wall_construction=_strip_code(survey.walls.party_wall_type),
sap_floor_dimensions=floor_dims,
wall_thickness_mm=survey.walls.thickness_mm,
roof_insulation_location=_strip_code(survey.roof.insulation),
roof_insulation_thickness=survey.roof.insulation_thickness_mm,
floor_type=_strip_code(survey.floor.location),
floor_construction_type=_strip_code(survey.floor.floor_type),
floor_insulation_type_str=_strip_code(survey.floor.insulation),
floor_u_value_known=survey.floor.u_value_known,
)
def _map_elmhurst_window(w: ElmhurstWindow) -> SapWindow:
return SapWindow(
pvc_frame=w.frame_type or "",
glazing_gap=w.glazing_gap or "",
orientation=w.orientation,
window_type="Window",
glazing_type=w.glazing_type,
window_width=w.width_m,
window_height=w.height_m,
draught_proofed=w.draught_proofed,
window_location=w.building_part,
window_wall_type=w.location,
permanent_shutters_present=w.permanent_shutters,
)
def _map_elmhurst_sap_heating(survey: ElmhurstSiteNotes) -> SapHeating:
mh = survey.main_heating
sap_control = mh.heating_controls_sap
control = (
sap_control.split(", ", 1)[1]
if sap_control.startswith("SAP code") and ", " in sap_control
else sap_control
)
shower_outlets = (
ShowerOutlets(
shower_outlet=ShowerOutlet(
shower_outlet_type=survey.baths_and_showers.showers[0].outlet_type
)
)
if survey.baths_and_showers.showers
else None
)
return SapHeating(
instantaneous_wwhrs=InstantaneousWwhrs(),
main_heating_details=[
MainHeatingDetail(
has_fghrs=survey.renewables.flue_gas_heat_recovery_present,
main_fuel_type=mh.fuel_type,
heat_emitter_type=mh.heat_emitter,
emitter_temperature=mh.design_flow_temperature,
fan_flue_present=mh.fan_assisted_flue,
main_heating_control=control,
central_heating_pump_age_str=mh.heat_pump_age,
)
],
has_fixed_air_conditioning=survey.ventilation.fixed_space_cooling,
shower_outlets=shower_outlets,
cylinder_size=None if not survey.water_heating.hot_water_cylinder_present else survey.water_heating.water_heating_code,
)
def _map_elmhurst_ventilation(v: ElmhurstVentilation) -> SapVentilation:
return SapVentilation(
ventilation_type=None,
draught_lobby=v.draught_lobby != "Not present",
pressure_test=v.pressure_test_method,
open_flues_count=v.open_flues_count,
closed_flues_count=v.open_chimneys_closed_fire_count,
boiler_flues_count=v.solid_fuel_boiler_flues_count,
other_flues_count=v.other_heater_flues_count,
extract_fans_count=v.extract_fans_count,
passive_vents_count=v.passive_vents_count,
flueless_gas_fires_count=v.flueless_gas_fires_count,
ventilation_in_pcdf_database=None,
)