Model/domain/epc/epc_property_data.py
2026-05-11 15:37:51 +00:00

378 lines
14 KiB
Python

from dataclasses import dataclass
from datetime import date
from typing import List, Optional, Union
from domain.epc.epc import Epc
@dataclass
class EnergyElement:
description: str
energy_efficiency_rating: int
environmental_efficiency_rating: int
@dataclass
class InstantaneousWwhrs:
wwhrs_index_number1: Optional[int] = None
wwhrs_index_number2: Optional[int] = None
@dataclass
class MainHeatingDetail:
has_fghrs: bool
main_fuel_type: Union[int, str] # int from API, str from site notes
heat_emitter_type: Union[int, str] # int from API, str from site notes
emitter_temperature: Union[int, str]
main_heating_control: Union[int, str] # int from API, str from site notes
fan_flue_present: Optional[bool] = None
boiler_flue_type: Optional[int] = None # TODO: make enum?
boiler_ignition_type: Optional[int] = None # TODO: make enum?
central_heating_pump_age: Optional[int] = None
central_heating_pump_age_str: Optional[str] = None # str from site notes e.g. "Unknown", "Pre 2013"
main_heating_index_number: Optional[int] = None
sap_main_heating_code: Optional[int] = None # TODO: make enum?
main_heating_number: Optional[int] = None
main_heating_category: Optional[int] = None
main_heating_fraction: Optional[int] = None
main_heating_data_source: Optional[int] = None
condensing: Optional[bool] = None
weather_compensator: Optional[bool] = None
@dataclass
class ShowerOutlet:
shower_outlet_type: Union[int, str]
shower_wwhrs: Optional[int] = None
@dataclass
class ShowerOutlets:
# TODO: consolidate ShowerOutlet and ShowerOutlets
shower_outlet: ShowerOutlet
@dataclass
class SapHeating:
instantaneous_wwhrs: InstantaneousWwhrs
main_heating_details: List[MainHeatingDetail]
has_fixed_air_conditioning: bool
cylinder_size: Optional[Union[int, str]] = (
None # int code from API; str (e.g. "Normal (90-130 litres)") from site notes
)
water_heating_code: Optional[int] = None # TODO: make enum?
water_heating_fuel: Optional[int] = None # TODO: make enum?
immersion_heating_type: Optional[Union[int, str]] = None # TODO: make enum?
shower_outlets: Optional[ShowerOutlets] = None
cylinder_insulation_type: Optional[Union[int, str]] = None
cylinder_thermostat: Optional[str] = None
secondary_fuel_type: Optional[int] = None
secondary_heating_type: Optional[Union[int, str]] = None # int from API; str from site notes
cylinder_insulation_thickness_mm: Optional[int] = None
@dataclass
class SapVentilation:
ventilation_type: Optional[str] = None
draught_lobby: Optional[bool] = None
pressure_test: Optional[str] = None # str from site notes e.g. "No test"; int in API via mechanical_ventilation
open_flues_count: Optional[int] = None
closed_flues_count: Optional[int] = None
boiler_flues_count: Optional[int] = None
other_flues_count: Optional[int] = None
extract_fans_count: Optional[int] = None
passive_vents_count: Optional[int] = None
flueless_gas_fires_count: Optional[int] = None
ventilation_in_pcdf_database: Optional[bool] = None
@dataclass
class WindowTransmissionDetails:
u_value: float
data_source: Union[int, str]
solar_transmittance: float
@dataclass
class SapWindow:
frame_material: Optional[str]
glazing_gap: Union[int, str]
orientation: Union[int, str]
window_type: Union[int, str]
glazing_type: Union[int, str]
window_width: float
window_height: float
draught_proofed: Union[bool, str] # TODO: make enum/mapping?
window_location: Union[int, str] # TODO: make enum/mapping
window_wall_type: Union[int, str] # TODO: make enum/mapping
permanent_shutters_present: Union[bool, str] # TODO: make enum/mapping
frame_factor: Optional[float] = None
window_transmission_details: Optional[WindowTransmissionDetails] = None
permanent_shutters_insulated: Optional[str] = None
@dataclass
class PvBattery:
battery_capacity: float
@dataclass
class PvBatteries:
pv_battery: PvBattery
@dataclass
class WindTurbineDetails:
hub_height: float
rotor_diameter: float
@dataclass
class PhotovoltaicSupplyNoneOrNoDetails:
percent_roof_area: int
@dataclass
class PhotovoltaicSupply:
none_or_no_details: PhotovoltaicSupplyNoneOrNoDetails
@dataclass
class SapEnergySource:
mains_gas: bool
meter_type: str # int in API, str (e.g. "Single") in site notes
pv_battery_count: int
wind_turbines_count: int
gas_smart_meter_present: bool
is_dwelling_export_capable: bool
wind_turbines_terrain_type: str # int in API, str (e.g. "Suburban") in site notes
electricity_smart_meter_present: bool
pv_connection: Optional[Union[int, str]] = None # int from API; str from site notes
photovoltaic_supply: Optional[PhotovoltaicSupply] = None
wind_turbine_details: Optional[WindTurbineDetails] = None
pv_batteries: Optional[PvBatteries] = None
@dataclass
class SapFloorDimension:
room_height_m: float
total_floor_area_m2: float
party_wall_length_m: float
heat_loss_perimeter_m: float
floor: Optional[int] = None
floor_insulation: Optional[int] = None
floor_construction: Optional[int] = None
@dataclass
class SapRoomInRoof:
floor_area: Union[int, float]
construction_age_band: str
@dataclass
class SapAlternativeWall:
wall_area: float
wall_dry_lined: str
wall_construction: int
wall_insulation_type: int
wall_thickness_measured: str
wall_insulation_thickness: Optional[str] = None
@dataclass
class SapBuildingPart:
# General
identifier: str # e.g. "main", "roof"
construction_age_band: str
# Wall
wall_construction: Union[
int, str
] # int from API, str from site notes TODO: make enum/mapping?
wall_insulation_type: Union[
int, str
] # int from API, str from site notes TODO: make enum/mapping?
wall_thickness_measured: bool
party_wall_construction: Union[int, str] # TODO: make enum/mapping?
# Floor
sap_floor_dimensions: List[
SapFloorDimension
] # Not included in site notes; should this be optional?
# Optional
building_part_number: Optional[int] = (
None # Not sure how we get this from site notes
)
wall_dry_lined: Optional[bool] = None # Don't think we have this in site notes
wall_thickness_mm: Optional[int] = None
wall_insulation_thickness: Optional[str] = None
sap_alternative_wall_1: Optional[SapAlternativeWall] = None
sap_alternative_wall_2: Optional[SapAlternativeWall] = None
floor_heat_loss: Optional[int] = None
floor_insulation_thickness: Optional[str] = None
flat_roof_insulation_thickness: Optional[Union[str, int]] = (
None # TODO: make enum/mapping?
)
floor_type: Optional[str] = None # str from site notes e.g. "Ground Floor"
floor_construction_type: Optional[str] = None # str from site notes; distinct from floor_construction: int in SapFloorDimension
floor_insulation_type_str: Optional[str] = None # str from site notes e.g. "As Built"
floor_u_value_known: Optional[bool] = None
roof_construction: Optional[int] = None
roof_insulation_location: Optional[Union[int, str]] = (
None # TODO: make enum/mapping?
)
roof_insulation_thickness: Optional[Union[str, int]] = (
None # TODO: make enum/mapping?
)
sap_room_in_roof: Optional[SapRoomInRoof] = None
@dataclass
class WindowsTransmissionDetails:
u_value: float
data_source: int
solar_transmittance: float
@dataclass
class SapFlatDetails:
level: int
top_storey: str
flat_location: int
heat_loss_corridor: int
storey_count: Optional[int] = None
unheated_corridor_length_m: Optional[int] = None
@dataclass
class EpcPropertyData:
# General
dwelling_type: str # TODO: make enum?
inspection_date: date
tenure: str # str in site notes; stringified int (e.g. "1") from API
transaction_type: str # str in site notes; stringified int from API
address_line_1: str
postcode: str
post_town: str
# Elements
roofs: List[EnergyElement]
walls: List[EnergyElement]
floors: List[EnergyElement]
main_heating: List[EnergyElement]
door_count: int
sap_heating: SapHeating
sap_windows: List[SapWindow]
sap_energy_source: SapEnergySource
sap_building_parts: List[SapBuildingPart]
solar_water_heating: bool
has_hot_water_cylinder: bool # must be inferred when mapping from site notes
has_fixed_air_conditioning: bool
# Counts
wet_rooms_count: int # If this isn't provided, should it be 0 or None?
extensions_count: int # If this isn't provided, should it be 0 or None?
heated_rooms_count: int # If this isn't provided, should it be 0 or None?
open_chimneys_count: int
habitable_rooms_count: int
insulated_door_count: (
int # Called "number_of_insulated_external_doors" in site notes; same thing?
)
cfl_fixed_lighting_bulbs_count: int
led_fixed_lighting_bulbs_count: int
incandescent_fixed_lighting_bulbs_count: int
# Measurements
total_floor_area_m2: float
# Optional fields
assessment_type: Optional[str] = None # not available from site notes
sap_version: Optional[float] = None # not available from site notes
uprn: Optional[int] = None # not available from site notes
status: Optional[str] = None # not available from site notes
window: Optional[EnergyElement] = None # not available from site notes
lighting: Optional[EnergyElement] = None # not available from site notes
hot_water: Optional[EnergyElement] = None # not available from site notes
schema_type: Optional[str] = None
schema_versions_original: Optional[str] = None
report_type: Optional[str] = None # TODO: make enum?
report_reference: Optional[str] = None
uprn_source: Optional[str] = None
address_line_2: Optional[str] = None
region_code: Optional[str] = None # TODO: make enum?
country_code: Optional[str] = None
built_form: Optional[str] = None # TODO: make enum?
property_type: Optional[str] = None
pressure_test: Optional[int] = None
language_code: Optional[str] = None
completion_date: Optional[date] = None
registration_date: Optional[date] = None
measurement_type: Optional[int] = None # What is this?
conservatory_type: Optional[int] = (
None # What is this? site notes have "has_conservatory" flag
)
has_conservatory: Optional[bool] = None # mapped directly from site notes
has_heated_separate_conservatory: Optional[bool] = None
secondary_heating: Optional[EnergyElement] = (
None # For site notes, secondary_fuel maps to sap_heating.secondary_fuel_type
)
blocked_chimneys_count: Optional[int] = None
energy_rating_average: Optional[int] = None
main_heating_controls: Optional[EnergyElement] = (
None # site notes has heating_and_hot_water.main_heating.controls: str - doesn't map to EnergyElement
)
current_energy_efficiency_band: Optional[Epc] = None # not available in site notes?
environmental_impact_current: Optional[int] = None
heating_cost_current: Optional[float] = None
co2_emissions_current: Optional[float] = None
energy_consumption_current: Optional[int] = None
energy_rating_current: Optional[int] = None
lighting_cost_current: Optional[float] = None
hot_water_cost_current: Optional[float] = None
insulated_door_u_value: Optional[float] = None # Not available in site notes
mechanical_ventilation: Optional[int] = (
None # ventilation details present in site notes, but I'm not sure they correspond directly to the integers returned by the API here
)
percent_draughtproofed: Optional[int] = (
None # Site notes have draught_proofed: bool field for each window, can we use that to infer percentage?
)
heating_cost_potential: Optional[float] = None
co2_emissions_potential: Optional[float] = None
energy_consumption_potential: Optional[int] = None
energy_rating_potential: Optional[float] = None
lighting_cost_potential: Optional[float] = None
hot_water_cost_potential: Optional[float] = None
environmental_impact_potential: Optional[int] = None
potential_energy_efficiency_band: Optional[Epc] = (
None # not available in site notes
)
# renewable_heat_incentive: Optional[Any] = None # Not sure what this is, skip for now
draughtproofed_door_count: Optional[int] = None
mechanical_vent_duct_type: Optional[int] = None
windows_transmission_details: Optional[WindowsTransmissionDetails] = None
multiple_glazed_propertion: Optional[int] = None
calculation_software_version: Optional[str] = None # Do we care about this?
mechanical_vent_duct_placement: Optional[int] = None
mechanical_vent_duct_insulation: Optional[int] = None
pressure_test_certificate_number: Optional[int] = None
mechanical_ventilation_index_number: Optional[int] = None
mechanical_vent_measured_installation: Optional[str] = None
co2_emissions_current_per_floor_area: Optional[int] = None
low_energy_fixed_lighting_bulbs_count: Optional[int] = None
sap_flat_details: Optional[SapFlatDetails] = None
# survey_addendum: Optional[Any] = None # not sure how to handle, skip for now
fixed_lighting_outlets_count: Optional[int] = None
low_energy_fixed_lighting_outlets_count: Optional[int] = None
# Site-notes-only fields
sap_ventilation: Optional[SapVentilation] = None
number_of_storeys: Optional[int] = None
any_unheated_rooms: Optional[bool] = None
waste_water_heat_recovery: Optional[str] = None
hydro: Optional[bool] = None
photovoltaic_array: Optional[bool] = None