diff --git a/.idea/Model.iml b/.idea/Model.iml index c6561970..09f2e496 100644 --- a/.idea/Model.iml +++ b/.idea/Model.iml @@ -7,7 +7,7 @@ - + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 50cad4ca..fb10c6b0 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -3,7 +3,7 @@ - + diff --git a/asset_list/AssetList.py b/asset_list/AssetList.py index 28e17e2a..5f354a27 100644 --- a/asset_list/AssetList.py +++ b/asset_list/AssetList.py @@ -36,14 +36,13 @@ from dotenv import load_dotenv logger = setup_logger() load_dotenv(dotenv_path="../backend/.env") - # OpenAI API Key (set this in your environment variables for security) -OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", "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") +OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", + "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") class DataRemapper: def __init__(self, standard_values, standard_map=None, max_tokens=1000): - print(f"{OPENAI_API_KEY}") """ Initialize the remapper with standard values and a predefined mapping. @@ -1298,8 +1297,8 @@ class AssetList: self.standardised_asset_list[ self.ATTRIBUTE_HAS_SOLAR ] = self.standardised_asset_list[ - self.FIND_EPC_DATA_NAMES["Solar photovoltaics"] - ] | ~self.standardised_asset_list[ + self.FIND_EPC_DATA_NAMES["Solar photovoltaics"] + ] | ~self.standardised_asset_list[ self.EPC_API_DATA_NAMES["photo-supply"] ].isin( ["0.0", 0, None, "", np.nan] @@ -1317,7 +1316,7 @@ class AssetList: property_type=( str(x[self.STANDARD_PROPERTY_TYPE]).title() if str(x[self.STANDARD_PROPERTY_TYPE]).title() - in accepted_epc_property_types + in accepted_epc_property_types else ( x[self.EPC_API_DATA_NAMES["property-type"]] if not pd.isnull( @@ -1375,9 +1374,9 @@ class AssetList: self.standardised_asset_list.apply( lambda x: estimate_perimeter( floor_area=x[self.EPC_API_DATA_NAMES["total-floor-area"]] - / x[self.ATTRIBUTE_NUMBER_OF_FLOORS], + / x[self.ATTRIBUTE_NUMBER_OF_FLOORS], num_rooms=x[self.EPC_API_DATA_NAMES["number-habitable-rooms"]] - / x[self.ATTRIBUTE_NUMBER_OF_FLOORS], + / x[self.ATTRIBUTE_NUMBER_OF_FLOORS], ), axis=1, ) @@ -1462,7 +1461,7 @@ class AssetList: year_lower_bound = ( 2007 if x[self.EPC_API_DATA_NAMES["construction-age-band"]] - == "England and Wales: 2007 onwards" + == "England and Wales: 2007 onwards" else 2012 ) @@ -1517,7 +1516,7 @@ class AssetList: age_band_matches = ( "EPC Age Band Matches Year Built" if x[self.STANDARD_YEAR_BUILT] - == int(x[self.EPC_API_DATA_NAMES["construction-age-band"]]) + == int(x[self.EPC_API_DATA_NAMES["construction-age-band"]]) else "EPC Age Band is different from Year Built" ) @@ -1547,7 +1546,7 @@ class AssetList: age_band_matches = ( "EPC Age Band Matches Year Built" if (x[self.STANDARD_YEAR_BUILT] >= float(lower_date)) - and (x[self.STANDARD_YEAR_BUILT] <= float(upper_date)) + and (x[self.STANDARD_YEAR_BUILT] <= float(upper_date)) else ( "EPC Age Band is older than Year Built" if x[self.STANDARD_YEAR_BUILT] > float(upper_date) @@ -1719,22 +1718,22 @@ class AssetList: if self.non_intrusives_present: if self.new_format_non_insturives_present_v2: non_intrusives_wall_filter = ( - self.standardised_asset_list["non-intrusives: Construction"] - == "CAVITY" - ) & self.standardised_asset_list["non-intrusives: Insulated"].isin( + self.standardised_asset_list["non-intrusives: Construction"] + == "CAVITY" + ) & self.standardised_asset_list["non-intrusives: Insulated"].isin( ["EMPTY", "PARTIAL", "EMPTY CAVITY"] ) else: non_intrusives_wall_filter = ( - self.standardised_asset_list["non-intrusives: Construction"] - == "CAVITY" - ) & self.standardised_asset_list["non-intrusives: Insulated"].isin( + self.standardised_asset_list["non-intrusives: Construction"] + == "CAVITY" + ) & self.standardised_asset_list["non-intrusives: Insulated"].isin( ["EMPTY", "PARTIAL"] ) elif self.old_format_non_intrusives_present: non_intrusives_wall_filter = self.standardised_asset_list[ - "non-intrusives: WFT Findings" - ].str.lower().str.strip().isin( + "non-intrusives: WFT Findings" + ].str.lower().str.strip().isin( [ "empty cavity", "partial fill", @@ -1744,18 +1743,18 @@ class AssetList: "empty cav", ] ) | ( - ( - self.standardised_asset_list["non-intrusives: WFT Findings"] - .str.lower() - .str.strip() - .str.contains("empty cavity|partial fill") - & ~self.standardised_asset_list["non-intrusives: WFT Findings"] - .astype(str) - .str.lower() - .str.strip() - .str.contains("major access issues") - ) - ) + ( + self.standardised_asset_list["non-intrusives: WFT Findings"] + .str.lower() + .str.strip() + .str.contains("empty cavity|partial fill") + & ~self.standardised_asset_list["non-intrusives: WFT Findings"] + .astype(str) + .str.lower() + .str.strip() + .str.contains("major access issues") + ) + ) else: # We set the filter to False, as we have no non-intrusives non_intrusives_wall_filter = False @@ -1767,12 +1766,12 @@ class AssetList: ) else: year_built_filter = ( - self.standardised_asset_list[self.STANDARD_YEAR_BUILT] - <= self.EMPTY_CAVITY_YEAR_THRESHOLD - ) | ( - self.standardised_asset_list["epc_year_upper_bound"] - <= self.EMPTY_CAVITY_YEAR_THRESHOLD - ) + self.standardised_asset_list[self.STANDARD_YEAR_BUILT] + <= self.EMPTY_CAVITY_YEAR_THRESHOLD + ) | ( + self.standardised_asset_list["epc_year_upper_bound"] + <= self.EMPTY_CAVITY_YEAR_THRESHOLD + ) # Criteria: # The property isn't a bedsit @@ -1813,8 +1812,8 @@ class AssetList: ] = ( ~self.standardised_asset_list["non_intrusive_indicates_empty_cavity"] & ~self.standardised_asset_list[ - "non_intrusive_indicates_empty_cavity_has_solar" - ] + "non_intrusive_indicates_empty_cavity_has_solar" + ] & ( ~self.standardised_asset_list[self.STANDARD_PROPERTY_TYPE].isin( ["bedsit"] @@ -1890,8 +1889,8 @@ class AssetList: .str.lower() .isin(self.EPC_NO_WALL_INSULATION_DESCRIPTIONS) | self.standardised_asset_list[self.STANDARD_WALL_CONSTRUCTION].isin( - ["uninsulated cavity"] - ) + ["uninsulated cavity"] + ) ) ###################################################### @@ -1928,8 +1927,8 @@ class AssetList: extraction_wall_filter = ( extraction_wall_filter & ~self.standardised_asset_list[ - "non-intrusives: Eligibility (Red/Yellow/Green)" - ].isin(["RED"]) + "non-intrusives: Eligibility (Red/Yellow/Green)" + ].isin(["RED"]) ) self.standardised_asset_list[ @@ -2025,26 +2024,26 @@ class AssetList: self.standardised_asset_list[ "solar_epc_data_indicates_correct_heating_system" ] = ( - self.standardised_asset_list[ - self.EPC_API_DATA_NAMES["mainheat-description"] - ] - .str.lower() - .str.contains( - "air source heat pump|ground source heat pump|boiler and radiators, electric" - ) - ) | ( - self.standardised_asset_list[ - self.EPC_API_DATA_NAMES["mainheat-description"] - ] - .str.lower() - .str.contains("electric storage heaters") - & ( self.standardised_asset_list[ - self.EPC_API_DATA_NAMES["mainheatcont-description"] + self.EPC_API_DATA_NAMES["mainheat-description"] ] - == "Controls for high heat retention storage heaters" + .str.lower() + .str.contains( + "air source heat pump|ground source heat pump|boiler and radiators, electric" + ) + ) | ( + self.standardised_asset_list[ + self.EPC_API_DATA_NAMES["mainheat-description"] + ] + .str.lower() + .str.contains("electric storage heaters") + & ( + self.standardised_asset_list[ + self.EPC_API_DATA_NAMES["mainheatcont-description"] + ] + == "Controls for high heat retention storage heaters" + ) ) - ) # If the landlord has given us the heating system, we default to that on heating upgrades. Because of the # poor heating in place, if the EPC indicates that this property had a low efficiency heating system but the @@ -2052,25 +2051,25 @@ class AssetList: self.standardised_asset_list[ "solar_epc_data_indicates_requires_heating_upgrade" ] = ( - self.standardised_asset_list[ - self.EPC_API_DATA_NAMES["mainheat-description"] - ] - .str.lower() - .str.contains("electric storage heaters|room heaters") - & ( self.standardised_asset_list[ - self.EPC_API_DATA_NAMES["mainheatcont-description"] + self.EPC_API_DATA_NAMES["mainheat-description"] ] - != "Controls for high heat retention storage heaters" + .str.lower() + .str.contains("electric storage heaters|room heaters") + & ( + self.standardised_asset_list[ + self.EPC_API_DATA_NAMES["mainheatcont-description"] + ] + != "Controls for high heat retention storage heaters" + ) + ) & ( + ~self.standardised_asset_list[self.STANDARD_HEATING_SYSTEM].isin( + ["district heating", "communal heating", "communal gas boiler"] + ) + & ~self.standardised_asset_list[self.STANDARD_HEATING_SYSTEM] + .astype(str) + .str.contains("gas ") ) - ) & ( - ~self.standardised_asset_list[self.STANDARD_HEATING_SYSTEM].isin( - ["district heating", "communal heating", "communal gas boiler"] - ) - & ~self.standardised_asset_list[self.STANDARD_HEATING_SYSTEM] - .astype(str) - .str.contains("gas ") - ) # Basic check - both of the previous two shouldn't be true simultaneously if ( @@ -2150,8 +2149,8 @@ class AssetList: self.standardised_asset_list[ "solar_non_intrusives_walls_insulated" ] = self.standardised_asset_list[ - "non-intrusives: WFT Findings" - ].str.lower().str.strip().isin( + "non-intrusives: WFT Findings" + ].str.lower().str.strip().isin( [ "retro drilled", "retro filled", @@ -2160,8 +2159,8 @@ class AssetList: "retro drilled and filled", ] ) | self.standardised_asset_list[ - "non-intrusives: WFT Findings" - ].str.lower().str.strip().str.contains( + "non-intrusives: WFT Findings" + ].str.lower().str.strip().str.contains( "retro drilled" ) else: @@ -2178,14 +2177,19 @@ class AssetList: ) self.standardised_asset_list["solar_epc_walls_insulated"] = ( - self.standardised_asset_list[self.EPC_API_DATA_NAMES["walls-description"]] - .str.lower() - .str.contains("|".join(self.EPC_INSULATED_WALLS_SUBSTRINGS)) - ) | ( - self.standardised_asset_list["walls_u_value"].apply( - lambda x: x <= 0.7 if not pd.isnull(x) else False - ) - ) + self.standardised_asset_list[ + self.EPC_API_DATA_NAMES[ + "walls-description"]] + .str.lower() + .str.contains("|".join( + self.EPC_INSULATED_WALLS_SUBSTRINGS)) + ) | ( + self.standardised_asset_list[ + "walls_u_value"].apply( + lambda x: x <= 0.7 if not pd.isnull( + x) else False + ) + ) roof_data = [] for desc in self.standardised_asset_list[ @@ -2227,20 +2231,20 @@ class AssetList: 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) < 200 if str(x).isdigit() else False ) | ( - ( - self.standardised_asset_list["is_loft"] - | self.standardised_asset_list["is_pitched"] + ( + self.standardised_asset_list["is_loft"] + | self.standardised_asset_list["is_pitched"] + ) + & ( + self.standardised_asset_list[ + self.ATTRIBUTE_EPC_ROOF_INSULATION_THICKNESS + ].isin(["below average", "none"]) + ) ) - & ( - self.standardised_asset_list[ - self.ATTRIBUTE_EPC_ROOF_INSULATION_THICKNESS - ].isin(["below average", "none"]) - ) - ) self.standardised_asset_list["epc_has_floor_recommendation"] = ( self.standardised_asset_list["epc_has_floor_recommendation"].fillna(False) @@ -2249,15 +2253,16 @@ class AssetList: # Check if the boiler is electric # We check if it contains both the terms boiler & electric self.standardised_asset_list["has_electric_boiler"] = ( - self.standardised_asset_list[ - self.EPC_API_DATA_NAMES["mainheat-description"] - ] - .str.lower() - .isin(["boiler and radiators, electric"]) - ) | ( - self.standardised_asset_list[self.STANDARD_HEATING_SYSTEM] - == "electric boiler" - ) + self.standardised_asset_list[ + self.EPC_API_DATA_NAMES["mainheat-description"] + ] + .str.lower() + .isin(["boiler and radiators, electric"]) + ) | ( + self.standardised_asset_list[ + self.STANDARD_HEATING_SYSTEM] + == "electric boiler" + ) #################################### # Check solar eligibility @@ -2395,11 +2400,11 @@ class AssetList: empty_cavity_map = { "non_intrusive_indicates_empty_cavity": self.EMPTY_CAVITY_NON_INTRUSIVE - + ": ", + + ": ", "non_intrusive_indicates_empty_cavity_has_solar": f"{self.EMPTY_CAVITY_NON_INTRUSIVE} - property " - "already has solar: ", + "already has solar: ", "non_intrusive_indicates_empty_cavity_no_year_filter": f"{self.EMPTY_CAVITY_NON_INTRUSIVE}, " - f"built after {self.EMPTY_CAVITY_YEAR_THRESHOLD}: ", + f"built after {self.EMPTY_CAVITY_YEAR_THRESHOLD}: ", } for variable, description in empty_cavity_map.items(): self.standardised_asset_list["cavity_reason"] = np.where( @@ -2415,8 +2420,8 @@ class AssetList: ( self.standardised_asset_list["epc_indicates_empty_cavity"] & ~self.standardised_asset_list[ - "non_intrusive_indicates_empty_cavity" - ] + "non_intrusive_indicates_empty_cavity" + ] & ( self.standardised_asset_list["non-intrusives: WFT Findings"] .str.lower() @@ -2441,8 +2446,8 @@ class AssetList: ( self.standardised_asset_list["epc_indicates_empty_cavity"] & ~self.standardised_asset_list[ - "non_intrusive_indicates_empty_cavity" - ] + "non_intrusive_indicates_empty_cavity" + ] & self.standardised_asset_list[ "non_intrusive_indicates_cavity_extraction" ] @@ -2457,8 +2462,8 @@ class AssetList: ( self.standardised_asset_list["epc_indicates_empty_cavity"] & ~self.standardised_asset_list[ - "non_intrusive_indicates_empty_cavity" - ] + "non_intrusive_indicates_empty_cavity" + ] & ( self.standardised_asset_list["non-intrusives: Insulated"] == "RETRO DRILLED" @@ -2474,8 +2479,8 @@ class AssetList: ( self.standardised_asset_list["epc_indicates_empty_cavity"] & ~self.standardised_asset_list[ - "non_intrusive_indicates_empty_cavity" - ] + "non_intrusive_indicates_empty_cavity" + ] & ( self.standardised_asset_list["non-intrusives: Insulated"] == "FILLED AT BUILD" @@ -2491,8 +2496,8 @@ class AssetList: ( self.standardised_asset_list["epc_indicates_empty_cavity"] & ~self.standardised_asset_list[ - "non_intrusive_indicates_empty_cavity" - ] + "non_intrusive_indicates_empty_cavity" + ] & pd.isnull(self.standardised_asset_list["cavity_reason"]) ), f"{self.EPC_EMPTY}: " + self.standardised_asset_list["SAP Category"], @@ -2636,7 +2641,7 @@ class AssetList: identified_work = self.standardised_asset_list[ ~pd.isnull(self.standardised_asset_list["cavity_reason"]) | ~pd.isnull(self.standardised_asset_list["solar_reason"]) - ][self.DOMNA_PROPERTY_ID].values + ][self.DOMNA_PROPERTY_ID].values if self.DOMNA_PROPERTY_ID in self.outcomes.columns: self.outcomes_for_output = self.outcomes[ @@ -2671,12 +2676,12 @@ class AssetList: blocks_of_flats = self.standardised_asset_list[ self.standardised_asset_list[self.STANDARD_PROPERTY_TYPE] == "block of flats" - ] + ] non_blocks_of_flats = self.standardised_asset_list[ self.standardised_asset_list[self.STANDARD_PROPERTY_TYPE] != "block of flats" - ] + ] # Produce some aggregate figures self.work_type_figures = { @@ -2719,7 +2724,7 @@ class AssetList: blocks = self.standardised_asset_list[ self.standardised_asset_list[self.STANDARD_PROPERTY_TYPE] == "block of flats" - ].copy() + ].copy() if blocks.empty: return @@ -2856,7 +2861,7 @@ class AssetList: self.standardised_asset_list = self.standardised_asset_list[ self.standardised_asset_list[self.STANDARD_PROPERTY_TYPE] != "block of flats" - ] + ] self.standardised_asset_list = pd.concat( [self.standardised_asset_list, expanded_blocks], ignore_index=True @@ -2936,7 +2941,7 @@ class AssetList: # find any block refs with more than 50% emptires viable_empty_blocks = self.block_analysis_df[ self.block_analysis_df["Percentage of Empties"] >= 0.50 - ] + ] if not viable_empty_blocks.empty: project_code_lookup = viable_empty_blocks[["Block Reference"]].copy() @@ -3175,7 +3180,7 @@ class AssetList: contact_details = pd.read_excel(local_filepath, sheet_name=sheet_name)[ [self.contact_detail_fields["landlord_property_id"]] + details_colnames - ] + ] contact_details = contact_details[ ~pd.isnull( contact_details[self.contact_detail_fields["landlord_property_id"]] @@ -3568,10 +3573,13 @@ class AssetList: "Non-Intrusives: Date Checked ": date_of_inspections, "Non-Intrusives: Wall Type ": non_intrusives_construction, "Non-intrusives: Insulation ": non_intrusives_insulated, - "Non-intrusives: Insulation Material ": non_intrusives_insulation_material, - "Non-Intrusives: CIGA Check Required ": non_intrusives_ciga_check_required, + "Non-intrusives: Insulation Material ": + non_intrusives_insulation_material, + "Non-Intrusives: CIGA Check Required ": + non_intrusives_ciga_check_required, "Non-Intrusives: PV Access Issues ": non_intrusives_pv_access, - "Non-Intrusives: Roof Orientation ": non_intrusives_roof_orientation, + "Non-Intrusives: Roof Orientation ": + non_intrusives_roof_orientation, "Non-Intrusives: Surveyor Notes ": non_intrusives_surveyor_notes, "Non-Intrusives: Surveyor Name ": non_intrusives_surveyor_name, "CIGA: Date Requested ": None, # TODO: Don't have this for the moment @@ -3748,8 +3756,8 @@ class AssetList: # We compare address line 1 to full address if any( df[self.STANDARD_FULL_ADDRESS] - .str.lower() - .str.contains(row["Address Line 1"].lower(), na=False) + .str.lower() + .str.contains(row["Address Line 1"].lower(), na=False) ): df = df[ df[self.STANDARD_FULL_ADDRESS] @@ -3989,7 +3997,7 @@ class AssetList: matched = matched[ matched["houseno"].astype(str) == house_no_to_match - ] + ] if matched.shape[0] == 1: lookup_i.append( { @@ -4014,7 +4022,7 @@ class AssetList: )[0] matched = matched[ matched[self.STANDARD_FULL_ADDRESS] == best_match - ] + ] lookup_i.append( { "row_id": x["row_id"], @@ -4325,7 +4333,7 @@ class AssetList: df = self.standardised_asset_list[ self.standardised_asset_list[self.STANDARD_LANDLORD_PROPERTY_ID] == row[master_id_colnames[idx]] - ] + ] if df.shape[0] == 1: matched.append( { @@ -4431,7 +4439,7 @@ class AssetList: )[1] ) > 90 - ] + ] if df.shape[0] == 0: unmatched.append(row["row_id"]) @@ -4439,8 +4447,8 @@ class AssetList: if any( df[self.STANDARD_FULL_ADDRESS] - .str.lower() - .str.contains( + .str.lower() + .str.contains( " ".join( [row[house_no_col], row["Street / Block Name"]] ).lower() @@ -4467,7 +4475,7 @@ class AssetList: row[property_type_col].split(" ")[-1].lower() ) & (df[self.STANDARD_PROPERTY_TYPE] != "block of flats") - ] + ] if df.shape[0] != 1: # We have multiple matches - it's likely because the landlord has a duplicate diff --git a/asset_list/app.py b/asset_list/app.py index 3e492118..0b792270 100644 --- a/asset_list/app.py +++ b/asset_list/app.py @@ -13,12 +13,11 @@ from asset_list.utils import get_data from dotenv import load_dotenv from backend.SearchEpc import SearchEpc -load_dotenv(dotenv_path="../backend/.env") +load_dotenv(dotenv_path="backend/.env") EPC_AUTH_TOKEN = os.getenv( "EPC_AUTH_TOKEN", ) - OPENAI_API_KEY = os.getenv( "OPENAI_API_KEY", ) @@ -74,24 +73,25 @@ def app(): Property UPRN """ - data_folder = "/workspaces/model/asset_list" - data_filename = "assests.xlsx" + data_folder = "/Users/khalimconn-kowlessar/Documents/hestia/Customers/Lifespace Rentals/Missed" + # data_filename = "For Modelling - Final - reviewed.xlsx" + data_filename = "Missed Properties - with address.xlsx" sheet_name = "Sheet1" postcode_column = "Postcode" - address1_column = "Address" - address1_method = "house_number_extraction" - fulladdress_column = None - address_cols_to_concat = ["Address"] + address1_column = "address1" + address1_method = None + fulladdress_column = "address1" + address_cols_to_concat = [] missing_postcodes_method = None landlord_year_built = None landlord_os_uprn = "UPRN" - landlord_property_type = "Archetype" - landlord_built_form = "Bedroom Count" - landlord_wall_construction = "Wall Insulation Type" - landlord_roof_construction = "Roof Type" - landlord_heating_system = "Boiler Type" + landlord_property_type = "Type" + landlord_built_form = None + landlord_wall_construction = None + landlord_roof_construction = None + landlord_heating_system = None landlord_existing_pv = None - landlord_property_id = "Tab" + landlord_property_id = "Reference" landlord_sap = None outcomes_filename = None outcomes_sheetname = None @@ -243,7 +243,7 @@ def app(): if skip is not None and not force_retrieve_data: if i <= skip: continue - chunk = asset_list.standardised_asset_list[i : i + chunk_size] + chunk = asset_list.standardised_asset_list[i: i + chunk_size] epc_data_chunk, errors_chunk, no_epc_chunk = get_data( df=chunk, row_id_name=asset_list.DOMNA_PROPERTY_ID, @@ -386,7 +386,7 @@ def app(): # Retrieve just the data we need epc_df = epc_df[ [asset_list.DOMNA_PROPERTY_ID] + list(asset_list.EPC_API_DATA_NAMES.keys()) - ].rename(columns=asset_list.EPC_API_DATA_NAMES) + ].rename(columns=asset_list.EPC_API_DATA_NAMES) # Look for columns not in the find my EPC data, which will have happened if we didn't # retrieve it in the first place @@ -403,16 +403,12 @@ def app(): find_my_epc_data[ [asset_list.DOMNA_PROPERTY_ID, "epc_has_floor_recommendation"] + list(asset_list.FIND_EPC_DATA_NAMES.keys()) - ].rename(columns=asset_list.FIND_EPC_DATA_NAMES), + ].rename(columns=asset_list.FIND_EPC_DATA_NAMES), how="left", on=asset_list.DOMNA_PROPERTY_ID, ) asset_list.merge_data(epc_df) - # asset_list.standardised_asset_list = asset_list.standardised_asset_list[ - # asset_list.standardised_asset_list["domna_full_address"] - # != "120 Airdrie Crescent, Burnley, Lancashire" - # ] asset_list.extract_attributes() asset_list.identify_worktypes() @@ -426,27 +422,6 @@ def app(): os.path.join(data_folder, ".".join(data_filename.split(".")[:-1])) + " - Standardised.xlsx" ) - # Store the data in two tabs. One for the asset list with the EPC data and the second with the flat data - - # Determine inspections priority - # solar_jobs = asset_list.standardised_asset_list[~pd.isnull(asset_list.standardised_asset_list["solar_reason"])][ - # "domna_postcode"].unique() - # asset_list.standardised_asset_list["in_solar_area"] = asset_list.standardised_asset_list["domna_postcode"].isin( - # solar_jobs - # ) - # # Same for cav - # cavity_jobs = asset_list.standardised_asset_list[ - # ~pd.isnull(asset_list.standardised_asset_list["cavity_reason"]) - # ]["domna_postcode"].unique() - # asset_list.standardised_asset_list["in_cavity_area"] = asset_list.standardised_asset_list["domna_postcode"].isin( - # cavity_jobs - # ) - # # We prioritise properties that are in solar areas and cavity areas - # import numpy as np - # asset_list.standardised_asset_list["inspection_priority"] = np.where( - # asset_list.standardised_asset_list["in_solar_area"] | asset_list.standardised_asset_list["in_cavity_area"], - # 1, 2 - # ) with pd.ExcelWriter(filename) as writer: asset_list.standardised_asset_list.to_excel( diff --git a/asset_list/mappings/built_form.py b/asset_list/mappings/built_form.py index d6466539..4842450d 100644 --- a/asset_list/mappings/built_form.py +++ b/asset_list/mappings/built_form.py @@ -528,6 +528,107 @@ BUILT_FORM_MAPPINGS = { 'House: Semi Detached: Top Floor': 'semi-detached', 'House: End Terrace: Ground Floor': 'end-terrace', 'Maisonette: Enclosed End Terrace: Mid Floor': 'enclosed end-terrace', - 'Bungalow: EnclosedEndTerrace': 'enclosed end-terrace' + 'Bungalow: EnclosedEndTerrace': 'enclosed end-terrace', + '2 BED MID TERRACED HOUSE': 'mid-terrace', + '4 BED SEMI DETACHED-PARLOURED': 'semi-detached', + '2 BED END TERRACED HOUSE': 'end-terrace', + '3 BED MID TERRACED HOUSE': 'mid-terrace', + '3 BED SEMI DETACHED HOUSE': 'semi-detached', + '3 BED MID TERRACE - PARLOURED': 'mid-terrace', + '3 BED END TERRACE - PARLOURED': 'end-terrace', + '4 BED+ END TERRACED HOUSE': 'end-terrace', + '3 BED END TERRACED HOUSE': 'end-terrace', + '3 BED SEMI DETACHED-PARLOURED': 'semi-detached', + '4 BED+ END TERRACE - PARLOURED': 'end-terrace', + '2 BED SEMI DETACHED HOUSE': 'semi-detached', + '3 BED DETACHED HOUSE': 'detached', + '2 BED GRD FLR COTT FLT-CNT STR': 'ground floor', + '2 BED 1ST FLOOR WALKUP FLAT': 'mid-floor', + '1 BED GRD FL COTT FLAT-OWN ENT': 'ground floor', + '1 BED 1ST FL WALK UP DECK ACC': 'mid-floor', + '2 BED MAISONETTE UPPER COM ENT': 'mid-floor', + '2 BED GRD FLR COTT FLT OWN ENT': 'ground floor', + '1 BED BUNGALOW': 'unknown', + '2 BED GRD FL COTT FLT-OWN ENTR': 'ground floor', + '1 BED 1ST FL COTT FLT-CNT STR': 'mid-floor', + '1 BED GRD FL WALK UP OWN ENT': 'ground floor', + '1 BED GRD FLOOR WALKUP FLAT': 'ground floor', + '2 BED GRD FLOOR WALKUP FLAT': 'ground floor', + '2 BED 1ST FLR FLT-SHELTERED': 'mid-floor', + '2 BED BUNGALOW': 'unknown', + '2 BED GRD FLR COTT FLT(P)-1950': 'ground floor', + + 'Ground Floor Front Left': 'ground floor', + 'End-Terrace House': 'end-terrace', + 'Ground floor': 'ground floor', + 'Ground Floor Front Right': 'ground floor', + 'End Terrace (GII List)': 'end-terrace', + 'Semi Detached House': 'semi-detached', + 'Ground Floor Right': 'ground floor', + 'PB Ground Floor Flat': 'ground floor', + 'Basement and Ground Floor': 'ground floor', + 'Semi-detached bungalow': 'detached', + 'Detached Cottage': 'detached', + 'Lower & Ground Floor': 'ground floor', + 'Ground FLoor Flat': 'ground floor', + 'ground floor': 'ground floor', + 'Ground Floor Left': 'ground floor', + 'Semi-detached House': 'detached', + 'Basement & Lower Ground': 'basement', + 'Semi-Detached House': 'detached', + 'Ground floor flat -': 'ground floor', + 'Basement Flat': 'basement', + 'semi-detached bungalow': 'semi-detached', + 'Lower Ground Floor Flat': 'ground floor', + 'Ground floor Flat': 'ground floor', + 'Ground Floor flat': 'ground floor', + 'Ground': 'ground floor', + 'Semi detached Bungalow': 'semi-detached', + 'ground floor flat': 'ground floor', + 'Mid terrace House': 'mid-terrace', + 'Raised Ground Floor': 'ground floor', + 'Basement Floor': 'basement', + 'Second floor flat': 'mid-floor', + 'Fourth Floor Flat': 'mid-floor', + 'First/Second Maisonette': 'mid-floor', + 'Ground/First': 'ground floor', + 'First and Second Floor': 'mid-floor', + 'Terrace House': 'mid-terrace', + '1st/2nd Floor Maisonette': 'mid-floor', + 'Semi-det House': 'semi-detached', + 'First': 'mid-floor', + 'Ground & First Floor': 'ground floor', + 'End of Terrace House': 'end-terrace', + '2nd Floor Purpose Built': 'mid-floor', + 'First/Second Floor Maison': 'mid-floor', + 'GFF purpose built': 'ground floor', + 'Second': 'mid-floor', + 'Semi-det House (GII List)': 'semi-detached', + '3rd and 4th Floor': 'mid-floor', + 'First Floor flat': 'mid-floor', + 'Mid-Terrace House': 'mid-terrace', + '1st & 2nd Floors': 'mid-floor', + 'Ground/first floor': 'ground floor', + 'FFF purpose built': 'mid-floor', + 'Second floor': 'mid-floor', + 'Second/Third floor': 'mid-floor', + 'First floor Flat': 'mid-floor', + 'First floor': 'mid-floor', + 'Lower Ground Flat': 'basement', + 'First Floor Rear Flat': 'mid-floor', + 'First & Second Floor': 'mid-floor', + 'Ground & Lower Ground': 'basement', + 'First Floor Rear': 'mid-floor', + 'First & Second': 'mid-floor', + 'First Floor Front': 'mid-floor', + 'First & Second Floors': 'mid-floor', + 'First/Second Floor': 'mid-floor', + 'Sem-detach house': 'semi-detached', + 'Second Floor Flat (Top)': 'top-floor', + '3 FloorTerrace House': 'mid-terrace', + 'First floor flat': 'mid-floor', + 'First & Second Floor Flat': 'mid-floor', + 'First Floor Purpose Built': 'mid-floor', + 'Purpose built First Floor': 'mid-floor', } diff --git a/asset_list/mappings/heating_systems.py b/asset_list/mappings/heating_systems.py index 272d6279..5f962108 100644 --- a/asset_list/mappings/heating_systems.py +++ b/asset_list/mappings/heating_systems.py @@ -498,6 +498,23 @@ HEATING_MAPPINGS = { 'Boiler: A rated Combi, System 2: Boiler: A rated Combi': 'gas combi boiler', 'System 2: Boiler: A rated Regular Boiler, Boiler: A rated Regular Boiler': 'gas boiler, radiators', - 'Boiler: A rated Combi, System 2: Boiler: C rated Combi': 'gas combi boiler' + 'Boiler: A rated Combi, System 2: Boiler: C rated Combi': 'gas combi boiler', + + 'IDEAL ISAR HE30': 'gas combi boiler', + 'WORCESTER GREENSTAR 25 SI': 'gas combi boiler', + 'POTTERTON PROMAX COMBI 28 HE PLUS': 'gas combi boiler', + 'WORCESTER GREENSTAR 28I JUNIOR': 'gas combi boiler', + 'BAXI ASSURE 25 COMBI': 'gas combi boiler', + 'POTTERTON PROMAX COMBI 28 HE PLUS A': 'gas combi boiler', + 'WORCESTER GREENSTAR 30 SI': 'gas combi boiler', + 'POTTERTON SUPRIMA 40L': 'gas boiler, radiators', + 'POTTERTON ASSURE 30 COMBI': 'gas combi boiler', + 'POTTERTON PROMAX 28 COMBI ERP': 'gas combi boiler', + 'BAXI ASSURE 30 COMBI': 'gas combi boiler', + 'POTTERTON PROMAX 18 SYSTEM ERP': 'gas boiler, radiators', + 'POTTERTON PROMAX COMBI 33 HE PLUS A': 'gas combi boiler', + 'POTTERTON SUPRIMA 40 HE': 'gas boiler, radiators', + 'FERROLI MODENA 102': 'gas boiler, radiators', + 'POTTERTON PROMAX COMBI 24 HE PLUS A': 'gas combi boiler' } diff --git a/asset_list/mappings/property_type.py b/asset_list/mappings/property_type.py index 177a7549..71788c25 100644 --- a/asset_list/mappings/property_type.py +++ b/asset_list/mappings/property_type.py @@ -444,6 +444,9 @@ PROPERTY_MAPPING = { 'Warden Bungalow': 'bungalow', 'Warden Flat': 'flat', 'Upper Floor Flat': 'flat', - 'Extracare Scheme': 'other' + 'Extracare Scheme': 'other', + + 'SHELTERED': 'unknown', + 'PARLOUR': 'unknown', } diff --git a/asset_list/mappings/roof.py b/asset_list/mappings/roof.py index 70cc8742..192238e0 100644 --- a/asset_list/mappings/roof.py +++ b/asset_list/mappings/roof.py @@ -320,6 +320,8 @@ ROOF_CONSTRUCTION_MAPPINGS = { 'Pitched (slates or tiles) access to loft, 100mm': 'pitched insulated', 'Pitched (slates or tiles) no loft access, 200mm': 'pitched insulated', 'Pitched (slates or tiles) access to loft, 200mm': 'pitched insulated', - 'Pitched (slates or tiles) access to loft, 50mm': 'pitched less than 100mm insulation' + 'Pitched (slates or tiles) access to loft, 50mm': 'pitched less than 100mm insulation', + + 'Pitched roofs': 'pitched unknown insulation', } diff --git a/asset_list/mappings/walls.py b/asset_list/mappings/walls.py index 1a252b33..c369204d 100644 --- a/asset_list/mappings/walls.py +++ b/asset_list/mappings/walls.py @@ -369,6 +369,9 @@ WALL_CONSTRUCTION_MAPPINGS = { 'Solid Brick, As built': 'solid brick unknown insulation', 'System built, As built': 'system built unknown insulation', 'Timber frame, As built': 'timber frame unknown insulation', - 'Cavity, As built': 'cavity unknown insulation' + 'Cavity, As built': 'cavity unknown insulation', + 'FILLED CAVITY': 'filled cavity', + 'EXTERNAL': 'insulated solid brick', + 'AS BUILT': 'other' }