westward complete

This commit is contained in:
Khalim Conn-Kowlessar 2025-02-26 11:00:12 +00:00
parent 67f3e8ab70
commit ddfbf33494
5 changed files with 94 additions and 61 deletions

View file

@ -286,7 +286,7 @@ class AssetList:
# This SAP threshold is a key search criteria for properties that may be eligible for extraction # This SAP threshold is a key search criteria for properties that may be eligible for extraction
FILLED_CAVITY_SAP_THRESHOLD = 75 FILLED_CAVITY_SAP_THRESHOLD = 75
# This SAP the # This SAP the
EMPTY_CAVITY_SAP_THRESHOLD = 71 EMPTY_CAVITY_SAP_THRESHOLD = 75
# Any EPC deemed to have been conducted prior to this year is deemed to be unreliable # Any EPC deemed to have been conducted prior to this year is deemed to be unreliable
EPC_YEAR_THRESHOLD = pd.Timestamp.now().year - 5 EPC_YEAR_THRESHOLD = pd.Timestamp.now().year - 5
@ -956,13 +956,28 @@ class AssetList:
(~self.standardised_asset_list[self.STANDARD_PROPERTY_TYPE].isin(["bedsit"])) & (~self.standardised_asset_list[self.STANDARD_PROPERTY_TYPE].isin(["bedsit"])) &
(self.standardised_asset_list['non-intrusives: Construction'] == "CAVITY") & (self.standardised_asset_list['non-intrusives: Construction'] == "CAVITY") &
self.standardised_asset_list['non-intrusives: Insulated'].isin(["EMPTY", "PARTIAL"]) & self.standardised_asset_list['non-intrusives: Insulated'].isin(["EMPTY", "PARTIAL"]) &
(self.standardised_asset_list[self.STANDARD_YEAR_BUILT] <= 2000) & (self.standardised_asset_list[self.STANDARD_YEAR_BUILT] <= 2002) &
( (
self.standardised_asset_list[ self.standardised_asset_list[
self.EPC_API_DATA_NAMES["current-energy-efficiency"] self.EPC_API_DATA_NAMES["current-energy-efficiency"]
] <= self.EMPTY_CAVITY_SAP_THRESHOLD ] <= self.EMPTY_CAVITY_SAP_THRESHOLD
) )
) )
# Let's also flag work that looks eligible without the SAP filter
self.standardised_asset_list["non_intrusive_indicates_empty_cavity_no_sap_filter"] = (
(~self.standardised_asset_list[self.STANDARD_PROPERTY_TYPE].isin(["bedsit"])) &
(self.standardised_asset_list['non-intrusives: Construction'] == "CAVITY") &
self.standardised_asset_list['non-intrusives: Insulated'].isin(["EMPTY", "PARTIAL"]) &
(self.standardised_asset_list[self.STANDARD_YEAR_BUILT] <= 2002)
)
# If non_intrusive_indicates_empty_cavity is True,
# set non_intrusive_indicates_empty_cavity_no_sap_filter to False
self.standardised_asset_list["non_intrusive_indicates_empty_cavity_no_sap_filter"] = np.where(
self.standardised_asset_list["non_intrusive_indicates_empty_cavity"],
False,
self.standardised_asset_list["non_intrusive_indicates_empty_cavity_no_sap_filter"]
)
self.standardised_asset_list["epc_indicates_empty_cavity"] = ( self.standardised_asset_list["epc_indicates_empty_cavity"] = (
self.standardised_asset_list[self.EPC_API_DATA_NAMES["walls-description"]].str.lower().isin( self.standardised_asset_list[self.EPC_API_DATA_NAMES["walls-description"]].str.lower().isin(
@ -977,17 +992,16 @@ class AssetList:
) )
) )
z0 = self.standardised_asset_list[ # If the EPC is esimtated, we defer to the non-intrusives
self.standardised_asset_list["epc_indicates_empty_cavity"] & ( self.standardised_asset_list["epc_indicates_empty_cavity"] = np.where(
~self.standardised_asset_list["non_intrusive_indicates_empty_cavity"] (
) self.standardised_asset_list["epc_indicates_empty_cavity"] &
] ~self.standardised_asset_list["non_intrusive_indicates_empty_cavity"] &
z0['non-intrusives: Construction'].value_counts() self.standardised_asset_list["estimated"]
z0['non-intrusives: Insulated'].value_counts() ),
z00 = z0[z0['non-intrusives: Insulated'] == "EWI"] False,
self.standardised_asset_list["epc_indicates_empty_cavity"]
# If the EPC is estimated, perhaps we should defer to the non-intrusives? )
z00[""]
###################################################### ######################################################
# Extraction # Extraction
@ -997,33 +1011,14 @@ class AssetList:
self.standardised_asset_list["non_intrusive_indicates_cavity_extraction"] = ( self.standardised_asset_list["non_intrusive_indicates_cavity_extraction"] = (
(self.standardised_asset_list["non-intrusives: Construction"] == "CAVITY") & (self.standardised_asset_list["non-intrusives: Construction"] == "CAVITY") &
(self.standardised_asset_list["non-intrusives: Insulated"].isin(["RETRO DRILLED", "FILLED AT BUILD"])) & (self.standardised_asset_list["non-intrusives: Insulated"].isin(["RETRO DRILLED", "FILLED AT BUILD"])) &
(~self.standardised_asset_list['non-intrusives: Material'].isin(["GREY LOOSE BEAD", "COMPACTED BEAD"]) (~self.standardised_asset_list['non-intrusives: Material'].isin(
["GREY LOOSE BEAD", "COMPACTED BEAD", "FIBRE BATT NO CAVITY", "EMPTY NARROW BELOW 30mm"]
)
) & ( ) & (
self.standardised_asset_list[self.ATTRIBUTE_SAP_THRESHOLD_AND_BELOW] self.standardised_asset_list[self.ATTRIBUTE_SAP_THRESHOLD_AND_BELOW]
) )
) )
# z3 = self.standardised_asset_list[
# self.standardised_asset_list["non_intrusive_indicates_cavity_extraction"]
# ]
# z3['non-intrusives: Material'].value_counts()
# self.standardised_asset_list['non-intrusives: Material'].value_counts()
#
# z = self.standardised_asset_list[
# self.standardised_asset_list["non-intrusives: CIGA Check Required"] == "YES"
# ]
# z["non-intrusives: Insulated"].value_counts()
# z["non-intrusives: Material"].value_counts()
# z[self.ATTRIBUTE_SAP_THRESHOLD_AND_BELOW].value_counts()
# z[self.EPC_API_DATA_NAMES["current-energy-efficiency"]].max()
# z[self.EPC_API_DATA_NAMES["current-energy-efficiency"]].min()
# z[self.STANDARD_YEAR_BUILT].describe()
#
# zz = z[z[self.EPC_API_DATA_NAMES["current-energy-efficiency"]] == 105]
# z2 = self.standardised_asset_list[
# self.standardised_asset_list["non-intrusives: CIGA Check Required"] == "NO"
# ]
###################################################### ######################################################
# Solar # Solar
###################################################### ######################################################
@ -1114,7 +1109,7 @@ class AssetList:
) | ( ) | (
self.standardised_asset_list[ self.standardised_asset_list[
"walls_u_value"].apply( "walls_u_value"].apply(
lambda x: x <= 0.3 if not pd.isnull( lambda x: x <= 0.7 if not pd.isnull(
x) else False x) else False
) )
) )
@ -1141,7 +1136,7 @@ class AssetList:
"|".join(self.EPC_INSULATED_ROOF_SUBSTRINGS), regex=False "|".join(self.EPC_INSULATED_ROOF_SUBSTRINGS), regex=False
) | ( ) | (
self.standardised_asset_list[self.ATTRIBUTE_EPC_ROOF_INSULATION_THICKNESS].apply( self.standardised_asset_list[self.ATTRIBUTE_EPC_ROOF_INSULATION_THICKNESS].apply(
lambda x: int(x) >= 270 if str(x).isdigit() else False lambda x: int(x) >= 200 if str(x).isdigit() else False
) )
) | ( ) | (
self.standardised_asset_list["roof_u_value"].apply( self.standardised_asset_list["roof_u_value"].apply(
@ -1152,7 +1147,7 @@ class AssetList:
self.standardised_asset_list["solar_epc_loft_needs_topup"] = self.standardised_asset_list[ self.standardised_asset_list["solar_epc_loft_needs_topup"] = self.standardised_asset_list[
self.ATTRIBUTE_EPC_ROOF_INSULATION_THICKNESS].apply( self.ATTRIBUTE_EPC_ROOF_INSULATION_THICKNESS].apply(
lambda x: int(x) < 270 if str(x).isdigit() else False lambda x: int(x) < 200 if str(x).isdigit() else False
) )
# TODO: Fill with False - should be temp! # TODO: Fill with False - should be temp!
@ -1187,7 +1182,7 @@ class AssetList:
) & ( ) & (
# We do not utilise estimated EPCs for this method because we will always find that # We do not utilise estimated EPCs for this method because we will always find that
# "epc_has_floor_recommendation" is False # "epc_has_floor_recommendation" is False
~self.standardised_asset_list["estimated"] (self.standardised_asset_list["estimated"] == False)
) )
) | ( ) | (
( (
@ -1212,7 +1207,7 @@ class AssetList:
) & ( ) & (
# We do not utilise estimated EPCs for this method because we will always find that # We do not utilise estimated EPCs for this method because we will always find that
# "epc_has_floor_recommendation" is False # "epc_has_floor_recommendation" is False
~self.standardised_asset_list["estimated"] self.standardised_asset_list["estimated"] == False
) )
) | ( ) | (
( (
@ -1274,6 +1269,7 @@ class AssetList:
) )
# Other floor type, fully insulated # Other floor type, fully insulated
self.standardised_asset_list["solar_eligible_other_floor"] = ( self.standardised_asset_list["solar_eligible_other_floor"] = (
# Landlord data or EPC data indicates the heating system is appropriate # Landlord data or EPC data indicates the heating system is appropriate
( (
@ -1332,6 +1328,9 @@ class AssetList:
"Empty Cavity (non-intrusives)": ( "Empty Cavity (non-intrusives)": (
self.standardised_asset_list["non_intrusive_indicates_empty_cavity"].sum() self.standardised_asset_list["non_intrusive_indicates_empty_cavity"].sum()
), ),
"Empty Cavity (non-intrusives, no SAP filter)": (
self.standardised_asset_list["non_intrusive_indicates_empty_cavity_no_sap_filter"].sum()
),
"Empty Cavity (EPC)": ( "Empty Cavity (EPC)": (
( (
self.standardised_asset_list["epc_indicates_empty_cavity"] & self.standardised_asset_list["epc_indicates_empty_cavity"] &
@ -1359,6 +1358,17 @@ class AssetList:
) )
} }
# We produce a breakdown of the property types, for cavity fills
cavity_fills = self.standardised_asset_list[
self.standardised_asset_list["non_intrusive_indicates_empty_cavity"] | (
self.standardised_asset_list["epc_indicates_empty_cavity"]
)
]
self.work_type_breakdowns = {
"empty_cavity": cavity_fills[self.STANDARD_PROPERTY_TYPE].value_counts()
}
# Finally, we note why each property has been flagged # Finally, we note why each property has been flagged
self.standardised_asset_list["cavity_reason"] = None self.standardised_asset_list["cavity_reason"] = None
self.standardised_asset_list["cavity_reason"] = np.where( self.standardised_asset_list["cavity_reason"] = np.where(
@ -1366,6 +1376,11 @@ class AssetList:
"Non-Intrusive Data Showed Empty Cavity", "Non-Intrusive Data Showed Empty Cavity",
self.standardised_asset_list["cavity_reason"] self.standardised_asset_list["cavity_reason"]
) )
self.standardised_asset_list["cavity_reason"] = np.where(
self.standardised_asset_list["non_intrusive_indicates_empty_cavity_no_sap_filter"],
"Non-Intrusive Data Showed Empty Cavity but all SAP scores allowed",
self.standardised_asset_list["cavity_reason"]
)
self.standardised_asset_list["cavity_reason"] = np.where( self.standardised_asset_list["cavity_reason"] = np.where(
( (
self.standardised_asset_list["epc_indicates_empty_cavity"] & self.standardised_asset_list["epc_indicates_empty_cavity"] &

View file

@ -246,22 +246,40 @@ def app():
# - We want: fully insulated property (all wall types), EPC D or below (floors should be solid) # - We want: fully insulated property (all wall types), EPC D or below (floors should be solid)
# - Or the insulation required is loft/cavity (floors should be solid) # - Or the insulation required is loft/cavity (floors should be solid)
data_folder = "/Users/khalimconn-kowlessar/Documents/hestia/Customers/Colchester" # data_folder = "/Users/khalimconn-kowlessar/Documents/hestia/Customers/Colchester"
data_filename = "Warmfront data- Colchester Borough Homes (Complete).xlsx" # data_filename = "Warmfront data- Colchester Borough Homes (Complete).xlsx"
# sheet_name = "Sheet1"
# postcode_column = 'Full Address.1'
# fulladdress_column = "Full Address"
# address1_column = None
# address1_method = "first_word"
# address_cols_to_concat = []
# missing_postcodes_method = None
# landlord_year_built = "Build Date"
# landlord_os_uprn = None
# landlord_property_type = "Property Type"
# landlord_wall_construction = "Wallinsul"
# landlord_heating_system = "HeatSorc"
# landlord_existing_pv = None
# landlord_property_id = "Property Reference"
# For Westward
data_folder = "/Users/khalimconn-kowlessar/Documents/hestia/Customers/Westward"
data_filename = "WESTWARD - completed list..xlsx"
sheet_name = "Sheet1" sheet_name = "Sheet1"
postcode_column = 'Full Address.1' postcode_column = "WFT EDIT Postcode"
fulladdress_column = "Full Address" fulladdress_column = "Address"
address1_column = None address1_column = None
address1_method = "first_word" address1_method = "house_number_extraction"
address_cols_to_concat = [] address_cols_to_concat = []
missing_postcodes_method = None missing_postcodes_method = None
landlord_year_built = "Build Date" landlord_year_built = "Build date"
landlord_os_uprn = None landlord_os_uprn = "UPRN"
landlord_property_type = "Property Type" landlord_property_type = "Location type"
landlord_wall_construction = "Wallinsul" landlord_wall_construction = "Wall Construction (EPC)"
landlord_heating_system = "HeatSorc" landlord_heating_system = "Heat Source"
landlord_existing_pv = None landlord_existing_pv = "PV (Y/N)"
landlord_property_id = "Property Reference" landlord_property_id = "Place ref"
# Maps addresses to uprn in problematic cases # Maps addresses to uprn in problematic cases
MANUAL_UPRN_MAP = {} MANUAL_UPRN_MAP = {}

View file

@ -84,7 +84,7 @@ WALL_CONSTRUCTION_MAPPINGS = {
'Timber frame, as built, no insulation (assumed)': 'timber frame', 'Timber frame, as built, no insulation (assumed)': 'timber frame',
'Timber frame, as built, partial insulation (assumed)': 'timber frame', 'Timber frame, as built, partial insulation (assumed)': 'timber frame',
'Timber frame, with additional insulation': 'timber frame', 'Timber frame, with additional insulation': 'timber frame',
'CAVITY': 'partial unknown cavity', 'CAVITY': 'cavity unknown insulation',
'COMB': 'unknown', 'COMB': 'unknown',
'NONE': 'unknown', 'NONE': 'unknown',
'NOTKNOWN': 'unknown', 'NOTKNOWN': 'unknown',

View file

@ -4,7 +4,7 @@ from dotenv import load_dotenv
from utils.s3 import save_csv_to_s3 from utils.s3 import save_csv_to_s3
from etl.find_my_epc.AssetListEpcData import AssetListEpcData from etl.find_my_epc.AssetListEpcData import AssetListEpcData
PORTFOLIO_ID = 133 PORTFOLIO_ID = 137
USER_ID = 8 USER_ID = 8
load_dotenv(dotenv_path="backend/.env") load_dotenv(dotenv_path="backend/.env")
@ -19,10 +19,10 @@ def app():
asset_list = [ asset_list = [
{ {
"address": "40", "address": "41 Gainsborough Way",
"postcode": "PE4 5BB", "postcode": "BA21 5XU",
"uprn": 100090220519, "uprn": 30016708,
} },
] ]
asset_list = pd.DataFrame(asset_list) asset_list = pd.DataFrame(asset_list)
@ -52,8 +52,8 @@ def app():
valuation_data = [ valuation_data = [
{ {
"uprn": 100090220519, "uprn": 30016708,
"valuation": 135_000 "valuation": 189000
} }
] ]
# Store valuation data to s3 # Store valuation data to s3

View file

@ -993,7 +993,7 @@ class HeatingRecommender:
# We check if there's a mains connection and the hot water is inefficient, as this will improve with a boiler # We check if there's a mains connection and the hot water is inefficient, as this will improve with a boiler
has_inefficient_water = ( has_inefficient_water = (
self.property.data["mains-gas-flag"] and self.property.data["mains-gas-flag"] and
self.property.data["hot-water-energy-eff"] in ["Very Poor", "Poor", "Average"] self.property.data["hot-water-energy-eff"] in ["Very Poor", "Poor"]
) )
non_invasive_recommendation = next(( non_invasive_recommendation = next((