mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
beginning to assembly the parity class
This commit is contained in:
parent
4d2275fd12
commit
f4f9fc5b19
16 changed files with 374 additions and 311 deletions
|
|
@ -1,7 +1,30 @@
|
|||
class OnboarderBase:
|
||||
import pandas as pd
|
||||
from utils.s3 import read_from_s3
|
||||
|
||||
def read(self):
|
||||
pass
|
||||
|
||||
class OnboarderBase:
|
||||
data: pd.DataFrame | None = None
|
||||
|
||||
def read_s3(self, bucket_name: str, file_name: str):
|
||||
self.data = read_from_s3(bucket_name=bucket_name, s3_file_name=file_name)
|
||||
|
||||
def write(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def assert_nulls_only_from_source_nulls(data: pd.DataFrame, original_column: str, mapped_column: str) -> bool:
|
||||
# We only allow nulls if the original value was null
|
||||
null_vals = data[pd.isnull(data[mapped_column])]
|
||||
if null_vals.empty:
|
||||
return True
|
||||
# We make sure all original values were null
|
||||
assert pd.isnull(null_vals[original_column]).all(), (
|
||||
f"Some values in {mapped_column} were not mapped, but original values were not null"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def assert_no_nulls(data: pd.DataFrame, column: str):
|
||||
assert pd.isnull(data[column]).sum() == 0, f"column {column} contains null values, but should not"
|
||||
|
||||
def map_construction_age_band(self):
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -1,57 +0,0 @@
|
|||
from enum import Enum
|
||||
|
||||
|
||||
class EpcFuel(Enum):
|
||||
electricity_not_community = "electricity (not community)"
|
||||
lpg_not_community = "LPG (not community)"
|
||||
mains_gas_not_community = "mains gas (not community)"
|
||||
oil_not_community = "oil (not community)"
|
||||
manufactured_smokeless_fuel = "Solid fuel: manufactured smokeless fuel"
|
||||
smokeless_coal = "smokeless coal"
|
||||
|
||||
|
||||
class EpcHeatingControls(Enum):
|
||||
programmer_room_thermostat_trvs = "Programmer, room thermostat and TRVs"
|
||||
programmers_trvs_bypass = "Programmer, TRVs and bypass"
|
||||
time_and_temperature_zone_control = "Time and temperature zone control"
|
||||
|
||||
# Room heaters
|
||||
programmer_and_appliance_thermostats = "Programmer and appliance thermostats"
|
||||
appliance_thermostats = "Appliance thermostats"
|
||||
|
||||
# Storage heaters
|
||||
automatic_charge_control = "Automatic charge control"
|
||||
manual_charge_control = "Manual charge control"
|
||||
|
||||
# Warm air
|
||||
programmer_and_atleast_two_room_thermostats = "Programmer and at least two room thermostats"
|
||||
|
||||
|
||||
class EpcHeatingSystems(Enum):
|
||||
# boiler and radiators
|
||||
boiler_and_radiators_electric = "Boiler and radiators, electric"
|
||||
boiler_and_radiators_lpg = "Boiler and radiators, LPG"
|
||||
boiler_radiators_mains_gas = "Boiler and radiators, mains gas"
|
||||
boiler_radiators_oil = "Boiler and radiators, oil"
|
||||
# underfloor
|
||||
electric_underfloor_heating = "Electric underfloor heating"
|
||||
# ashp
|
||||
air_to_air_ashp = "Air source heat pump, warm air, electric"
|
||||
ashp_radiators_electric = "Air source heat pump, radiators, electric"
|
||||
# Room heaters
|
||||
room_heaters_electric = "Room heaters, electric"
|
||||
room_heaters_mains_gas = "Room heaters, mains gas"
|
||||
room_heaters_smokeless_fuel = "Room heaters, smokeless fuel"
|
||||
room_heaters_coal = "Room heaters, coal"
|
||||
# Storage heaters
|
||||
electric_storage_heaters = "Electric storage heaters"
|
||||
# Warm air
|
||||
warm_air_electricaire = "Warm air, Electricaire"
|
||||
warm_air_mains_gas = "Warm air, mains gas"
|
||||
|
||||
|
||||
class EpcHotWaterSystems(Enum):
|
||||
# from primary heating system
|
||||
from_main_system = "From main system"
|
||||
# Common for heater-based systems, e.g. room heaters or storage heaters
|
||||
electric_immersion_off_peak = "Electric immersion, off-peak"
|
||||
|
|
@ -1,215 +1,11 @@
|
|||
import pandas as pd
|
||||
import re
|
||||
from collections.abc import Mapping
|
||||
from enum import Enum
|
||||
from typing import Callable, Union, List
|
||||
|
||||
|
||||
class EpcConstructionAgeBand(Enum):
|
||||
before_1900: str = 'England and Wales: before 1900'
|
||||
from_1900_to_1929: str = 'England and Wales: 1900-1929'
|
||||
from_1930_to_1949: str = 'England and Wales: 1930-1949'
|
||||
from_1950_to_1966: str = 'England and Wales: 1950-1966'
|
||||
from_1967_to_1975: str = 'England and Wales: 1967-1975'
|
||||
from_1976_to_1982: str = 'England and Wales: 1976-1982'
|
||||
from_1983_to_1990: str = 'England and Wales: 1983-1990'
|
||||
from_1991_to_1995: str = 'England and Wales: 1991-1995'
|
||||
from_1996_to_2002: str = 'England and Wales: 1996-2002'
|
||||
from_2003_to_2006: str = 'England and Wales: 2003-2006'
|
||||
from_2007_to_2011: str = 'England and Wales: 2007-2011'
|
||||
from_2012_onwards: str = 'England and Wales: 2012-onwards'
|
||||
from_2012_to_2022: str = 'England and Wales: 2012-2022'
|
||||
from_2023_onwards: str = 'England and Wales: 2023 onwards'
|
||||
|
||||
def start_year(self) -> int:
|
||||
"""
|
||||
Extract the starting year of the age band.
|
||||
"""
|
||||
value = self.value.lower()
|
||||
|
||||
if 'before' in value:
|
||||
return 0
|
||||
match = re.search(r'(\d{4})', value)
|
||||
if not match:
|
||||
raise ValueError(f"Cannot determine start year from '{self.value}'")
|
||||
|
||||
return int(match.group(1))
|
||||
|
||||
@classmethod
|
||||
def from_year_onwards(cls, year: int) -> List["EpcConstructionAgeBand"]:
|
||||
"""
|
||||
Return all age bands whose starting year is >= the given year.
|
||||
"""
|
||||
return [
|
||||
band
|
||||
for band in cls
|
||||
if band.start_year() >= year
|
||||
]
|
||||
|
||||
|
||||
class EpcWallDescriptions(Enum):
|
||||
# Cavity wall descriptions
|
||||
cavity_insulated_assumed: str = "Cavity wall, as built, insulated (assumed)"
|
||||
cavity_partial_insulated_assumed: str = "Cavity wall, as built, partial insulation (assumed)"
|
||||
cavity_no_insulation_assumed: str = "Cavity wall, as built, no insulation (assumed)"
|
||||
cavity_filled_cavity: str = "Cavity wall, filled cavity"
|
||||
cavity_internal_insulation: str = "Cavity wall, with internal insulation"
|
||||
cavity_external_insulation: str = "Cavity wall, with external insulation"
|
||||
cavity_filled_plus_internal: str = "Cavity wall, filled cavity and internal insulation"
|
||||
cavity_filled_plus_external: str = "Cavity wall, filled cavity and external insulation"
|
||||
|
||||
# Solid wall descriptions
|
||||
solid_brick_internal_insulation: str = "Solid brick, with internal insulation"
|
||||
solid_brick_external_insulation: str = "Solid brick, with external insulation"
|
||||
solid_brick_no_insulation_assumed: str = 'Solid brick, as built, no insulation (assumed)'
|
||||
solid_brick_partial_insulated_assumed: str = 'Solid brick, as built, partial insulation (assumed)'
|
||||
solid_brick_insulated_assumed: str = 'Solid brick, as built, insulated (assumed)'
|
||||
|
||||
# System
|
||||
system_external_insulation: str = "System built, with external insulation"
|
||||
system_internal_insulation: str = "System built, with internal insulation"
|
||||
system_no_insulation_assumed: str = "System built, as built, no insulation (assumed)"
|
||||
system_partial_insulated_assumed: str = "System built, as built, partial insulation (assumed)"
|
||||
system_insulated_assumed: str = "System built, as built, insulated (assumed)"
|
||||
|
||||
# Timber
|
||||
timber_frame_internal_insulation: str = "Timber frame, with internal insulation"
|
||||
timber_frame_external_insulation: str = "Timber frame, with external insulation"
|
||||
timber_frame_no_insulation_assumed: str = "Timber frame, as built, no insulation (assumed)"
|
||||
timber_frame_partial_insulated_assumed: str = "Timber frame, as built, partial insulation (assumed)"
|
||||
timber_frame_insulated_assumed: str = "Timber frame, as built, insulated (assumed)"
|
||||
|
||||
# Granite/whinstone
|
||||
granite_whinstone_external_insulation: str = "Granite or whin, with external insulation"
|
||||
granite_whinstone_internal_insulation: str = "Granite or whin, with internal insulation"
|
||||
granite_whinstone_no_insulation_assumed: str = "Granite or whin, as built, no insulation (assumed)"
|
||||
granite_whinstone_partial_insulated_assumed: str = "Granite or whin, as built, partial insulation (assumed)"
|
||||
granite_whinestone_insulated_assumed: str = "Granite or whin, as built, insulated (assumed)"
|
||||
|
||||
# Sandstone/limestone
|
||||
sandstone_limestone_internal_insulation: str = "Sandstone, with internal insulation"
|
||||
sandstone_limestone_external_insulation: str = "Sandstone, with external insulation"
|
||||
sandstone_limestone_no_insulation_assumed: str = "Sandstone, as built, no insulation (assumed)"
|
||||
sandstone_limestone_partial_insulated_assumed: str = "Sandstone, as built, partial insulation (assumed)"
|
||||
sandstone_limestone_insulated_assumed: str = "Sandstone, as built, insulated (assumed)"
|
||||
|
||||
# Cob
|
||||
cob_as_built_average = "Cob, as built"
|
||||
cob_as_built_good = "Cob, as built"
|
||||
|
||||
# unknown descriptions which may get mapped later or handled via fallback
|
||||
cavity_as_built_unknown = "Cavity wall, as built, unknown insulation"
|
||||
solid_brick_as_built_unknown = "Solid brick, as built, unknown insulation"
|
||||
system_as_built_unknown = "System built, as built, unknown insulation"
|
||||
timber_frame_as_built_unknown = "Timber frame, as built, unknown insulation"
|
||||
granite_as_built_unknown = "Granite or whin, as built, unknown insulation"
|
||||
sandstone_as_built_unknown = "Sandstone, as built, unknown insulation"
|
||||
cob_as_built_unknown = "Cob, as built, unknown insulation"
|
||||
|
||||
@property
|
||||
def unknown_descriptions(self) -> List["EpcWallDescriptions"]:
|
||||
return [
|
||||
EpcWallDescriptions.cavity_as_built_unknown,
|
||||
EpcWallDescriptions.solid_brick_as_built_unknown,
|
||||
EpcWallDescriptions.system_as_built_unknown,
|
||||
EpcWallDescriptions.timber_frame_as_built_unknown,
|
||||
EpcWallDescriptions.granite_as_built_unknown,
|
||||
EpcWallDescriptions.sandstone_as_built_unknown,
|
||||
EpcWallDescriptions.cob_as_built_unknown,
|
||||
]
|
||||
|
||||
|
||||
class EpcRoofDescriptions(Enum):
|
||||
# Loft
|
||||
# Assumed options
|
||||
pitched_insulated_assumed = "Pitched, insulated (assumed)"
|
||||
pitched_no_insulation = "Pitched, no insulation"
|
||||
# Insulation thickness options
|
||||
loft_12mm_insulation: str = "Pitched, 12 mm loft insulation"
|
||||
loft_25mm_insulation: str = "Pitched, 25 mm loft insulation"
|
||||
loft_50mm_insulation: str = "Pitched, 50 mm loft insulation"
|
||||
loft_75mm_insulation: str = "Pitched, 75 mm loft insulation"
|
||||
loft_100mm_insulation: str = "Pitched, 100 mm loft insulation"
|
||||
loft_125mm_insulation: str = "Pitched, 125 mm loft insulation"
|
||||
loft_150mm_insulation: str = "Pitched, 150 mm loft insulation"
|
||||
loft_175mm_insulation: str = "Pitched, 175 mm loft insulation"
|
||||
loft_200mm_insulation: str = "Pitched, 200 mm loft insulation"
|
||||
loft_250mm_insulation: str = "Pitched, 250 mm loft insulation"
|
||||
loft_270mm_insulation: str = "Pitched, 270 mm loft insulation"
|
||||
loft_300mm_insulation: str = "Pitched, 300 mm loft insulation"
|
||||
loft_350mm_insulation: str = "Pitched, 350 mm loft insulation"
|
||||
loft_400mm_plus_insulation: str = "Pitched, 400+ mm loft insulation"
|
||||
# Insulated at rafters "Pitched, insulated at rafters"
|
||||
# Rafters
|
||||
# 400mm, 350mm = very good
|
||||
# 200-300mm = good
|
||||
# 125-175 = average
|
||||
# 50-100 = poor
|
||||
# 25 and below= very poor
|
||||
loft_insulated_at_rafters: str = "Pitched, insulated at rafters"
|
||||
# another dwelling above
|
||||
another_dwelling_above: str = "(another dwelling above)"
|
||||
# flat roof, which if there is observed insulation is just "flat, insulated", however there is a
|
||||
# different efficiency rating depending on insulation thickness
|
||||
# categories:
|
||||
# 12mm = very poor & has limited insulation description
|
||||
# 25, 50 = poor & has limited insulation description
|
||||
# 75, 100, 125mm = average (Flat, insulated)
|
||||
# 150, 175, 200, 225, 250mm = good (Flat, insulated)
|
||||
# 270mm+ = very good (Flat, insulated)
|
||||
# As built 2023 = Flat, insulated, Very good
|
||||
# 2003 - 2006, up to 2012-2022 = Flat insulated, Good
|
||||
# 1983-1990, 1996-2002 = Flat, insulated, Average
|
||||
# 1976-1982 = Flat, limited insulation, poor
|
||||
# 1967 - 1975 = Flat, limited insulation, Very Poor
|
||||
# 1950-1966 and earlier bands = flat, no insulation, very poor
|
||||
|
||||
flat_insulated = "Flat, insulated"
|
||||
flat_limited_insulation = "Flat, limited insulation"
|
||||
flat_no_insulation = "Flat, no insulation"
|
||||
|
||||
# Thatched roof descriptions
|
||||
# With Loft insulation at joists
|
||||
# Thatched + 12mm = thatched, with additional insulation, average
|
||||
# Thatched + 25, 50, 100, 150mm = thatched, with additional insulation, good
|
||||
# Thatched + 175mm+ = thatched, with additional insulation, very good
|
||||
# With loft insulation at rafters [out of scope atm]
|
||||
# Unknown insulation
|
||||
# Pre 1900, 1930-1949, 1967-1975, 1983-1990, 1996-2002 = "Thatched", Average
|
||||
# 2003-2006, 2012-2022 = "Thatched", Good
|
||||
# 2023 onwards = "Thatched", Very Good
|
||||
thatched = "Thatched" # We see this for no insulation, has average performance
|
||||
thatched_with_additional_insulation: str = "Thatched, with additional insulation"
|
||||
|
||||
# Sloping ceiling
|
||||
# For sloping ceiling tags, we don't use any (assumed) tags so that it's unambiguous that the roof is sloped
|
||||
sloping_pitched_no_insulation: str = "Pitched, no insulation"
|
||||
sloping_pitched_limited_insulation: str = "Pitched, limited insulation"
|
||||
sloping_pitched_insulated: str = "Pitched, insulated"
|
||||
|
||||
# Unknown descriptions which may get mapped later or handled via fallback
|
||||
flat_as_built_unknown: str = "Flat, as built, unknown insulation"
|
||||
loft_as_built_unknown: str = "Loft, as built, unknown insulation"
|
||||
thatched_as_built_unknown: str = "Thatched, as built, unknown insulation"
|
||||
sloping_pitched_as_built_unknown: str = "Pitched, as built, unknown insulation"
|
||||
|
||||
@property
|
||||
def unknown_descriptions(self) -> List["EpcRoofDescriptions"]:
|
||||
return [
|
||||
EpcRoofDescriptions.flat_as_built_unknown,
|
||||
EpcRoofDescriptions.loft_as_built_unknown,
|
||||
EpcRoofDescriptions.thatched_as_built_unknown,
|
||||
EpcRoofDescriptions.sloping_pitched_as_built_unknown,
|
||||
]
|
||||
|
||||
|
||||
class EpcEfficiency(Enum):
|
||||
VERY_POOR = "Very Poor"
|
||||
POOR = "Poor"
|
||||
AVERAGE = "Average"
|
||||
GOOD = "Good"
|
||||
VERY_GOOD = "Very Good"
|
||||
NA = "N/A"
|
||||
from collections.abc import Mapping
|
||||
from typing import Callable, Union
|
||||
from datatypes.epc.construction_age_band import EpcConstructionAgeBand
|
||||
from datatypes.epc.efficiency import EpcEfficiency
|
||||
from datatypes.epc.walls import EpcWallDescriptions
|
||||
from datatypes.epc.roof import EpcRoofDescriptions
|
||||
|
||||
|
||||
def cavity_filled_efficiency(age_band: EpcConstructionAgeBand) -> EpcEfficiency:
|
||||
|
|
|
|||
|
|
@ -1,15 +1,10 @@
|
|||
parity_map = {
|
||||
"MidTerrace": "Mid-Terrace",
|
||||
"EndTerrace": "End-Terrace",
|
||||
"Detached": "Detached",
|
||||
"SemiDetached": "Semi-Detached",
|
||||
"EnclosedMidTerrace": "Enclosed Mid-Terrace",
|
||||
"EnclosedEndTerrace": "Enclosed End-Terrace",
|
||||
}
|
||||
from datatypes.epc.property_type_built_form import BuiltForm
|
||||
|
||||
# MidTerrace 41462
|
||||
# EndTerrace 20910
|
||||
# Detached 16875
|
||||
# SemiDetached 14725
|
||||
# EnclosedMidTerrace 3176
|
||||
# EnclosedEndTerrace 2393
|
||||
parity_map = {
|
||||
"MidTerrace": BuiltForm.mid_terrace,
|
||||
"EndTerrace": BuiltForm.end_terrace,
|
||||
"Detached": BuiltForm.detached,
|
||||
"SemiDetached": BuiltForm.semi_detached,
|
||||
"EnclosedMidTerrace": BuiltForm.enclosed_mid_terrace,
|
||||
"EnclosedEndTerrace": BuiltForm.enclosed_end_terrace,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
from datatypes.epc.property_type_built_form import PropertyType
|
||||
|
||||
parity_map = {
|
||||
"Flat": "Flat",
|
||||
"Maisonette": "Maisonette",
|
||||
"Bungalow": "Bungalow",
|
||||
"House": "House",
|
||||
"Flat": PropertyType.flat,
|
||||
"Maisonette": PropertyType.maisonette,
|
||||
"Bungalow": PropertyType.bungalow,
|
||||
"House": PropertyType.house,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,16 @@ import re
|
|||
from numpy import nan
|
||||
from tqdm import tqdm
|
||||
import pandas as pd
|
||||
from backend.onboarders.base import OnboarderBase
|
||||
from backend.onboarders.mappings.property_type import parity_map as property_map
|
||||
from backend.onboarders.mappings.age_band import parity_map as age_band_map
|
||||
from backend.onboarders.mappings.built_form import parity_map as built_form_map
|
||||
from backend.onboarders.epc_descriptions import EpcWallDescriptions, EpcConstructionAgeBand, EpcEfficiency, \
|
||||
WALL_DESCRIPTION_EFFICIENCIES, EpcRoofDescriptions, resolve_roof_efficiency, EpcFloorDescriptions
|
||||
from backend.onboarders.epc.placeholder import EpcFuel, EpcHeatingControls, EpcHeatingSystems, EpcHotWaterSystems
|
||||
from datatypes.epc.fuel import EpcFuel
|
||||
from datatypes.epc.heating_controls import EpcHeatingControls
|
||||
from datatypes.epc.main_heating import EpcHeatingSystems
|
||||
from datatypes.epc.hotwater import EpcHotWaterSystems
|
||||
from backend.onboarders.mappings.as_built_wall_classifiers import AS_BUILT_WALL_CLASSIFIERS
|
||||
from backend.onboarders.mappings.as_built_roof_classifiers import AS_BUILT_ROOF_CLASSIFIERS
|
||||
from backend.onboarders.mappings.as_built_floor_classifiers import unknown_floor_as_built, unknown_floor_retrofitted, \
|
||||
|
|
@ -15,18 +19,6 @@ from backend.onboarders.mappings.as_built_floor_classifiers import unknown_floor
|
|||
|
||||
tqdm.pandas()
|
||||
|
||||
|
||||
def check_nulls(data, original_column, mapped_column):
|
||||
# We only allow nulls if the oroginal value was null
|
||||
null_vals = data[pd.isnull(data[mapped_column])]
|
||||
if null_vals.empty:
|
||||
return True
|
||||
# We make sure all original values were null
|
||||
assert pd.isnull(null_vals[original_column]).all(), (
|
||||
f"Some values in {mapped_column} were not mapped, but original values were not null"
|
||||
)
|
||||
|
||||
|
||||
# Sample input data
|
||||
data = pd.read_excel(
|
||||
"/Users/khalimconn-kowlessar/Documents/hestia/Customers/Peabody/Nov 2025 Consulting Project/2025_11_11 - Peabody "
|
||||
|
|
@ -34,21 +26,41 @@ data = pd.read_excel(
|
|||
sheet_name="Sustainability"
|
||||
)
|
||||
|
||||
|
||||
class ParityOnboarder(OnboarderBase):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
fileuri: str,
|
||||
):
|
||||
# Extract bucket, and filekey; Will be in the format s3://bucket/key
|
||||
bucket_name = fileuri.split("/")[2]
|
||||
file_name = "/".join(fileuri.split("/")[3:])
|
||||
|
||||
self.read_s3(bucket_name=bucket_name, file_name=file_name)
|
||||
pass
|
||||
|
||||
def map_construction_age_band(self):
|
||||
data["construction_age_band"] = data["Construction Years"].map(age_band_map)
|
||||
self.assert_nulls_only_from_source_nulls(data, "Construction Years", "construction_age_band")
|
||||
|
||||
def map_property_type(self):
|
||||
data["property_type"] = data["Type"].map(property_map)
|
||||
self.assert_no_nulls(data, "property_type")
|
||||
|
||||
def process(self):
|
||||
# ------------ construction_age_band ------------
|
||||
self.map_construction_age_band()
|
||||
|
||||
# ------------ property_type ------------
|
||||
self.map_property_type()
|
||||
|
||||
|
||||
# We want to map the parity fields to standard EPC references. This will allow us to
|
||||
# 1) Estimate EPCs, more accurately
|
||||
# 2) Patch incorrect EPCs with ease
|
||||
# 3) Indicate already installed measures
|
||||
|
||||
# ------------ construction_age_band ------------
|
||||
|
||||
data["construction_age_band"] = data["Construction Years"].map(age_band_map)
|
||||
|
||||
check_nulls(data, "Construction Years", "construction_age_band")
|
||||
|
||||
# ------------ property_type ------------
|
||||
data["property_type"] = data["Type"].map(property_map)
|
||||
|
||||
assert pd.isnull(data["property_type"]).sum() == 0, "Some property types were not mapped"
|
||||
|
||||
# ------------ built_form ------------
|
||||
data["built_form"] = data["Attachment"].map(built_form_map)
|
||||
|
|
|
|||
0
datatypes/epc/__init__.py
Normal file
0
datatypes/epc/__init__.py
Normal file
45
datatypes/epc/construction_age_band.py
Normal file
45
datatypes/epc/construction_age_band.py
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import re
|
||||
from enum import Enum
|
||||
from typing import List
|
||||
|
||||
|
||||
class EpcConstructionAgeBand(Enum):
|
||||
before_1900: str = 'England and Wales: before 1900'
|
||||
from_1900_to_1929: str = 'England and Wales: 1900-1929'
|
||||
from_1930_to_1949: str = 'England and Wales: 1930-1949'
|
||||
from_1950_to_1966: str = 'England and Wales: 1950-1966'
|
||||
from_1967_to_1975: str = 'England and Wales: 1967-1975'
|
||||
from_1976_to_1982: str = 'England and Wales: 1976-1982'
|
||||
from_1983_to_1990: str = 'England and Wales: 1983-1990'
|
||||
from_1991_to_1995: str = 'England and Wales: 1991-1995'
|
||||
from_1996_to_2002: str = 'England and Wales: 1996-2002'
|
||||
from_2003_to_2006: str = 'England and Wales: 2003-2006'
|
||||
from_2007_to_2011: str = 'England and Wales: 2007-2011'
|
||||
from_2012_onwards: str = 'England and Wales: 2012-onwards'
|
||||
from_2012_to_2022: str = 'England and Wales: 2012-2022'
|
||||
from_2023_onwards: str = 'England and Wales: 2023 onwards'
|
||||
|
||||
def start_year(self) -> int:
|
||||
"""
|
||||
Extract the starting year of the age band.
|
||||
"""
|
||||
value = self.value.lower()
|
||||
|
||||
if 'before' in value:
|
||||
return 0
|
||||
match = re.search(r'(\d{4})', value)
|
||||
if not match:
|
||||
raise ValueError(f"Cannot determine start year from '{self.value}'")
|
||||
|
||||
return int(match.group(1))
|
||||
|
||||
@classmethod
|
||||
def from_year_onwards(cls, year: int) -> List["EpcConstructionAgeBand"]:
|
||||
"""
|
||||
Return all age bands whose starting year is >= the given year.
|
||||
"""
|
||||
return [
|
||||
band
|
||||
for band in cls
|
||||
if band.start_year() >= year
|
||||
]
|
||||
10
datatypes/epc/efficiency.py
Normal file
10
datatypes/epc/efficiency.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
from enum import Enum
|
||||
|
||||
|
||||
class EpcEfficiency(Enum):
|
||||
VERY_POOR: str = "Very Poor"
|
||||
POOR: str = "Poor"
|
||||
AVERAGE: str = "Average"
|
||||
GOOD: str = "Good"
|
||||
VERY_GOOD: str = "Very Good"
|
||||
NA: str = "N/A"
|
||||
10
datatypes/epc/fuel.py
Normal file
10
datatypes/epc/fuel.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
from enum import Enum
|
||||
|
||||
|
||||
class EpcFuel(Enum):
|
||||
electricity_not_community = "electricity (not community)"
|
||||
lpg_not_community = "LPG (not community)"
|
||||
mains_gas_not_community = "mains gas (not community)"
|
||||
oil_not_community = "oil (not community)"
|
||||
manufactured_smokeless_fuel = "Solid fuel: manufactured smokeless fuel"
|
||||
smokeless_coal = "smokeless coal"
|
||||
18
datatypes/epc/heating_controls.py
Normal file
18
datatypes/epc/heating_controls.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
from enum import Enum
|
||||
|
||||
|
||||
class EpcHeatingControls(Enum):
|
||||
programmer_room_thermostat_trvs = "Programmer, room thermostat and TRVs"
|
||||
programmers_trvs_bypass = "Programmer, TRVs and bypass"
|
||||
time_and_temperature_zone_control = "Time and temperature zone control"
|
||||
|
||||
# Room heaters
|
||||
programmer_and_appliance_thermostats = "Programmer and appliance thermostats"
|
||||
appliance_thermostats = "Appliance thermostats"
|
||||
|
||||
# Storage heaters
|
||||
automatic_charge_control = "Automatic charge control"
|
||||
manual_charge_control = "Manual charge control"
|
||||
|
||||
# Warm air
|
||||
programmer_and_atleast_two_room_thermostats = "Programmer and at least two room thermostats"
|
||||
8
datatypes/epc/hotwater.py
Normal file
8
datatypes/epc/hotwater.py
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
from enum import Enum
|
||||
|
||||
|
||||
class EpcHotWaterSystems(Enum):
|
||||
# from primary heating system
|
||||
from_main_system = "From main system"
|
||||
# Common for heater-based systems, e.g. room heaters or storage heaters
|
||||
electric_immersion_off_peak = "Electric immersion, off-peak"
|
||||
24
datatypes/epc/main_heating.py
Normal file
24
datatypes/epc/main_heating.py
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
from enum import Enum
|
||||
|
||||
|
||||
class EpcHeatingSystems(Enum):
|
||||
# boiler and radiators
|
||||
boiler_and_radiators_electric = "Boiler and radiators, electric"
|
||||
boiler_and_radiators_lpg = "Boiler and radiators, LPG"
|
||||
boiler_radiators_mains_gas = "Boiler and radiators, mains gas"
|
||||
boiler_radiators_oil = "Boiler and radiators, oil"
|
||||
# underfloor
|
||||
electric_underfloor_heating = "Electric underfloor heating"
|
||||
# ashp
|
||||
air_to_air_ashp = "Air source heat pump, warm air, electric"
|
||||
ashp_radiators_electric = "Air source heat pump, radiators, electric"
|
||||
# Room heaters
|
||||
room_heaters_electric = "Room heaters, electric"
|
||||
room_heaters_mains_gas = "Room heaters, mains gas"
|
||||
room_heaters_smokeless_fuel = "Room heaters, smokeless fuel"
|
||||
room_heaters_coal = "Room heaters, coal"
|
||||
# Storage heaters
|
||||
electric_storage_heaters = "Electric storage heaters"
|
||||
# Warm air
|
||||
warm_air_electricaire = "Warm air, Electricaire"
|
||||
warm_air_mains_gas = "Warm air, mains gas"
|
||||
17
datatypes/epc/property_type_built_form.py
Normal file
17
datatypes/epc/property_type_built_form.py
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
from enum import Enum
|
||||
|
||||
|
||||
class PropertyType(Enum):
|
||||
flat = "Flat"
|
||||
maisonette = "Maisonette"
|
||||
bungalow = "Bungalow"
|
||||
house = "House"
|
||||
|
||||
|
||||
class BuiltForm(Enum):
|
||||
mid_terrace = "Mid-Terrace"
|
||||
end_terrace = "End-Terrace"
|
||||
detached = "Detached"
|
||||
semi_detached = "Semi-Detached"
|
||||
enclosed_mid_terrace = "Enclosed Mid-Terrace"
|
||||
enclosed_end_terrace = "Enclosed End-Terrace"
|
||||
86
datatypes/epc/roof.py
Normal file
86
datatypes/epc/roof.py
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
from enum import Enum
|
||||
from typing import List
|
||||
|
||||
|
||||
class EpcRoofDescriptions(Enum):
|
||||
# Loft
|
||||
# Assumed options
|
||||
pitched_insulated_assumed: str = "Pitched, insulated (assumed)"
|
||||
pitched_no_insulation: str = "Pitched, no insulation"
|
||||
# Insulation thickness options
|
||||
loft_12mm_insulation: str = "Pitched, 12 mm loft insulation"
|
||||
loft_25mm_insulation: str = "Pitched, 25 mm loft insulation"
|
||||
loft_50mm_insulation: str = "Pitched, 50 mm loft insulation"
|
||||
loft_75mm_insulation: str = "Pitched, 75 mm loft insulation"
|
||||
loft_100mm_insulation: str = "Pitched, 100 mm loft insulation"
|
||||
loft_125mm_insulation: str = "Pitched, 125 mm loft insulation"
|
||||
loft_150mm_insulation: str = "Pitched, 150 mm loft insulation"
|
||||
loft_175mm_insulation: str = "Pitched, 175 mm loft insulation"
|
||||
loft_200mm_insulation: str = "Pitched, 200 mm loft insulation"
|
||||
loft_250mm_insulation: str = "Pitched, 250 mm loft insulation"
|
||||
loft_270mm_insulation: str = "Pitched, 270 mm loft insulation"
|
||||
loft_300mm_insulation: str = "Pitched, 300 mm loft insulation"
|
||||
loft_350mm_insulation: str = "Pitched, 350 mm loft insulation"
|
||||
loft_400mm_plus_insulation: str = "Pitched, 400+ mm loft insulation"
|
||||
# Insulated at rafters "Pitched, insulated at rafters"
|
||||
# Rafters
|
||||
# 400mm, 350mm = very good
|
||||
# 200-300mm = good
|
||||
# 125-175 = average
|
||||
# 50-100 = poor
|
||||
# 25 and below= very poor
|
||||
loft_insulated_at_rafters: str = "Pitched, insulated at rafters"
|
||||
# another dwelling above
|
||||
another_dwelling_above: str = "(another dwelling above)"
|
||||
# flat roof, which if there is observed insulation is just "flat, insulated", however there is a
|
||||
# different efficiency rating depending on insulation thickness
|
||||
# categories:
|
||||
# 12mm = very poor & has limited insulation description
|
||||
# 25, 50 = poor & has limited insulation description
|
||||
# 75, 100, 125mm = average (Flat, insulated)
|
||||
# 150, 175, 200, 225, 250mm = good (Flat, insulated)
|
||||
# 270mm+ = very good (Flat, insulated)
|
||||
# As built 2023 = Flat, insulated, Very good
|
||||
# 2003 - 2006, up to 2012-2022 = Flat insulated, Good
|
||||
# 1983-1990, 1996-2002 = Flat, insulated, Average
|
||||
# 1976-1982 = Flat, limited insulation, poor
|
||||
# 1967 - 1975 = Flat, limited insulation, Very Poor
|
||||
# 1950-1966 and earlier bands = flat, no insulation, very poor
|
||||
|
||||
flat_insulated: str = "Flat, insulated"
|
||||
flat_limited_insulation: str = "Flat, limited insulation"
|
||||
flat_no_insulation: str = "Flat, no insulation"
|
||||
|
||||
# Thatched roof descriptions
|
||||
# With Loft insulation at joists
|
||||
# Thatched + 12mm = thatched, with additional insulation, average
|
||||
# Thatched + 25, 50, 100, 150mm = thatched, with additional insulation, good
|
||||
# Thatched + 175mm+ = thatched, with additional insulation, very good
|
||||
# With loft insulation at rafters [out of scope atm]
|
||||
# Unknown insulation
|
||||
# Pre 1900, 1930-1949, 1967-1975, 1983-1990, 1996-2002 = "Thatched", Average
|
||||
# 2003-2006, 2012-2022 = "Thatched", Good
|
||||
# 2023 onwards = "Thatched", Very Good
|
||||
thatched: str = "Thatched" # We see this for no insulation, has average performance
|
||||
thatched_with_additional_insulation: str = "Thatched, with additional insulation"
|
||||
|
||||
# Sloping ceiling
|
||||
# For sloping ceiling tags, we don't use any (assumed) tags so that it's unambiguous that the roof is sloped
|
||||
sloping_pitched_no_insulation: str = "Pitched, no insulation"
|
||||
sloping_pitched_limited_insulation: str = "Pitched, limited insulation"
|
||||
sloping_pitched_insulated: str = "Pitched, insulated"
|
||||
|
||||
# Unknown descriptions which may get mapped later or handled via fallback
|
||||
flat_as_built_unknown: str = "Flat, as built, unknown insulation"
|
||||
loft_as_built_unknown: str = "Loft, as built, unknown insulation"
|
||||
thatched_as_built_unknown: str = "Thatched, as built, unknown insulation"
|
||||
sloping_pitched_as_built_unknown: str = "Pitched, as built, unknown insulation"
|
||||
|
||||
@property
|
||||
def unknown_descriptions(self) -> List["EpcRoofDescriptions"]:
|
||||
return [
|
||||
EpcRoofDescriptions.flat_as_built_unknown,
|
||||
EpcRoofDescriptions.loft_as_built_unknown,
|
||||
EpcRoofDescriptions.thatched_as_built_unknown,
|
||||
EpcRoofDescriptions.sloping_pitched_as_built_unknown,
|
||||
]
|
||||
74
datatypes/epc/walls.py
Normal file
74
datatypes/epc/walls.py
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
from enum import Enum
|
||||
from typing import List
|
||||
|
||||
|
||||
class EpcWallDescriptions(Enum):
|
||||
# Cavity wall descriptions
|
||||
cavity_insulated_assumed: str = "Cavity wall, as built, insulated (assumed)"
|
||||
cavity_partial_insulated_assumed: str = "Cavity wall, as built, partial insulation (assumed)"
|
||||
cavity_no_insulation_assumed: str = "Cavity wall, as built, no insulation (assumed)"
|
||||
cavity_filled_cavity: str = "Cavity wall, filled cavity"
|
||||
cavity_internal_insulation: str = "Cavity wall, with internal insulation"
|
||||
cavity_external_insulation: str = "Cavity wall, with external insulation"
|
||||
cavity_filled_plus_internal: str = "Cavity wall, filled cavity and internal insulation"
|
||||
cavity_filled_plus_external: str = "Cavity wall, filled cavity and external insulation"
|
||||
|
||||
# Solid wall descriptions
|
||||
solid_brick_internal_insulation: str = "Solid brick, with internal insulation"
|
||||
solid_brick_external_insulation: str = "Solid brick, with external insulation"
|
||||
solid_brick_no_insulation_assumed: str = 'Solid brick, as built, no insulation (assumed)'
|
||||
solid_brick_partial_insulated_assumed: str = 'Solid brick, as built, partial insulation (assumed)'
|
||||
solid_brick_insulated_assumed: str = 'Solid brick, as built, insulated (assumed)'
|
||||
|
||||
# System
|
||||
system_external_insulation: str = "System built, with external insulation"
|
||||
system_internal_insulation: str = "System built, with internal insulation"
|
||||
system_no_insulation_assumed: str = "System built, as built, no insulation (assumed)"
|
||||
system_partial_insulated_assumed: str = "System built, as built, partial insulation (assumed)"
|
||||
system_insulated_assumed: str = "System built, as built, insulated (assumed)"
|
||||
|
||||
# Timber
|
||||
timber_frame_internal_insulation: str = "Timber frame, with internal insulation"
|
||||
timber_frame_external_insulation: str = "Timber frame, with external insulation"
|
||||
timber_frame_no_insulation_assumed: str = "Timber frame, as built, no insulation (assumed)"
|
||||
timber_frame_partial_insulated_assumed: str = "Timber frame, as built, partial insulation (assumed)"
|
||||
timber_frame_insulated_assumed: str = "Timber frame, as built, insulated (assumed)"
|
||||
|
||||
# Granite/whinstone
|
||||
granite_whinstone_external_insulation: str = "Granite or whin, with external insulation"
|
||||
granite_whinstone_internal_insulation: str = "Granite or whin, with internal insulation"
|
||||
granite_whinstone_no_insulation_assumed: str = "Granite or whin, as built, no insulation (assumed)"
|
||||
granite_whinstone_partial_insulated_assumed: str = "Granite or whin, as built, partial insulation (assumed)"
|
||||
granite_whinestone_insulated_assumed: str = "Granite or whin, as built, insulated (assumed)"
|
||||
|
||||
# Sandstone/limestone
|
||||
sandstone_limestone_internal_insulation: str = "Sandstone, with internal insulation"
|
||||
sandstone_limestone_external_insulation: str = "Sandstone, with external insulation"
|
||||
sandstone_limestone_no_insulation_assumed: str = "Sandstone, as built, no insulation (assumed)"
|
||||
sandstone_limestone_partial_insulated_assumed: str = "Sandstone, as built, partial insulation (assumed)"
|
||||
sandstone_limestone_insulated_assumed: str = "Sandstone, as built, insulated (assumed)"
|
||||
|
||||
# Cob
|
||||
cob_as_built_average: str = "Cob, as built"
|
||||
cob_as_built_good: str = "Cob, as built"
|
||||
|
||||
# unknown descriptions which may get mapped later or handled via fallback
|
||||
cavity_as_built_unknown: str = "Cavity wall, as built, unknown insulation"
|
||||
solid_brick_as_built_unknown: str = "Solid brick, as built, unknown insulation"
|
||||
system_as_built_unknown: str = "System built, as built, unknown insulation"
|
||||
timber_frame_as_built_unknown: str = "Timber frame, as built, unknown insulation"
|
||||
granite_as_built_unknown: str = "Granite or whin, as built, unknown insulation"
|
||||
sandstone_as_built_unknown: str = "Sandstone, as built, unknown insulation"
|
||||
cob_as_built_unknown: str = "Cob, as built, unknown insulation"
|
||||
|
||||
@property
|
||||
def unknown_descriptions(self) -> List["EpcWallDescriptions"]:
|
||||
return [
|
||||
EpcWallDescriptions.cavity_as_built_unknown,
|
||||
EpcWallDescriptions.solid_brick_as_built_unknown,
|
||||
EpcWallDescriptions.system_as_built_unknown,
|
||||
EpcWallDescriptions.timber_frame_as_built_unknown,
|
||||
EpcWallDescriptions.granite_as_built_unknown,
|
||||
EpcWallDescriptions.sandstone_as_built_unknown,
|
||||
EpcWallDescriptions.cob_as_built_unknown,
|
||||
]
|
||||
Loading…
Add table
Reference in a new issue