beginning to assembly the parity class

This commit is contained in:
Khalim Conn-Kowlessar 2026-02-04 18:34:59 +00:00
parent 4d2275fd12
commit f4f9fc5b19
16 changed files with 374 additions and 311 deletions

View file

@ -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

View file

@ -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"

View file

@ -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:

View file

@ -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,
}

View file

@ -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,
}

View file

@ -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)

View file

View 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
]

View 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
View 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"

View 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"

View 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"

View 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"

View 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
View 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
View 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,
]