diff --git a/backend/Property.py b/backend/Property.py index d1f5a1e2..e57d8326 100644 --- a/backend/Property.py +++ b/backend/Property.py @@ -118,7 +118,6 @@ class Property: self.number_lighting_outlets = epc_record.prepared_epc.get("fixed_lighting_outlets_count") self.floor_level = None self.number_of_windows = None - self.solar_pv_roof_area = None self.solar_pv_percentage = None self.current_adjusted_energy = None @@ -185,6 +184,8 @@ class Property: recommendation_record["walls_insulation_thickness_ending"] = "above average" recommendation_record["walls_energy_eff_ending"] = "Good" + # Note: often when the wall is insulatied, the internal/external insulation is not noted so we should + # test the impact of using these booleans if recommendation["type"] == "external_wall_insulation": recommendation_record["external_insulation"] = True recommendation_record["internal_insulation"] = False @@ -238,7 +239,10 @@ class Property: recommendation_record["roof_insulation_thickness_ending"] = str(proposed_depth) if recommendation["type"] == "loft_insulation": - recommendation_record["roof_energy_eff_ending"] = "Good" + if proposed_depth >= 270: + recommendation_record["roof_energy_eff_ending"] = "Very Good" + else: + recommendation_record["roof_energy_eff_ending"] = "Good" else: recommendation_record["roof_energy_eff_ending"] = "Very Good" else: @@ -682,9 +686,16 @@ class Property: percentage_of_roof = photo_supply_matched["photo_supply_median"].mean() percentage_of_roof = percentage_of_roof / 100 - self.solar_pv_roof_area = ( + self.solar_pv_percentage = percentage_of_roof + + def get_solar_pv_roof_area(self, percentage_of_roof): + """ + Given a percentage of the roof, this method will return the estimated area of the solar panels + :param percentage_of_roof: + :return: + """ + + return ( self.insulation_floor_area * percentage_of_roof if self.roof["is_flat"] else self.pitched_roof_area * percentage_of_roof ) - - self.solar_pv_percentage = percentage_of_roof diff --git a/backend/ml_models/Valuation.py b/backend/ml_models/Valuation.py index ff771252..83c20bb4 100644 --- a/backend/ml_models/Valuation.py +++ b/backend/ml_models/Valuation.py @@ -51,13 +51,14 @@ class PropertyValuation: KNIGHT_FRANK_MAPPING = [ {"start": "D", "end": "C", "increase_percentage": 0.03}, {"start": "D", "end": "B", "increase_percentage": 0.088}, + {"start": "D", "end": "A", "increase_percentage": 0.088}, ] NATIONWIDE_MAPPING = [ - {"start": "G", "end": "D", "increase_percentage": 0.035}, - {"start": "F", "end": "D", "increase_percentage": 0.035}, - {"start": "D", "end": "B", "increase_percentage": 0.017}, - {"start": "D", "end": "A", "increase_percentage": 0.017}, + # {"start": "G", "end": "D", "increase_percentage": 0.035}, + # {"start": "F", "end": "D", "increase_percentage": 0.035}, + # {"start": "D", "end": "B", "increase_percentage": 0.017}, + # {"start": "D", "end": "A", "increase_percentage": 0.017}, ] EPC_BANDS = ["G", "F", "E", "D", "C", "B", "A"] diff --git a/etl/testing_data/sap_model_simulation.py b/etl/testing_data/sap_model_simulation.py index 467b50db..726c2428 100644 --- a/etl/testing_data/sap_model_simulation.py +++ b/etl/testing_data/sap_model_simulation.py @@ -6,7 +6,7 @@ from utils.s3 import read_dataframe_from_s3_parquet, save_data_to_s3, save_dataf from backend.Property import Property # This is the github pr number -MODEL_VERSION = "100" +MODEL_VERSION = "101" def app(): @@ -590,6 +590,22 @@ def app(): # Let's use the API to find exactly the record from backend.SearchEpc import SearchEpc + + testing_model_api = ModelApi(portfolio_id="simulation-testing-loft-example", timestamp=created_at) + testing_model_api.MODEL_PREFIXES = ["sap_change_predictions"] + + ############################################################################################################ + # TODO:! + # Findings: + # 1) For uprn 10009320092, the number of rooms and number of heated rooms has changed and can change from + # epc to epc. We should therefore include a starting and ending value for this + # 2) Maybe we should include tenure??? Owner occupied homes are going to be a lot more unusual. Both investigation + # 2 and 3 were owner occupied + # 3) Maybe we should treat photo_supply missing differently than 0? + + ################################################################################################ + # Investigation 1) + searcher = SearchEpc( address1="2 Darkfield Way", postcode="TA7 8HY", @@ -609,15 +625,6 @@ def app(): if v != older_epc[k]: differences[k] = (v, older_epc[k]) - testing_model_api = ModelApi(portfolio_id="simulation-testing-loft-example", timestamp=created_at) - testing_model_api.MODEL_PREFIXES = ["sap_change_predictions"] - - ############################################################################################################ - # TODO:! - # Findings: 1) For uprn 10009320092, the number of rooms and number of heated rooms has changed and can change from - # epc to epc. We should therefore include a starting and ending value for this - - # Investigation 1) testing_row = insulation_200mm_starting[insulation_200mm_starting["uprn"] == "10009320092"].copy() testing_row["id"] = "testing-200mm-loft-insulation-starting-baseline+recommendation_id_baseline" testing_row["recommendation_id"] = "recommendation_id_baseline" @@ -652,3 +659,1490 @@ def app(): pred_df_5_rooms = prediction_5_rooms["sap_change_predictions"] impact_5_rooms = pred_df_5_rooms["predictions"].values[0] - testing_row_5_rooms["sap_starting"].values[0] + + ################################################################################################ + # Investigation 2 + + searcher = SearchEpc( + address1="19 Rossal Place", + postcode="MK12 6JE", + auth_token="a2Nvbm5rb3dsZXNzYXJAZ21haWwuY29tOjY5MGJiMWM0NmIyOGI5ZDUxYzAxMzQzYzNiZGNlZGJjZDNmODQwMzA=", + os_api_key="" + ) + searcher.uprn = "25006966" + searcher.find_property(skip_os=True) + + newest_epc = searcher.newest_epc + older_epc = [epc for epc in searcher.older_epcs if + epc["lmk-key"] == "fe23917ac59fbf3a608c76431941011ab4c7938546a432fc6212182caab31d73"][0] + # Iterate through the keys in the newest_epc and find the values in older epc that are different to the newest epc + + differences = {} + for k, v in newest_epc.items(): + if v != older_epc[k]: + differences[k] = (v, older_epc[k]) + + testing_row2 = insulation_200mm_starting[insulation_200mm_starting["uprn"] == "25006966"].copy() + # THERE IS NOTHING CLEAR THAT IS CHANGING IN THIS RECORD THAT INDICATES SUGGESTS WE'RE MISSING INFORMATION + + ################################################################################################ + # Investigation 3 + + insulation_200mm_starting[ + insulation_200mm_starting["rdsap_change"] == insulation_200mm_starting["rdsap_change"].max() + ]["uprn"].values[0] + # This UPRN: 100060350521 + + searcher = SearchEpc( + address1="138 Nicholas Crescent", + postcode="PO15 5AN", + auth_token="a2Nvbm5rb3dsZXNzYXJAZ21haWwuY29tOjY5MGJiMWM0NmIyOGI5ZDUxYzAxMzQzYzNiZGNlZGJjZDNmODQwMzA=", + os_api_key="" + ) + searcher.uprn = "100060350521" + searcher.find_property(skip_os=True) + + newest_epc = searcher.newest_epc + older_epc = [epc for epc in searcher.older_epcs if + epc["lmk-key"] == "9c4059762189b451191c98d2ef980a5364b8b7e0be3f064f681abcd4a0da681b"][0] + # Iterate through the keys in the newest_epc and find the values in older epc that are different to the newest epc + + differences = {} + for k, v in newest_epc.items(): + if v != older_epc[k]: + differences[k] = (v, older_epc[k]) + + # Nothing looks amiss about this record - let's score it in the model + testing_row_3 = insulation_200mm_starting[insulation_200mm_starting["uprn"] == "100060350521"].copy() + + testing_row_3["id"] = "testing-200mm-loft-insulation-starting-baseline+recommendation_id_baseline" + testing_row_3["recommendation_id"] = "recommendation_id_baseline" + # The testing row has 4 rooms + # Score in the model to see what we get + + baseline_prediction3 = testing_model_api.predict_all( + df=testing_row_3, + bucket="retrofit-data-dev", + prediction_buckets={ + "sap_change_predictions": "retrofit-sap-predictions-dev", + } + ) + + baseline_pred_df3 = baseline_prediction3["sap_change_predictions"] + impact3 = baseline_pred_df3["predictions"].values[0] - testing_row_3["sap_starting"].values[0] + + # TODO: Look at some of the example properties we have, and test using the model to score the impact of the + # different measures when multiple measures are scored together e.g. cavity + loft together vs individually + + # Look at performance on loft only rows + loft_insulation_training_data["id"] = (loft_insulation_training_data["uprn"] + + "_loft_insulation + recommendation_id_placeholder") + + loft_only_predictions = [] + loft_to_loop_over = range(0, loft_insulation_training_data.shape[0], 400) + for chunk in tqdm(loft_to_loop_over, total=len(loft_to_loop_over)): + loft_insulation_predictions_dict = model_api.predict_all( + df=loft_insulation_training_data.iloc[chunk:chunk + 400], + bucket="retrofit-data-dev", + prediction_buckets={ + "sap_change_predictions": "retrofit-sap-predictions-dev", + } + ) + + loft_only_predictions.append(loft_insulation_predictions_dict["sap_change_predictions"]) + + loft_only_predictions = pd.concat(loft_only_predictions) + + def calculate_mape(actuals, predictions): + """ + Calculate the Mean Absolute Percentage Error (MAPE). + + Parameters: + - actuals: A list or array of actual values. + - predictions: A list or array of predicted values, corresponding to actuals. + + Returns: + - mape: The MAPE score as a float. + + Note: This function assumes actuals and predictions are of the same length and + does not contain zeros in the actuals (to avoid division by zero). + """ + # Convert inputs to numpy arrays for vectorized operations + import numpy as np + actuals = np.array(actuals) + predictions = np.array(predictions) + + # Calculate the absolute percentage errors + ape = np.abs((actuals - predictions) / actuals) * 100 + + # Calculate the mean of these percentage errors + mape = np.mean(ape) + + return mape + + calculate_mape(actuals=loft_insulation_training_data["sap_ending"], + predictions=loft_only_predictions["predictions"]) + + comparison_df = pd.DataFrame( + { + "sap_starting": loft_insulation_training_data["sap_starting"].values, + "actual_sap_ending": loft_insulation_training_data["sap_ending"].values, + "predicted_sap_ending": loft_only_predictions["predictions"].values + } + ) + + comparison_df["residual"] = abs(comparison_df["actual_sap_ending"] - comparison_df["predicted_sap_ending"]) + comparison_df["residual"].describe() + + comparison_df[comparison_df["residual"] == comparison_df["residual"].max()] + + # TODO: Test scoring separately vs combined + from etl.epc.Record import EPCRecord + from etl.solar.SolarPhotoSupply import SolarPhotoSupply + from utils.s3 import read_from_s3 + import msgpack + from recommendations.Recommendations import Recommendations + import datetime + from numpy import nan + cleaning_data = read_dataframe_from_s3_parquet( + bucket_name="retrofit-data-dev", file_key="sap_change_model/cleaning_dataset.parquet", + ) + uprn_filenames = read_dataframe_from_s3_parquet( + bucket_name="retrofit-data-dev", file_key="spatial/filename_meta.parquet" + ) + photo_supply_lookup, floor_area_decile_thresholds = SolarPhotoSupply.load(bucket="retrofit-data-dev") + cleaned = read_from_s3( + s3_file_name="cleaned_epc_data/cleaned.bson", + bucket_name="retrofit-data-dev".format(environment="retrofit-data-dev") + ) + cleaned = msgpack.unpackb(cleaned, raw=False) + materials = [ + {'id': 17, 'type': 'mechanical_ventilation', 'description': 'Mechanical Extract Ventilation', 'depth': None, + 'depth_unit': None, 'cost': 500, 'cost_unit': 'gbp_per_unit', 'r_value_per_mm': None, 'r_value_unit': None, + 'thermal_conductivity': None, 'thermal_conductivity_unit': None, 'link': None, + 'created_at': datetime.datetime(2023, 10, 18, 16, 39, 9, 827188), 'is_active': True, + 'prime_material_cost': None, 'material_cost': None, 'labour_cost': None, 'labour_hours_per_unit': None, + 'plant_cost': None, 'total_cost': None, 'notes': None}, + {'id': 1221, 'type': 'flat_roof_preparation', 'description': 'clean surface to receive new damp-proof membrane', + 'depth': 0.0, 'depth_unit': None, 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': nan, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': None, + 'thermal_conductivity_unit': None, 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 12, 4, 20, 1, 49, 298076), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 0.0, 'labour_cost': 4.36, 'labour_hours_per_unit': 0.14, + 'plant_cost': 0.0, 'total_cost': 4.36, + 'notes': 'This data is based on concrete however forms a decent baseline for a Bituminous Felt flat roof'}, + {'id': 1223, 'type': 'flat_roof_preparation', + 'description': 'One coat primer; on wood surfaces before fixing; General surfaces; over 300 mm girth', + 'depth': 0.0, 'depth_unit': None, 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': nan, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': None, + 'thermal_conductivity_unit': None, 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 12, 4, 20, 1, 49, 298076), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 2.49, 'labour_cost': 1.5, 'labour_hours_per_unit': 0.08, + 'plant_cost': 0.0, 'total_cost': 3.99, 'notes': 'SPONs data gives us a baseline for a wood surface'}, + {'id': 1224, 'type': 'flat_roof_vapour_barrier', 'description': 'Visqueen High Performance Vapour Barrier', + 'depth': 0.0, 'depth_unit': None, 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': nan, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': None, + 'thermal_conductivity_unit': None, 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 12, 4, 20, 1, 49, 298076), 'is_active': True, + 'prime_material_cost': 0.58, 'material_cost': 1.21, 'labour_cost': 0.48, 'labour_hours_per_unit': 0.02, + 'plant_cost': 0.0, 'total_cost': 1.69, 'notes': None}, + {'id': 1226, 'type': 'flat_roof_insulation', 'description': 'Ravatherm XPS × 500 SL', 'depth': 100.0, + 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.03125, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.032, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 12, 4, 20, 1, 49, 298076), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 22.14, 'labour_cost': 10.66, 'labour_hours_per_unit': 0.48, + 'plant_cost': 0.0, 'total_cost': 32.8, 'notes': None}, + {'id': 1227, 'type': 'flat_roof_insulation', 'description': 'Ravatherm XPS × 500 SL', 'depth': 120.0, + 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.03125, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.032, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', + 'link': 'https://www.panelsystems.co.uk/product/floormate-ravatherm-sb?attribute_pa_group=floormate-500a' + '&attribute_pa_product-name=ravatherm-xps-x-500-sl&attribute_pa_length=1250&attribute_pa_width=600' + '&attribute_pa_thickness=120&attribute_pa_unit-of-sale=pack-3-brds&attribute_pa_min-order-qty=10' + '&gclid=CjwKCAiAjrarBhAWEiwA2qWdCKJK2iqlzUZ-mBFOfCLy2f5TldAbOj7G3LrvYw5JLaigplJAajzYpRoCtB8QAvD_BwE', + 'created_at': datetime.datetime(2023, 12, 4, 20, 1, 49, 298076), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 26.187656, 'labour_cost': 10.66, 'labour_hours_per_unit': 0.48, + 'plant_cost': 0.0, 'total_cost': 36.847656, + 'notes': "SPONs didn't have this thickness, so the material price is based on the fact that on the link, " + "the 120mm thickness is 18% more expensive per board than the 100mm thickness"}, + {'id': 1228, 'type': 'flat_roof_insulation', 'description': 'Ravatherm XPS × 500 SL', 'depth': 140.0, + 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.03125, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.032, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', + 'link': 'https://www.panelsystems.co.uk/product/floormate-ravatherm-sb?attribute_pa_group=floormate-500a' + '&attribute_pa_product-name=ravatherm-xps-x-500-sl&attribute_pa_length=1250&attribute_pa_width=600' + '&attribute_pa_thickness=120&attribute_pa_unit-of-sale=pack-3-brds&attribute_pa_min-order-qty=10' + '&gclid=CjwKCAiAjrarBhAWEiwA2qWdCKJK2iqlzUZ-mBFOfCLy2f5TldAbOj7G3LrvYw5JLaigplJAajzYpRoCtB8QAvD_BwE', + 'created_at': datetime.datetime(2023, 12, 4, 20, 1, 49, 298076), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 31.114737, 'labour_cost': 10.66, 'labour_hours_per_unit': 0.48, + 'plant_cost': 0.0, 'total_cost': 41.77474, + 'notes': "SPONs didn't have this thickness, so the material price is based on the fact that on the link, " + "the 140mm thickness is 40% more expensive per board than the 100mm thickness"}, + {'id': 1229, 'type': 'flat_roof_insulation', 'description': 'Foamglas T3+ Flat Roof Insulation', 'depth': 100.0, + 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.027777778, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.036, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 12, 4, 20, 1, 49, 298076), 'is_active': True, + 'prime_material_cost': 95.83, 'material_cost': 109.09, 'labour_cost': 30.7, 'labour_hours_per_unit': 1.3, + 'plant_cost': 0.0, 'total_cost': 139.79, 'notes': None}, + {'id': 1230, 'type': 'flat_roof_insulation', 'description': 'Foamglas T4+ Flat Roof Insulation', 'depth': 100.0, + 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.024390243, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.041, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 12, 4, 20, 1, 49, 298076), 'is_active': True, + 'prime_material_cost': 63.89, 'material_cost': 76.19, 'labour_cost': 28.34, 'labour_hours_per_unit': 1.2, + 'plant_cost': 0.0, 'total_cost': 104.53, 'notes': None}, {'id': 1231, 'type': 'flat_roof_insulation', + 'description': 'Ecotherm Eco-Versal General ' + 'Purpose Insulation Board', + 'depth': 100.0, 'depth_unit': 'mm', 'cost': None, + 'cost_unit': 'gbp_per_m2', + 'r_value_per_mm': 0.045454547, + 'r_value_unit': 'square_meter_kelvin_per_watt', + 'thermal_conductivity': 0.022, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', + 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 12, 4, 20, 1, + 49, 298076), + 'is_active': True, 'prime_material_cost': 15.12, + 'material_cost': 25.96, 'labour_cost': 30.7, + 'labour_hours_per_unit': 1.3, 'plant_cost': 0.0, + 'total_cost': 56.66, 'notes': None}, + {'id': 1232, 'type': 'flat_roof_insulation', + 'description': 'Ecotherm Eco-Versal General Purpose Insulation Board', 'depth': 120.0, 'depth_unit': 'mm', + 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.045454547, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.022, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 12, 4, 20, 1, 49, 298076), 'is_active': True, + 'prime_material_cost': 20.16, 'material_cost': 34.613335, 'labour_cost': 30.7, 'labour_hours_per_unit': 1.3, + 'plant_cost': 0.0, 'total_cost': 65.31333, + 'notes': "SPONs didn't have this thickness, so the material price is based on the fact that on the link, " + "the 120mm thickness is 33% more expensive than the 100mm thickness"}, + {'id': 1233, 'type': 'flat_roof_insulation', + 'description': 'Ecotherm Eco-Versal General Purpose Insulation Board', 'depth': 150.0, 'depth_unit': 'mm', + 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.045454547, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.022, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 12, 4, 20, 1, 49, 298076), 'is_active': True, + 'prime_material_cost': 23.53, 'material_cost': 34.62, 'labour_cost': 33.06, 'labour_hours_per_unit': 1.4, + 'plant_cost': 0.0, 'total_cost': 67.68, 'notes': None}, {'id': 1234, 'type': 'flat_roof_waterproofing', + 'description': '20 mm thick two coat coverings; ' + 'felt isolating membrane; to ' + 'concrete (or timber) base; flat or ' + 'to falls or slopes not exceeding ' + '10° from horizontal', + 'depth': 0.0, 'depth_unit': None, 'cost': None, + 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': nan, + 'r_value_unit': 'square_meter_kelvin_per_watt', + 'thermal_conductivity': None, + 'thermal_conductivity_unit': None, 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 12, 4, 20, 1, + 49, 298076), + 'is_active': True, 'prime_material_cost': None, + 'material_cost': 0.0, 'labour_cost': 0.0, + 'labour_hours_per_unit': 0.5, 'plant_cost': 0.0, + 'total_cost': 31.13, 'notes': None}, + {'id': 1225, 'type': 'flat_roof_insulation', + 'description': 'Kingspan Thermaroof TR21 zero OPD urethene insulation board', 'depth': 100.0, + 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.04, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.025, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 12, 4, 20, 1, 49, 298076), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 50.95, 'labour_cost': 10.66, 'labour_hours_per_unit': 0.48, + 'plant_cost': 0.0, 'total_cost': 61.61, + 'notes': "SPONs didn't have a labour hours so we use 0.48 which is similar to other materials"}, + {'id': 1235, 'type': 'windows_glazing', + 'description': 'uPVC windows; Profile 22 or other equal and approved; reinforced where appropriate with ' + 'aluminium alloy; in refurbishment work, including standard ironmongery; sills and factory ' + 'glazed with low-e 24 mm double glazing; removing existing windows and fixing new in ' + 'position; including lugs plugged and screwed to brickwork or blockwork; Casement/fixed ' + 'light; including vents; e.p.d.m. glazing gaskets and weather seals; 1770 mm × 1200 mm; ref ' + 'P312WW', + 'depth': 0, 'depth_unit': None, 'cost': None, 'cost_unit': 'gbp_per_unit', 'r_value_per_mm': None, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': None, + 'thermal_conductivity_unit': None, 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 12, 20, 14, 37, 51, 728866), 'is_active': True, + 'prime_material_cost': 176.55, 'material_cost': 182.25, 'labour_cost': 163.36, 'labour_hours_per_unit': 6.5, + 'plant_cost': 0.0, 'total_cost': 345.61, + 'notes': 'This is the cost of removal of existing windows and installation of new windows. This is a ' + 'casement style window, which is the most common but also the cheapest style. In the cost ' + 'estimation framework, we can inflate prices for different finishes, to be conservative on price. '}, + {'id': 1109, 'type': 'cavity_wall_insulation', + 'description': 'Expanded Polystyrene Beads cavity wall insulation', 'depth': 75.0, 'depth_unit': 'mm', + 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.030303031, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.033, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', + 'link': 'https://www.styrene.co.uk/downloads/Datasheets' + '/Stylite_Cavity_Loose_Fill_Insulation_Datasheet_v20211.pdf', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 18.875, 'labour_cost': 1.125, 'labour_hours_per_unit': 0.065, + 'plant_cost': 0.0, 'total_cost': 20.0, + 'notes': "It is hard to find materials online. To price this, we've used this article: " + "https://www.greenmatch.co.uk/blog/cavity-wall-insulation-cost It puts EPS beads at around £22 per " + "meter squared, blowing wool insulation at £18 per meter squared and Polyurethane Foam at £26 per " + "meter squared, when taking the most pessimistic prices. These rates have been used to adjust the " + "price of the mineral wool insulation to give us the other forms of insulation"}, + {'id': 1110, 'type': 'cavity_wall_insulation', + 'description': 'Injected Polyurthane Foam cavity wall insulation', 'depth': 75.0, 'depth_unit': 'mm', + 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.030303031, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.033, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', + 'link': 'https://www.foaminstall.co.uk/wp-content/uploads/2017/04/Lapolla-Cavity-Fill-BBA-certificate-sheet1' + '.pdf', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 22.875, 'labour_cost': 1.125, 'labour_hours_per_unit': 0.065, + 'plant_cost': 0.0, 'total_cost': 24.0, 'notes': None}, + {'id': 1111, 'type': 'loft_insulation', 'description': 'Crown Loft Roll 44 glass fibre roll', 'depth': 100.0, + 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.022727273, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.044, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 2.03, 'material_cost': 2.1, 'labour_cost': 1.56, 'labour_hours_per_unit': 0.09, + 'plant_cost': 0.0, 'total_cost': 3.66, 'notes': None}, + {'id': 1112, 'type': 'loft_insulation', 'description': 'Crown Loft Roll 44 glass fibre roll', 'depth': 150.0, + 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.022727273, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.044, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 3.06, 'material_cost': 3.16, 'labour_cost': 1.78, 'labour_hours_per_unit': 0.1, + 'plant_cost': 0.0, 'total_cost': 4.94, 'notes': None}, + {'id': 1113, 'type': 'loft_insulation', 'description': 'Crown Loft Roll 44 glass fibre roll', 'depth': 170.0, + 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.022727273, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.044, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', + 'link': 'https://insulation4less.co.uk/products/knauf-170mm-combi-cut?variant=31671561257013&dfw_tracker' + '=77750-31671561257013&utm_source=google&utm_medium=shopping&utm_campaign=shoptimised&gad_source=1' + '&gclid=CjwKCAiAx_GqBhBQEiwAlDNAZi1LiTWKVn0W1vktOYAPPQU3hss5Tq2qNn6GNhodCQoRD_tvqCLdxhoCKnIQAvD_BwE', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 3.81938, 'labour_cost': 1.71304, 'labour_hours_per_unit': 0.11, + 'plant_cost': 0.0, 'total_cost': 5.53242, + 'notes': "We don't have a 170mm in SPONs so the material cost is based on the fact that the 170mm insulation " + "is 87.4% of the cost of the 200mm insulation"}, + {'id': 1114, 'type': 'loft_insulation', 'description': 'Crown Loft Roll 44 glass fibre roll', 'depth': 200.0, + 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.022727273, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.044, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 4.25, 'material_cost': 4.37, 'labour_cost': 1.96, 'labour_hours_per_unit': 0.11, + 'plant_cost': 0.0, 'total_cost': 6.33, 'notes': None}, + {'id': 1115, 'type': 'loft_insulation', 'description': 'Crown Loft Roll 44 glass fibre roll', 'depth': 270.0, + 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.022727273, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.044, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 5.91938, 'labour_cost': 1.96, 'labour_hours_per_unit': 0.11, + 'plant_cost': 0.0, 'total_cost': 7.87938, 'notes': 'This is the 100mm product + the 170mm product'}, + {'id': 1116, 'type': 'loft_insulation', 'description': 'Crown Loft Roll 44 glass fibre roll', 'depth': 300.0, + 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.022727273, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.044, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 6.47, 'labour_cost': 1.96, 'labour_hours_per_unit': 0.11, + 'plant_cost': 0.0, 'total_cost': 8.43, 'notes': 'This is the 100mm product + the 200mm product'}, + {'id': 1117, 'type': 'loft_insulation', 'description': 'Isover Mineral Wool Modular Roll', 'depth': 100.0, + 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.023255814, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.043, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 1.99, 'material_cost': 2.05, 'labour_cost': 1.6, 'labour_hours_per_unit': 0.09, + 'plant_cost': 0.0, 'total_cost': 3.65, 'notes': None}, + {'id': 1118, 'type': 'loft_insulation', 'description': 'Isover Mineral Wool Modular Roll', 'depth': 150.0, + 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.023255814, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.043, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 2.96, 'material_cost': 3.05, 'labour_cost': 1.78, 'labour_hours_per_unit': 0.1, + 'plant_cost': 0.0, 'total_cost': 4.83, 'notes': None}, + {'id': 1119, 'type': 'loft_insulation', 'description': 'Isover Mineral Wool Modular Roll', 'depth': 170.0, + 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.023255814, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.043, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', + 'link': 'https://flooringwarehousedirect.co.uk/product/isover-spacesaver-roll-170mm-x-1160mm-x-7-03m-8-15m2/', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 3.8706238, 'labour_cost': 2.281361, + 'labour_hours_per_unit': 0.12816635, 'plant_cost': 0.0, 'total_cost': 6.1519847, + 'notes': "We don't have a 170mm in SPONs so the material cost is based on the fact that the 170mm insulation " + "is 85.4% of the cost of the 200mm insulation"}, + {'id': 1120, 'type': 'loft_insulation', 'description': 'Isover Mineral Wool Modular Roll', 'depth': 200.0, + 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.023255814, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.043, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 4.4, 'material_cost': 4.53, 'labour_cost': 2.67, 'labour_hours_per_unit': 0.15, + 'plant_cost': 0.0, 'total_cost': 7.2, 'notes': None}, + {'id': 1121, 'type': 'loft_insulation', 'description': 'Isover Mineral Wool Modular Roll', 'depth': 270.0, + 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.023255814, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.043, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 5.920624, 'labour_cost': 2.67, 'labour_hours_per_unit': 0.15, + 'plant_cost': 0.0, 'total_cost': 8.590624, 'notes': 'This is the 100mm product + the 170mm product'}, + {'id': 1122, 'type': 'loft_insulation', 'description': 'Isover Mineral Wool Modular Roll', 'depth': 300.0, + 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.023255814, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.043, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 6.58, 'labour_cost': 2.67, 'labour_hours_per_unit': 0.15, + 'plant_cost': 0.0, 'total_cost': 9.25, 'notes': 'This is the 100mm product + the 200mm product'}, + {'id': 1123, 'type': 'loft_insulation', 'description': 'Isover Acoustic Partition Roll', 'depth': 100.0, + 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.023255814, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.043, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 5.93, 'material_cost': 6.4, 'labour_cost': 2.67, 'labour_hours_per_unit': 0.15, + 'plant_cost': 0.0, 'total_cost': 9.07, 'notes': 'This provides acoustic insulation as well'}, + {'id': 1124, 'type': 'loft_insulation', 'description': 'Isover Acoustic Partition Roll', 'depth': 300.0, + 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.023255814, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.043, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 17.79, 'material_cost': 19.2, 'labour_cost': 2.67, 'labour_hours_per_unit': 0.15, + 'plant_cost': 0.0, 'total_cost': 21.87, 'notes': 'This provides acoustic insulation as well'}, + {'id': 1125, 'type': 'loft_insulation', 'description': 'Thermafleece EcoRoll Insulation', 'depth': 300.0, + 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.025641026, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.039, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 24.78, 'labour_cost': 2.67, 'labour_hours_per_unit': 0.15, + 'plant_cost': 0.0, 'total_cost': 27.45, + 'notes': 'This material is based on installing 3 layers of the 100mm product'}, + {'id': 1126, 'type': 'loft_insulation', 'description': 'Thermafleece EcoRoll Insulation', 'depth': 280.0, + 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.025641026, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.039, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 23.36, 'labour_cost': 3.12, 'labour_hours_per_unit': 0.18, + 'plant_cost': 0.0, 'total_cost': 26.48, + 'notes': 'This material is based on installed 2 layers of the 140mm product'}, + {'id': 1127, 'type': 'iwi_wall_demolition', + 'description': 'Solid & Dry Lined walls: Hack of wall finishes with chipping hammer; plaster to walls.', + 'depth': 0.0, 'depth_unit': None, 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': nan, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': None, + 'thermal_conductivity_unit': None, 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 0.0, 'labour_cost': 10.27, 'labour_hours_per_unit': 0.33, + 'plant_cost': 1.28, 'total_cost': 11.55, 'notes': None}, {'id': 1128, 'type': 'iwi_wall_demolition', + 'description': 'Stud walls: Remove wall linings ' + 'including battening behind; ' + 'plasterboard and skim', + 'depth': 0.0, 'depth_unit': None, 'cost': None, + 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': nan, + 'r_value_unit': 'square_meter_kelvin_per_watt', + 'thermal_conductivity': None, + 'thermal_conductivity_unit': None, 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, + 12, 244907), + 'is_active': True, 'prime_material_cost': None, + 'material_cost': 0.0, 'labour_cost': 6.23, + 'labour_hours_per_unit': 0.2, 'plant_cost': 1.25, + 'total_cost': 7.48, 'notes': None}, + {'id': 1129, 'type': 'iwi_wall_demolition', + 'description': 'Lathe and Plaster walls: Remove wall linings including battening behind; wood lath and ' + 'plaster', + 'depth': 0.0, 'depth_unit': None, 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': nan, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': None, + 'thermal_conductivity_unit': None, 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 0.0, 'labour_cost': 6.85, 'labour_hours_per_unit': 0.22, + 'plant_cost': 2.09, 'total_cost': 8.94, 'notes': None}, + {'id': 1130, 'type': 'internal_wall_insulation', 'description': 'Foamglas Grade F Wall Insulation Slabs', + 'depth': 60.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.02631579, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.038, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 41.69, 'material_cost': 53.33, 'labour_cost': 29.52, 'labour_hours_per_unit': 1.25, + 'plant_cost': 0.0, 'total_cost': 82.85, 'notes': None}, + {'id': 1131, 'type': 'internal_wall_insulation', 'description': 'Foamglas Grade F Wall Insulation Slabs', + 'depth': 100.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.02631579, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.038, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 86.86, 'material_cost': 99.85, 'labour_cost': 29.52, 'labour_hours_per_unit': 1.25, + 'plant_cost': 0.0, 'total_cost': 129.37, 'notes': None}, + {'id': 1132, 'type': 'internal_wall_insulation', 'description': 'Foamglas Grade F Wall Insulation Slabs', + 'depth': 150.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.02631579, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.038, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 130.29, 'material_cost': 144.58, 'labour_cost': 29.52, 'labour_hours_per_unit': 1.25, + 'plant_cost': 0.0, 'total_cost': 174.1, 'notes': None}, + {'id': 1133, 'type': 'internal_wall_insulation', 'description': 'Ecotherm Eco-Versal PIR Insulation Board', + 'depth': 30.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.045454547, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.022, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 6.16, 'material_cost': 16.73, 'labour_cost': 28.34, 'labour_hours_per_unit': 1.2, + 'plant_cost': 0.0, 'total_cost': 45.07, 'notes': None}, + {'id': 1134, 'type': 'internal_wall_insulation', 'description': 'Ecotherm Eco-Versal PIR Insulation Board', + 'depth': 50.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.045454547, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.022, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 8.46, 'material_cost': 19.1, 'labour_cost': 28.34, 'labour_hours_per_unit': 1.2, + 'plant_cost': 0.0, 'total_cost': 47.44, 'notes': None}, + {'id': 1135, 'type': 'internal_wall_insulation', 'description': 'Ecotherm Eco-Versal PIR Insulation Board', + 'depth': 100.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.045454547, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.022, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 15.12, 'material_cost': 25.96, 'labour_cost': 30.7, 'labour_hours_per_unit': 1.3, + 'plant_cost': 0.0, 'total_cost': 56.66, 'notes': None}, + {'id': 1136, 'type': 'internal_wall_insulation', 'description': 'Kingspan Kooltherm K18 insulated plasterboard', + 'depth': 37.5, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.04761905, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.021, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 26.86, 'labour_cost': 5.21, 'labour_hours_per_unit': 0.23, + 'plant_cost': 0.0, 'total_cost': 32.07, 'notes': None}, + {'id': 1137, 'type': 'internal_wall_insulation', 'description': 'Kingspan Kooltherm K18 insulated plasterboard', + 'depth': 42.5, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.04761905, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.021, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 17.37, 'labour_cost': 5.21, 'labour_hours_per_unit': 0.23, + 'plant_cost': 0.0, 'total_cost': 22.58, 'notes': None}, + {'id': 1138, 'type': 'internal_wall_insulation', 'description': 'Kingspan Kooltherm K18 insulated plasterboard', + 'depth': 52.5, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.04761905, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.021, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 21.74, 'labour_cost': 5.79, 'labour_hours_per_unit': 0.25, + 'plant_cost': 0.0, 'total_cost': 27.53, 'notes': None}, + {'id': 1139, 'type': 'internal_wall_insulation', 'description': 'Kingspan Kooltherm K18 insulated plasterboard', + 'depth': 62.5, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.04761905, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.021, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 19.3, 'labour_cost': 5.79, 'labour_hours_per_unit': 0.25, + 'plant_cost': 0.0, 'total_cost': 25.09, 'notes': None}, + {'id': 1140, 'type': 'internal_wall_insulation', 'description': 'Kingspan Kooltherm K18 insulated plasterboard', + 'depth': 72.5, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.04761905, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.021, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 23.15, 'labour_cost': 5.79, 'labour_hours_per_unit': 0.25, + 'plant_cost': 0.0, 'total_cost': 28.94, 'notes': None}, + {'id': 1141, 'type': 'iwi_vapour_barrier', 'description': 'Visqueen High Performance Vapour Barrier', + 'depth': 0.0, 'depth_unit': None, 'cost': None, 'cost_unit': None, 'r_value_per_mm': nan, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': None, + 'thermal_conductivity_unit': None, 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 0.58, 'material_cost': 1.21, 'labour_cost': 0.48, 'labour_hours_per_unit': 0.02, + 'plant_cost': 0.0, 'total_cost': 1.69, 'notes': None}, {'id': 1142, 'type': 'iwi_redecoration', + 'description': 'Plaster; one coat Thistle board ' + 'finish or other equal; steel ' + 'trowelled; 3 mm thick work to walls ' + 'or ceilings; one coat; to ' + 'plasterboard base; over 600mm wide', + 'depth': 0.0, 'depth_unit': None, 'cost': None, + 'cost_unit': None, 'r_value_per_mm': nan, + 'r_value_unit': 'square_meter_kelvin_per_watt', + 'thermal_conductivity': None, + 'thermal_conductivity_unit': None, 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, + 12, 244907), + 'is_active': True, 'prime_material_cost': None, + 'material_cost': 0.06, 'labour_cost': 6.58, + 'labour_hours_per_unit': 0.25, 'plant_cost': 0.0, + 'total_cost': 6.64, 'notes': None}, + {'id': 1143, 'type': 'iwi_redecoration', + 'description': 'Two coats emulsion paint on plaster, over 40mm girth; 3.5m - 5m high', 'depth': 0.0, + 'depth_unit': None, 'cost': None, 'cost_unit': None, 'r_value_per_mm': nan, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': None, + 'thermal_conductivity_unit': None, 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 0.41, 'labour_cost': 3.93, 'labour_hours_per_unit': 0.21, + 'plant_cost': 0.0, 'total_cost': 4.34, 'notes': None}, {'id': 1144, 'type': 'iwi_redecoration', + 'description': 'Fitting existing softwood skirting ' + 'or architrave to new frames; 150mm ' + 'high', + 'depth': 0.0, 'depth_unit': None, 'cost': None, + 'cost_unit': None, 'r_value_per_mm': nan, + 'r_value_unit': 'square_meter_kelvin_per_watt', + 'thermal_conductivity': None, + 'thermal_conductivity_unit': None, 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, + 12, 244907), + 'is_active': True, 'prime_material_cost': None, + 'material_cost': 0.01, 'labour_cost': 4.87, + 'labour_hours_per_unit': 0.12, 'plant_cost': 0.0, + 'total_cost': 4.88, 'notes': None}, + {'id': 1145, 'type': 'suspended_floor_demolition', 'description': 'Removal of carpet and underfelt', + 'depth': 0.0, 'depth_unit': None, 'cost': None, 'cost_unit': None, 'r_value_per_mm': nan, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': None, + 'thermal_conductivity_unit': None, 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 0.0, 'labour_cost': 3.32, 'labour_hours_per_unit': 0.11, + 'plant_cost': 0.0, 'total_cost': 3.32, + 'notes': 'We ignore the plant cost that is in SPONs because we assume the carpet is not scrapped and ' + 'therefore there is no need for a skip'}, + {'id': 1146, 'type': 'suspended_floor_demolition', + 'description': 'Remove boarding; withdraw nails; set aside for reuse; ground level', 'depth': 0.0, + 'depth_unit': None, 'cost': None, 'cost_unit': None, 'r_value_per_mm': nan, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': None, + 'thermal_conductivity_unit': None, 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 0.0, 'labour_cost': 9.34, 'labour_hours_per_unit': 0.3, + 'plant_cost': 0.0, 'total_cost': 9.34, 'notes': None}, {'id': 1147, 'type': 'suspended_floor_vapour_barrier', + 'description': 'Visqueen High Performance Vapour ' + 'Barrier', + 'depth': 0.0, 'depth_unit': None, 'cost': None, + 'cost_unit': None, 'r_value_per_mm': nan, + 'r_value_unit': 'square_meter_kelvin_per_watt', + 'thermal_conductivity': None, + 'thermal_conductivity_unit': None, 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, + 12, 244907), + 'is_active': True, 'prime_material_cost': 0.58, + 'material_cost': 1.21, 'labour_cost': 0.48, + 'labour_hours_per_unit': 0.02, 'plant_cost': 0.0, + 'total_cost': 1.69, 'notes': None}, + {'id': 1148, 'type': 'suspended_floor_insulation', 'description': 'Thermafleece CosyWool Roll', 'depth': 50.0, + 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.025641026, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.039, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 4.24, 'labour_cost': 1.56, 'labour_hours_per_unit': 0.09, + 'plant_cost': 0.0, 'total_cost': 5.8, + 'notes': 'Spons did not contain labour costs so we use values for similar insulations. We use the same ' + 'values as in Crown loft roll 44, since it is also an insulation roll'}, + {'id': 1149, 'type': 'suspended_floor_insulation', 'description': 'Thermafleece CosyWool Roll', 'depth': 75.0, + 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.025641026, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.039, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 6.31, 'labour_cost': 1.56, 'labour_hours_per_unit': 0.09, + 'plant_cost': 0.0, 'total_cost': 7.87, + 'notes': 'Spons did not contain labour costs so we use values for similar insulations. We use the same ' + 'values as in Crown loft roll 44, since it is also an insulation roll'}, + {'id': 1150, 'type': 'suspended_floor_insulation', 'description': 'Thermafleece CosyWool Roll', 'depth': 100.0, + 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.025641026, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.039, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 8.26, 'labour_cost': 1.56, 'labour_hours_per_unit': 0.1, + 'plant_cost': 0.0, 'total_cost': 9.82, + 'notes': 'Spons did not contain labour costs so we use values for similar insulations. We use the same ' + 'values as in Crown loft roll 44, since it is also an insulation roll'}, + {'id': 1151, 'type': 'suspended_floor_insulation', 'description': 'Thermafleece CosyWool Roll', 'depth': 140.0, + 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.025641026, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.039, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 11.68, 'labour_cost': 1.78, 'labour_hours_per_unit': 0.1, + 'plant_cost': 0.0, 'total_cost': 13.46, + 'notes': 'Spons did not contain labour costs so we use values for similar insulations. We use the same ' + 'values as in Crown loft roll 44, since it is also an insulation roll'}, + {'id': 1152, 'type': 'suspended_floor_insulation', + 'description': 'Thermafleece TF35 high density wool insulating batts', 'depth': 50.0, 'depth_unit': 'mm', + 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.028571429, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.035, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 6.63, 'labour_cost': 1.56, 'labour_hours_per_unit': 0.09, + 'plant_cost': 0.0, 'total_cost': 8.19, + 'notes': 'Spons did not contain labour costs so we use values for similar insulations. We use the same ' + 'values as in Crown loft roll 44, since it is also an insulation roll'}, + {'id': 1153, 'type': 'suspended_floor_insulation', + 'description': 'Thermafleece TF35 high density wool insulating batts', 'depth': 75.0, 'depth_unit': 'mm', + 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.028571429, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.035, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 10.31, 'labour_cost': 1.56, 'labour_hours_per_unit': 0.09, + 'plant_cost': 0.0, 'total_cost': 11.87, + 'notes': 'Spons did not contain labour costs so we use values for similar insulations. We use the same ' + 'values as in Crown loft roll 44, since it is also an insulation roll'}, + {'id': 1154, 'type': 'suspended_floor_insulation', + 'description': 'Ecotherm Eco-Versal General Purpose Insulation Board', 'depth': 30.0, 'depth_unit': 'mm', + 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.045454547, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.022, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 6.16, 'material_cost': 16.73, 'labour_cost': 28.34, 'labour_hours_per_unit': 1.2, + 'plant_cost': 0.0, 'total_cost': 45.07, 'notes': None}, {'id': 1155, 'type': 'suspended_floor_insulation', + 'description': 'Ecotherm Eco-Versal General Purpose ' + 'Insulation Board', + 'depth': 50.0, 'depth_unit': 'mm', 'cost': None, + 'cost_unit': 'gbp_per_m2', + 'r_value_per_mm': 0.045454547, + 'r_value_unit': 'square_meter_kelvin_per_watt', + 'thermal_conductivity': 0.022, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', + 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, + 12, 244907), + 'is_active': True, 'prime_material_cost': 8.46, + 'material_cost': 19.1, 'labour_cost': 28.34, + 'labour_hours_per_unit': 1.2, 'plant_cost': 0.0, + 'total_cost': 47.44, 'notes': None}, + {'id': 1156, 'type': 'suspended_floor_insulation', + 'description': 'Ecotherm Eco-Versal General Purpose Insulation Board', 'depth': 100.0, 'depth_unit': 'mm', + 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.045454547, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.022, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 15.12, 'material_cost': 25.96, 'labour_cost': 30.7, 'labour_hours_per_unit': 1.3, + 'plant_cost': 0.0, 'total_cost': 56.66, 'notes': None}, {'id': 1157, 'type': 'suspended_floor_insulation', + 'description': 'Ecotherm Eco-Versal General Purpose ' + 'Insulation Board', + 'depth': 150.0, 'depth_unit': 'mm', 'cost': None, + 'cost_unit': 'gbp_per_m2', + 'r_value_per_mm': 0.045454547, + 'r_value_unit': 'square_meter_kelvin_per_watt', + 'thermal_conductivity': 0.022, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', + 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, + 12, 244907), + 'is_active': True, 'prime_material_cost': 23.53, + 'material_cost': 34.62, 'labour_cost': 33.06, + 'labour_hours_per_unit': 1.4, 'plant_cost': 0.0, + 'total_cost': 67.68, 'notes': None}, + {'id': 1158, 'type': 'suspended_floor_insulation', 'description': 'Crown Loft Roll 44 glass fibre roll', + 'depth': 100.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_unit', 'r_value_per_mm': 0.022727273, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.044, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 2.03, 'material_cost': 2.1, 'labour_cost': 1.56, 'labour_hours_per_unit': 0.09, + 'plant_cost': 0.0, 'total_cost': 3.66, 'notes': None}, + {'id': 1159, 'type': 'suspended_floor_insulation', 'description': 'Crown Loft Roll 44 glass fibre roll', + 'depth': 150.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_unit', 'r_value_per_mm': 0.022727273, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.044, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 3.06, 'material_cost': 3.16, 'labour_cost': 1.78, 'labour_hours_per_unit': 0.1, + 'plant_cost': 0.0, 'total_cost': 4.94, 'notes': None}, + {'id': 1160, 'type': 'suspended_floor_insulation', 'description': 'Crown Loft Roll 44 glass fibre roll', + 'depth': 200.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_unit', 'r_value_per_mm': 0.022727273, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.044, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 4.25, 'material_cost': 4.37, 'labour_cost': 1.96, 'labour_hours_per_unit': 0.11, + 'plant_cost': 0.0, 'total_cost': 6.33, 'notes': None}, + {'id': 1161, 'type': 'suspended_floor_insulation', 'description': 'Isover Mineral Wool Modular Roll', + 'depth': 100.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_unit', 'r_value_per_mm': 0.023255814, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.043, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 1.99, 'material_cost': 2.05, 'labour_cost': 1.6, 'labour_hours_per_unit': 0.09, + 'plant_cost': 0.0, 'total_cost': 3.65, 'notes': None}, + {'id': 1162, 'type': 'suspended_floor_insulation', 'description': 'Isover Mineral Wool Modular Roll', + 'depth': 150.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_unit', 'r_value_per_mm': 0.023255814, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.043, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 2.96, 'material_cost': 3.05, 'labour_cost': 1.78, 'labour_hours_per_unit': 0.1, + 'plant_cost': 0.0, 'total_cost': 4.83, 'notes': None}, + {'id': 1163, 'type': 'suspended_floor_insulation', 'description': 'Isover Mineral Wool Modular Roll', + 'depth': 200.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_unit', 'r_value_per_mm': 0.023255814, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.043, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 4.4, 'material_cost': 4.53, 'labour_cost': 2.67, 'labour_hours_per_unit': 0.15, + 'plant_cost': 0.0, 'total_cost': 7.2, 'notes': None}, + {'id': 1164, 'type': 'suspended_floor_insulation', 'description': 'Isover Acoustic Partition Roll', + 'depth': 25.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_unit', 'r_value_per_mm': 0.025641026, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.039, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 1.67, 'material_cost': 2.01, 'labour_cost': 1.43, 'labour_hours_per_unit': 0.08, + 'plant_cost': 0.0, 'total_cost': 3.44, 'notes': None}, + {'id': 1165, 'type': 'suspended_floor_insulation', 'description': 'Isover Acoustic Partition Roll', + 'depth': 50.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_unit', 'r_value_per_mm': 0.025641026, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.039, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 2.74, 'material_cost': 3.11, 'labour_cost': 1.6, 'labour_hours_per_unit': 0.09, + 'plant_cost': 0.0, 'total_cost': 4.71, 'notes': None}, + {'id': 1166, 'type': 'suspended_floor_insulation', 'description': 'Isover Acoustic Partition Roll', + 'depth': 75.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_unit', 'r_value_per_mm': 0.023255814, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.043, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 4.57, 'material_cost': 5.01, 'labour_cost': 1.78, 'labour_hours_per_unit': 0.1, + 'plant_cost': 0.0, 'total_cost': 6.79, 'notes': None}, + {'id': 1167, 'type': 'suspended_floor_insulation', 'description': 'Isover Acoustic Partition Roll', + 'depth': 100.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_unit', 'r_value_per_mm': 0.023255814, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.043, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 5.93, 'material_cost': 6.4, 'labour_cost': 2.67, 'labour_hours_per_unit': 0.15, + 'plant_cost': 0.0, 'total_cost': 9.07, 'notes': None}, + {'id': 1168, 'type': 'suspended_floor_insulation', 'description': 'Kay-Cel Expanded Polystyrene Board', + 'depth': 25.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.030303031, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.033, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 3.88, 'labour_cost': 3.24, 'labour_hours_per_unit': 0.14, + 'plant_cost': 0.0, 'total_cost': 7.12, 'notes': None}, + {'id': 1169, 'type': 'suspended_floor_insulation', 'description': 'Kay-Cel Expanded Polystyrene Board', + 'depth': 50.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.030303031, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.033, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 6.62, 'labour_cost': 3.71, 'labour_hours_per_unit': 0.16, + 'plant_cost': 0.0, 'total_cost': 10.33, 'notes': None}, + {'id': 1170, 'type': 'suspended_floor_insulation', 'description': 'Kay-Cel Expanded Polystyrene Board', + 'depth': 75.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.030303031, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.033, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 9.3, 'labour_cost': 4.17, 'labour_hours_per_unit': 0.18, + 'plant_cost': 0.0, 'total_cost': 13.47, 'notes': None}, + {'id': 1171, 'type': 'suspended_floor_insulation', 'description': 'Kay-Cel Expanded Polystyrene Board', + 'depth': 100.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.030303031, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.033, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 12.02, 'labour_cost': 4.4, 'labour_hours_per_unit': 0.19, + 'plant_cost': 0.0, 'total_cost': 16.42, 'notes': None}, {'id': 1172, 'type': 'suspended_floor_insulation', + 'description': 'Kingspan Thermafloor TF70 High ' + 'Performance Rigid Floor Insulation', + 'depth': 50.0, 'depth_unit': 'mm', 'cost': None, + 'cost_unit': 'gbp_per_m2', + 'r_value_per_mm': 0.045454547, + 'r_value_unit': 'square_meter_kelvin_per_watt', + 'thermal_conductivity': 0.022, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', + 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, + 12, 244907), + 'is_active': True, 'prime_material_cost': None, + 'material_cost': 10.36, 'labour_cost': 4.06, + 'labour_hours_per_unit': 0.18, 'plant_cost': 0.0, + 'total_cost': 14.42, 'notes': None}, + {'id': 1173, 'type': 'suspended_floor_insulation', + 'description': 'Kingspan Thermafloor TF70 High Performance Rigid Floor Insulation', 'depth': 75.0, + 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.045454547, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.022, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 15.35, 'labour_cost': 4.06, 'labour_hours_per_unit': 0.18, + 'plant_cost': 0.0, 'total_cost': 19.41, 'notes': None}, {'id': 1174, 'type': 'suspended_floor_insulation', + 'description': 'Kingspan Thermafloor TF70 High ' + 'Performance Rigid Floor Insulation', + 'depth': 100.0, 'depth_unit': 'mm', 'cost': None, + 'cost_unit': 'gbp_per_m2', + 'r_value_per_mm': 0.045454547, + 'r_value_unit': 'square_meter_kelvin_per_watt', + 'thermal_conductivity': 0.022, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', + 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, + 12, 244907), + 'is_active': True, 'prime_material_cost': None, + 'material_cost': 19.17, 'labour_cost': 4.06, + 'labour_hours_per_unit': 0.18, 'plant_cost': 0.0, + 'total_cost': 23.23, 'notes': None}, + {'id': 1175, 'type': 'suspended_floor_insulation', + 'description': 'Kingspan Thermafloor TF70 High Performance Rigid Floor Insulation', 'depth': 125.0, + 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.045454547, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.022, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 26.59, 'labour_cost': 4.06, 'labour_hours_per_unit': 0.18, + 'plant_cost': 0.0, 'total_cost': 30.65, 'notes': None}, {'id': 1176, 'type': 'suspended_floor_insulation', + 'description': 'Kingspan Thermafloor TF70 High ' + 'Performance Rigid Floor Insulation', + 'depth': 150.0, 'depth_unit': 'mm', 'cost': None, + 'cost_unit': 'gbp_per_m2', + 'r_value_per_mm': 0.045454547, + 'r_value_unit': 'square_meter_kelvin_per_watt', + 'thermal_conductivity': 0.022, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', + 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, + 12, 244907), + 'is_active': True, 'prime_material_cost': None, + 'material_cost': 31.13, 'labour_cost': 4.64, + 'labour_hours_per_unit': 0.2, 'plant_cost': 0.0, + 'total_cost': 35.77, 'notes': None}, + {'id': 1177, 'type': 'suspended_floor_redecoration', 'description': 'refix floorboards previously set aside', + 'depth': 0.0, 'depth_unit': None, 'cost': None, 'cost_unit': None, 'r_value_per_mm': nan, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': None, + 'thermal_conductivity_unit': None, 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 1.54, 'labour_cost': 24.98, 'labour_hours_per_unit': 0.74, + 'plant_cost': 0.0, 'total_cost': 26.52, 'notes': None}, + {'id': 1178, 'type': 'suspended_floor_redecoration', 'description': 'Fitting carpet', 'depth': 0.0, + 'depth_unit': None, 'cost': None, 'cost_unit': None, 'r_value_per_mm': nan, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': None, + 'thermal_conductivity_unit': None, 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 0.0, 'labour_cost': 6.59, 'labour_hours_per_unit': 0.37, + 'plant_cost': 0.0, 'total_cost': 6.59, + 'notes': 'SPONs does not have data on re-fitting the carpet so we use the data in Fitted carpeting; Gradus ' + 'woven polypropylene tufted loop\n\n as a baseline. We assume re-use of carpets, therefore we need ' + 'just labour rates'}, + {'id': 1179, 'type': 'solid_floor_demolition', 'description': 'Removal of carpet and underfelt', 'depth': 0.0, + 'depth_unit': None, 'cost': None, 'cost_unit': None, 'r_value_per_mm': nan, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': None, + 'thermal_conductivity_unit': None, 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 0.0, 'labour_cost': 3.32, 'labour_hours_per_unit': 0.11, + 'plant_cost': 0.0, 'total_cost': 3.32, + 'notes': 'We ignore the plant cost that is in SPONs because we assume the carpet is not scrapped and ' + 'therefore there is no need for a skip'}, + {'id': 1180, 'type': 'solid_floor_preparation', + 'description': 'clean surface of concrete to receive new damp-proof membrane', 'depth': 0.0, + 'depth_unit': None, 'cost': None, 'cost_unit': None, 'r_value_per_mm': nan, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': None, + 'thermal_conductivity_unit': None, 'link': None, + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 0.0, 'labour_cost': 4.36, 'labour_hours_per_unit': 0.14, + 'plant_cost': 0.0, 'total_cost': 4.36, 'notes': None}, {'id': 1181, 'type': 'solid_floor_preparation', + 'description': 'Clean out crack to form a 20mm×20mm ' + 'groove and fill with cement: mortar ' + 'mixed with bonding agent', + 'depth': 0.0, 'depth_unit': None, 'cost': None, + 'cost_unit': None, 'r_value_per_mm': nan, + 'r_value_unit': 'square_meter_kelvin_per_watt', + 'thermal_conductivity': None, + 'thermal_conductivity_unit': None, 'link': None, + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, + 12, 244907), + 'is_active': True, 'prime_material_cost': None, + 'material_cost': 6.91, 'labour_cost': 18.99, + 'labour_hours_per_unit': 0.61, 'plant_cost': 0.16, + 'total_cost': 26.06, + 'notes': 'This step is the assessment and repair of ' + 'any damage to the concrete floor such as ' + 'filling cracks or levelling uneven areas'}, + {'id': 1182, 'type': 'solid_floor_vapour_barrier', 'description': 'Visqueen High Performance Vapour Barrier', + 'depth': 0.0, 'depth_unit': None, 'cost': None, 'cost_unit': None, 'r_value_per_mm': nan, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': None, + 'thermal_conductivity_unit': None, 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 0.58, 'material_cost': 1.21, 'labour_cost': 0.48, 'labour_hours_per_unit': 0.02, + 'plant_cost': 0.0, 'total_cost': 1.69, 'notes': None}, + {'id': 1183, 'type': 'solid_floor_insulation', 'description': 'Kay-Cel Expanded Polystyrene Board', + 'depth': 25.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.030303031, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.033, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 3.88, 'labour_cost': 3.24, 'labour_hours_per_unit': 0.14, + 'plant_cost': 0.0, 'total_cost': 7.12, 'notes': None}, + {'id': 1184, 'type': 'solid_floor_insulation', 'description': 'Kay-Cel Expanded Polystyrene Board', + 'depth': 50.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.030303031, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.033, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 6.62, 'labour_cost': 3.71, 'labour_hours_per_unit': 0.16, + 'plant_cost': 0.0, 'total_cost': 10.33, 'notes': None}, + {'id': 1185, 'type': 'solid_floor_insulation', 'description': 'Kay-Cel Expanded Polystyrene Board', + 'depth': 75.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.030303031, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.033, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 9.3, 'labour_cost': 4.17, 'labour_hours_per_unit': 0.18, + 'plant_cost': 0.0, 'total_cost': 13.47, 'notes': None}, {'id': 1186, 'type': 'solid_floor_insulation', + 'description': 'Kingspan Thermafloor TF70 High ' + 'Performance Rigid Floor Insulation', + 'depth': 50.0, 'depth_unit': 'mm', 'cost': None, + 'cost_unit': 'gbp_per_m2', + 'r_value_per_mm': 0.045454547, + 'r_value_unit': 'square_meter_kelvin_per_watt', + 'thermal_conductivity': 0.022, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', + 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, + 12, 244907), + 'is_active': True, 'prime_material_cost': None, + 'material_cost': 10.36, 'labour_cost': 4.06, + 'labour_hours_per_unit': 0.18, 'plant_cost': 0.0, + 'total_cost': 14.42, 'notes': None}, + {'id': 1187, 'type': 'solid_floor_insulation', + 'description': 'Kingspan Thermafloor TF70 High Performance Rigid Floor Insulation', 'depth': 75.0, + 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.045454547, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.022, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 15.35, 'labour_cost': 4.06, 'labour_hours_per_unit': 0.18, + 'plant_cost': 0.0, 'total_cost': 19.41, 'notes': None}, {'id': 1188, 'type': 'solid_floor_insulation', + 'description': 'Ecotherm Eco-Versal General Purpose ' + 'Insulation Board', + 'depth': 30.0, 'depth_unit': 'mm', 'cost': None, + 'cost_unit': 'gbp_per_m2', + 'r_value_per_mm': 0.045454547, + 'r_value_unit': 'square_meter_kelvin_per_watt', + 'thermal_conductivity': 0.022, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', + 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, + 12, 244907), + 'is_active': True, 'prime_material_cost': 6.16, + 'material_cost': 16.73, 'labour_cost': 28.34, + 'labour_hours_per_unit': 1.2, 'plant_cost': 0.0, + 'total_cost': 45.07, 'notes': None}, + {'id': 1189, 'type': 'solid_floor_insulation', + 'description': 'Ecotherm Eco-Versal General Purpose Insulation Board', 'depth': 50.0, 'depth_unit': 'mm', + 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.045454547, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.022, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 8.46, 'material_cost': 19.1, 'labour_cost': 28.34, 'labour_hours_per_unit': 1.2, + 'plant_cost': 0.0, 'total_cost': 47.44, 'notes': None}, {'id': 1190, 'type': 'solid_floor_insulation', + 'description': 'Ecotherm Eco-Versal General Purpose ' + 'Insulation Board', + 'depth': 60.0, 'depth_unit': 'mm', 'cost': None, + 'cost_unit': 'gbp_per_m2', + 'r_value_per_mm': 0.045454547, + 'r_value_unit': 'square_meter_kelvin_per_watt', + 'thermal_conductivity': 0.022, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', + 'link': 'https://londonbuildingsupplies.co.uk/products/60mm--ecotherm-eco-versal-general-purpose-pir-insulation-board---2.4m-x-1.2m-x-60mm.html', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, + 12, 244907), + 'is_active': True, 'prime_material_cost': None, + 'material_cost': 24.081198, 'labour_cost': 28.34, + 'labour_hours_per_unit': 1.2, 'plant_cost': 0.0, + 'total_cost': 52.421196, + 'notes': "This material isn't in SPONs but checking" + " online, is around 92% of the cost of the" + " 100mm"}, + {'id': 1191, 'type': 'solid_floor_insulation', + 'description': 'Ecotherm Eco-Versal General Purpose Insulation Board', 'depth': 70.0, 'depth_unit': 'mm', + 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.045454547, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.022, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', + 'link': 'https://londonbuildingsupplies.co.uk/products/70mm--ecotherm-eco-versal-general-purpose-pir' + '-insulation-board---2.4m-x-1.2m-x-70mm.html', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 27.089088, 'labour_cost': 28.34, 'labour_hours_per_unit': 1.2, + 'plant_cost': 0.0, 'total_cost': 55.42909, + 'notes': "This material isn't in SPONs but checking online, is around 104% of the cost of the 100mm (more " + "expensive than 100mm)"}, + {'id': 1192, 'type': 'solid_floor_insulation', + 'description': 'Ecotherm Eco-Versal General Purpose Insulation Board', 'depth': 100.0, 'depth_unit': 'mm', + 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.045454547, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.022, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 15.12, 'material_cost': 25.96, 'labour_cost': 30.7, 'labour_hours_per_unit': 1.3, + 'plant_cost': 0.0, 'total_cost': 56.66, 'notes': None}, + {'id': 1193, 'type': 'solid_floor_insulation', 'description': 'Ravatherm XPS X 500 SL Polystyrene Foam', + 'depth': 50.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.032258064, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.031, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 11.07, 'labour_cost': 10.66, 'labour_hours_per_unit': 0.46, + 'plant_cost': 0.0, 'total_cost': 21.73, + 'notes': "In Spons, the thermal conductivity is 0.033 however the datasheet indicates it's 0.32: " + "https://ravagobuildingsolutions.com/uk/wp-content/uploads/sites/30/2022/08/ravatherm-xps-x-500-sl" + "-tds-version-1-20210901.pdf"}, + {'id': 1194, 'type': 'solid_floor_insulation', 'description': 'Ravatherm XPS X 500 SL Polystyrene Foam', + 'depth': 75.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.03125, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.032, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 16.28, 'labour_cost': 10.66, 'labour_hours_per_unit': 0.46, + 'plant_cost': 0.0, 'total_cost': 26.94, 'notes': None}, {'id': 1195, 'type': 'solid_floor_redecoration', + 'description': 'Screeded beds; protection to ' + 'compressible formwork exceeding ' + '600mm wide', + 'depth': 0.0, 'depth_unit': None, 'cost': None, + 'cost_unit': None, 'r_value_per_mm': nan, + 'r_value_unit': 'square_meter_kelvin_per_watt', + 'thermal_conductivity': None, + 'thermal_conductivity_unit': None, 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, + 12, 244907), + 'is_active': True, 'prime_material_cost': 9.6, + 'material_cost': 9.89, 'labour_cost': 2.67, + 'labour_hours_per_unit': 0.15, 'plant_cost': 0.0, + 'total_cost': 12.56, + 'notes': 'This is the screed layer, placed on top ' + 'of the insulation'}, + {'id': 1196, 'type': 'solid_floor_redecoration', 'description': 'Fitting carpet', 'depth': 0.0, + 'depth_unit': None, 'cost': None, 'cost_unit': None, 'r_value_per_mm': nan, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': None, + 'thermal_conductivity_unit': None, 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 0.0, 'labour_cost': 6.59, 'labour_hours_per_unit': 0.37, + 'plant_cost': 0.0, 'total_cost': 6.59, + 'notes': 'SPONs does not have data on re-fitting the carpet so we use the data in Fitted carpeting; Gradus ' + 'woven polypropylene tufted loop\n\n as a baseline. We assume re-use of carpets, therefore we need ' + 'just labour rates'}, + {'id': 1197, 'type': 'solid_floor_redecoration', + 'description': 'Fitting existing softwood skirting or architrave to new frames; 150mm high', 'depth': 0.0, + 'depth_unit': None, 'cost': None, 'cost_unit': None, 'r_value_per_mm': nan, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': None, + 'thermal_conductivity_unit': None, 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 0.01, 'labour_cost': 4.87, 'labour_hours_per_unit': 0.12, + 'plant_cost': 0.0, 'total_cost': 4.88, 'notes': None}, {'id': 1198, 'type': 'ewi_wall_demolition', + 'description': 'Solid & Dry Lined walls: Hack of ' + 'wall finishes with chipping hammer; ' + 'plaster to walls.', + 'depth': 0.0, 'depth_unit': None, 'cost': None, + 'cost_unit': None, 'r_value_per_mm': nan, + 'r_value_unit': 'square_meter_kelvin_per_watt', + 'thermal_conductivity': None, + 'thermal_conductivity_unit': None, 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, + 12, 244907), + 'is_active': True, 'prime_material_cost': None, + 'material_cost': 0.0, 'labour_cost': 10.27, + 'labour_hours_per_unit': 0.33, 'plant_cost': 1.28, + 'total_cost': 11.55, 'notes': None}, + {'id': 1199, 'type': 'ewi_wall_demolition', + 'description': 'Stud walls: Remove wall linings including battening behind; plasterboard and skim', + 'depth': 0.0, 'depth_unit': None, 'cost': None, 'cost_unit': None, 'r_value_per_mm': nan, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': None, + 'thermal_conductivity_unit': None, 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 0.0, 'labour_cost': 6.23, 'labour_hours_per_unit': 0.2, + 'plant_cost': 1.25, 'total_cost': 7.48, 'notes': None}, {'id': 1200, 'type': 'ewi_wall_demolition', + 'description': 'Lathe and Plaster walls: Remove ' + 'wall linings including battening ' + 'behind; wood lath and plaster', + 'depth': 0.0, 'depth_unit': None, 'cost': None, + 'cost_unit': None, 'r_value_per_mm': nan, + 'r_value_unit': 'square_meter_kelvin_per_watt', + 'thermal_conductivity': None, + 'thermal_conductivity_unit': None, 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, + 12, 244907), + 'is_active': True, 'prime_material_cost': None, + 'material_cost': 0.0, 'labour_cost': 6.85, + 'labour_hours_per_unit': 0.22, 'plant_cost': 2.09, + 'total_cost': 8.94, 'notes': None}, + {'id': 1201, 'type': 'ewi_wall_preparation', + 'description': 'Clean and prepare surfaces, one coat Keim dilution, one coat primer and two coats of Keim ' + 'Ecosil paint; Brick or block walls; over 300 mm girth', + 'depth': 0.0, 'depth_unit': None, 'cost': None, 'cost_unit': None, 'r_value_per_mm': nan, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': None, + 'thermal_conductivity_unit': None, 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 7.3, 'labour_cost': 5.62, 'labour_hours_per_unit': 0.3, + 'plant_cost': 0.0, 'total_cost': 12.92, + 'notes': 'This work covers the preparation and priming of the wall before insulating'}, + {'id': 1202, 'type': 'external_wall_insulation', 'description': 'Ecotherm Eco-Versal PIR Insulation Board', + 'depth': 30.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.045454547, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.022, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 6.16, 'material_cost': 16.73, 'labour_cost': 28.34, 'labour_hours_per_unit': 1.2, + 'plant_cost': 0.0, 'total_cost': 45.07, 'notes': None}, + {'id': 1203, 'type': 'external_wall_insulation', 'description': 'Ecotherm Eco-Versal PIR Insulation Board', + 'depth': 50.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.045454547, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.022, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 8.46, 'material_cost': 19.1, 'labour_cost': 28.34, 'labour_hours_per_unit': 1.2, + 'plant_cost': 0.0, 'total_cost': 47.44, 'notes': None}, + {'id': 1204, 'type': 'external_wall_insulation', 'description': 'Ecotherm Eco-Versal PIR Insulation Board', + 'depth': 100.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.045454547, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.022, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 15.12, 'material_cost': 25.96, 'labour_cost': 30.7, 'labour_hours_per_unit': 1.3, + 'plant_cost': 0.0, 'total_cost': 56.66, 'notes': None}, + {'id': 1205, 'type': 'external_wall_insulation', 'description': 'Ecotherm Eco-Versal PIR Insulation Board', + 'depth': 150.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.045454547, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.022, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 23.53, 'material_cost': 34.62, 'labour_cost': 33.06, 'labour_hours_per_unit': 1.4, + 'plant_cost': 0.0, 'total_cost': 67.68, 'notes': None}, + {'id': 1206, 'type': 'external_wall_insulation', 'description': 'Foamglas Grade F Wall Insulation Slabs', + 'depth': 60.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.02631579, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.038, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 41.69, 'material_cost': 53.33, 'labour_cost': 29.52, 'labour_hours_per_unit': 1.25, + 'plant_cost': 0.0, 'total_cost': 82.85, 'notes': None}, + {'id': 1207, 'type': 'external_wall_insulation', 'description': 'Foamglas Grade F Wall Insulation Slabs', + 'depth': 100.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.02631579, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.038, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 86.86, 'material_cost': 99.85, 'labour_cost': 29.52, 'labour_hours_per_unit': 1.25, + 'plant_cost': 0.0, 'total_cost': 129.37, 'notes': None}, + {'id': 1208, 'type': 'external_wall_insulation', 'description': 'Foamglas Grade F Wall Insulation Slabs', + 'depth': 150.0, 'depth_unit': 'mm', 'cost': None, 'cost_unit': 'gbp_per_m2', 'r_value_per_mm': 0.02631579, + 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.038, + 'thermal_conductivity_unit': 'watt_per_meter_kelvin', 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': 130.29, 'material_cost': 144.58, 'labour_cost': 29.52, 'labour_hours_per_unit': 1.25, + 'plant_cost': 0.0, 'total_cost': 174.1, 'notes': None}, {'id': 1209, 'type': 'ewi_wall_redecoration', + 'description': 'EPS insulation fixed with adhesive ' + 'to SFS structure (measured ' + 'separately) with horizontal PVC ' + 'intermediate track and vertical ' + 'T-spines; with glassfibre mesh ' + 'reinforcement embedded in Sto Armat ' + 'Classic Basecoat Render and Stolit ' + 'K 1.5 Decorative Topcoat Render (' + 'white)', + 'depth': 0.0, 'depth_unit': None, 'cost': None, + 'cost_unit': None, 'r_value_per_mm': nan, + 'r_value_unit': 'square_meter_kelvin_per_watt', + 'thermal_conductivity': None, + 'thermal_conductivity_unit': None, 'link': 'SPONs', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, + 12, 244907), + 'is_active': True, 'prime_material_cost': None, + 'material_cost': 0.0, 'labour_cost': 0.0, + 'labour_hours_per_unit': 0.0, 'plant_cost': 0.0, + 'total_cost': 69.94, + 'notes': 'This material in SPONs is for 70mm EPS ' + 'insulation, which comes in at a cost of ' + '99.17 per meter square. This includes the ' + 'cost of insulation. To get the costing ' + 'for just the works and not the ' + 'insulation, we subtract the cost of EPS ' + 'insulation, using Ravathem 75mm ' + 'insulation as an example, which costs ' + '£29.23 per meter square, giving us the ' + 'cost of the remaining works without ' + 'insulation. This material gives us a cost ' + 'for basecoat, mesh application and a ' + 'render finish'}, + {'id': 1210, 'type': 'low_energy_lighting_installation', + 'description': 'Installation of fittings and cost of bub', 'depth': 0.0, 'depth_unit': None, 'cost': None, + 'cost_unit': 'gbp_per_unit', 'r_value_per_mm': nan, 'r_value_unit': 'square_meter_kelvin_per_watt', + 'thermal_conductivity': None, 'thermal_conductivity_unit': None, + 'link': 'https://www.checkatrade.com/blog/cost-guides/cost-install-downlights/ ' + 'https://www.hamuch.com/cost/led-spot-light#:~:text=It%20costs%20an%20average%20of,' + 'will%20drive%20up%20the%20cost.', + 'created_at': datetime.datetime(2023, 11, 28, 22, 49, 12, 244907), 'is_active': True, + 'prime_material_cost': None, 'material_cost': 20.0, 'labour_cost': 15.0, 'labour_hours_per_unit': 0.8, + 'plant_cost': 0.0, 'total_cost': 66.0, + 'notes': 'We estimate the unit economics from the checkatrade article. We assume that the average job ' + 'consists of installing 6 lights based on the hamuch article. We use the median value of 400 for a ' + 'job of 6 lights'}] + + testing_properties = [ + { + "address": "2 South Terrace", + "postcode": "NN1 5JY" + }, + { + "address": "8 Lindlings", + "postcode": "HP1 2HA", + }, + { + "address": "44 Lindlings", + "postcode": "HP1 2HE", + }, + { + "address": "46 Chaulden Terrace", + "postcode": "HP1 2AN", + }, + { + "address": "73 Long Chaulden", + "postcode": "HP1 2HX", + }, + { + "address": "77 Simmons Drive", + "postcode": "B32 1SL", + }, + { + "address": "139 School Road", + "postcode": "B28 8JF", + }, + ] + + testing_properties_results = [] + for testing_property_config in testing_properties: + + searcher = SearchEpc( + address1=testing_property_config["address"], + postcode=testing_property_config["postcode"], + auth_token="a2Nvbm5rb3dsZXNzYXJAZ21haWwuY29tOjY5MGJiMWM0NmIyOGI5ZDUxYzAxMzQzYzNiZGNlZGJjZDNmODQwMzA=", + os_api_key="" + ) + searcher.find_property(skip_os=True) + epc_records = { + 'original_epc': searcher.newest_epc.copy(), + 'full_sap_epc': searcher.full_sap_epc.copy(), + 'old_data': searcher.older_epcs.copy(), + } + + prepared_epc = EPCRecord( + epc_records=epc_records, + run_mode="newdata", + cleaning_data=cleaning_data + ) + + p = Property( + id=prepared_epc.uprn, + address=searcher.address_clean, + postcode=searcher.postcode_clean, + epc_record=prepared_epc, + ) + p.get_spatial_data(uprn_filenames) + p.get_components(cleaned, photo_supply_lookup, floor_area_decile_thresholds) + + recommender = Recommendations(property_instance=p, materials=materials) + recommender.recommend() + + wall_recommendations = recommender.wall_recomender.recommendations + loft_recommendations = recommender.roof_recommender.recommendations + floor_recommendations = recommender.floor_recommender.recommendations + solar_recommendations = recommender.solar_recommender.recommendation + + # TODO: TEMP! + solar_recommendations[0]["photo_supply"] = 50 + + # Take just the cavity wall and loft recommendations + p.create_base_difference_epc_record(cleaned_lookup=cleaned) + + wall_scoring_data = [] + for wall_rec in wall_recommendations: + recommendation_record = p.base_difference_record.df.to_dict("records")[0].copy() + scoring_dict = p.create_recommendation_scoring_data( + property_id=p.id, recommendation_record=recommendation_record, recommendation=wall_rec, + ) + wall_scoring_data.append(scoring_dict) + + roof_scoring_data = [] + for roof_rec in loft_recommendations: + recommendation_record = p.base_difference_record.df.to_dict("records")[0].copy() + scoring_dict = p.create_recommendation_scoring_data( + property_id=p.id, recommendation_record=recommendation_record, recommendation=roof_rec, + ) + roof_scoring_data.append(scoring_dict) + + floor_scoring_data = [] + for floor_rec in floor_recommendations: + recommendation_record = p.base_difference_record.df.to_dict("records")[0].copy() + scoring_dict = p.create_recommendation_scoring_data( + property_id=p.id, recommendation_record=recommendation_record, recommendation=floor_rec, + ) + floor_scoring_data.append(scoring_dict) + + solar_scoring_data = [] + for solar_rec in solar_recommendations: + recommendation_record = p.base_difference_record.df.to_dict("records")[0].copy() + scoring_dict = p.create_recommendation_scoring_data( + property_id=p.id, recommendation_record=recommendation_record, recommendation=solar_rec, + ) + solar_scoring_data.append(scoring_dict) + + # We now produce a combined, applying just the first roof recommendation to the first wall recommendation + + # Firstly apply the wall + starting_record = p.base_difference_record.df.to_dict("records")[0].copy() + scoring_dict_with_wall = p.create_recommendation_scoring_data( + property_id=p.id, + recommendation_record=starting_record.copy(), + recommendation=wall_recommendations[0], + ) if wall_recommendations else starting_record.copy() + + scoring_dict_with_wall_and_roof = p.create_recommendation_scoring_data( + property_id=p.id, + recommendation_record=scoring_dict_with_wall.copy(), + recommendation=loft_recommendations[0], + ) if loft_recommendations else scoring_dict_with_wall.copy() + + scoring_dict_with_wall_roof_floor = p.create_recommendation_scoring_data( + property_id=p.id, + recommendation_record=scoring_dict_with_wall_and_roof.copy(), + recommendation=floor_recommendations[0], + ) if floor_recommendations else scoring_dict_with_wall_and_roof.copy() + + scoring_dict_with_wall_roof_floor_solar = p.create_recommendation_scoring_data( + property_id=p.id, + recommendation_record=scoring_dict_with_wall_roof_floor.copy(), + recommendation=solar_recommendations[0], + ) if solar_recommendations else scoring_dict_with_wall_roof_floor.copy() + + # We score each dataset with the model + wall_only_predictions_dict = model_api.predict_all( + df=pd.DataFrame(wall_scoring_data), + bucket="retrofit-data-dev", + prediction_buckets={ + "sap_change_predictions": "retrofit-sap-predictions-dev", + } + ) if wall_scoring_data else {"sap_change_predictions": pd.DataFrame()} + + roof_only_predictions_dict = model_api.predict_all( + df=pd.DataFrame(roof_scoring_data), + bucket="retrofit-data-dev", + prediction_buckets={ + "sap_change_predictions": "retrofit-sap-predictions-dev", + } + ) if roof_scoring_data else {"sap_change_predictions": pd.DataFrame()} + + floor_only_predictions_dict = model_api.predict_all( + df=pd.DataFrame(floor_scoring_data), + bucket="retrofit-data-dev", + prediction_buckets={ + "sap_change_predictions": "retrofit-sap-predictions-dev", + } + ) if floor_scoring_data else {"sap_change_predictions": pd.DataFrame()} + + solar_only_predictions_dict = model_api.predict_all( + df=pd.DataFrame(solar_scoring_data), + bucket="retrofit-data-dev", + prediction_buckets={ + "sap_change_predictions": "retrofit-sap-predictions-dev", + } + ) if solar_scoring_data else {"sap_change_predictions": pd.DataFrame()} + + wall_roof_predictions_dict = model_api.predict_all( + df=pd.DataFrame([scoring_dict_with_wall_and_roof]), + bucket="retrofit-data-dev", + prediction_buckets={ + "sap_change_predictions": "retrofit-sap-predictions-dev", + } + ) + + wall_roof_floor_predictions_dict = model_api.predict_all( + df=pd.DataFrame([scoring_dict_with_wall_roof_floor]), + bucket="retrofit-data-dev", + prediction_buckets={ + "sap_change_predictions": "retrofit-sap-predictions-dev", + } + ) + + wall_roof_floor_solar_predictions_dict = model_api.predict_all( + df=pd.DataFrame([scoring_dict_with_wall_roof_floor_solar]), + bucket="retrofit-data-dev", + prediction_buckets={ + "sap_change_predictions": "retrofit-sap-predictions-dev", + } + ) + + wall_only_predictions = wall_only_predictions_dict["sap_change_predictions"].copy() + wall_only_predictions["type"] = "wall_only" + + roof_only_predictions = roof_only_predictions_dict["sap_change_predictions"].copy() + roof_only_predictions["type"] = "roof_only" + + floor_only_predictions = floor_only_predictions_dict["sap_change_predictions"].copy() + floor_only_predictions["type"] = "floor_only" + + solar_only_predictions = solar_only_predictions_dict["sap_change_predictions"].copy() + solar_only_predictions["type"] = "solar_only" + + wall_and_roof_predictions = wall_roof_predictions_dict["sap_change_predictions"].copy() + wall_and_roof_predictions["type"] = "wall_and_roof" + + wall_roof_floor_predictions = wall_roof_floor_predictions_dict["sap_change_predictions"].copy() + wall_roof_floor_predictions["type"] = "wall_roof_floor" + + wall_roof_floor_solar_predictions = wall_roof_floor_solar_predictions_dict["sap_change_predictions"].copy() + wall_roof_floor_solar_predictions["type"] = "wall_roof_floor_solar" + + # We collate the results + results = pd.concat( + [ + wall_only_predictions, + roof_only_predictions, + floor_only_predictions, + solar_only_predictions, + wall_and_roof_predictions, + wall_roof_floor_predictions, + wall_roof_floor_solar_predictions + ] + ) + results["gain"] = results["predictions"] - int(p.data["current-energy-efficiency"]) + results["address"] = p.address + + testing_properties_results.append(results) + + testing_properties_results = pd.concat(testing_properties_results) diff --git a/recommendations/Costs.py b/recommendations/Costs.py index 106f4453..dd1f1a21 100644 --- a/recommendations/Costs.py +++ b/recommendations/Costs.py @@ -37,6 +37,9 @@ MCS_SOLAR_PV_COST_DATA = { "average_cost_per_kwh-Northern Ireland": 2126.09, } +# This is based on quotes from installers +BATTERY_COST = 3500 + class Costs: """ @@ -835,7 +838,7 @@ class Costs: "labour_days": labour_days } - def solar_pv(self, wattage: float): + def solar_pv(self, wattage: float, has_battery: bool = False): """ Calculates the total cost for solar PV based data provided by the MCS dashboard, which contains @@ -847,8 +850,8 @@ class Costs: Price can also be benchmarked against this checkatrade article: https://www.checkatrade.com/blog/cost-guides/cost-of-solar-panel-installation/ - :param wattage: Peak wattage of the solar PV system - :return: + :param wattage: Peak wattage of the solar PV system] + :param has_battery: Bool, whether the system includes a battery """ # Get the cost data relevant to the region @@ -858,6 +861,11 @@ class Costs: total_cost = kw * regional_cost subtotal_before_vat = total_cost / (1 + self.VAT_RATE) + + if has_battery: + # The battery cost is based on the £3500 quote, recieved from installers + subtotal_before_vat += BATTERY_COST + vat = total_cost - subtotal_before_vat # Labour hours are based on estimates from online research but an average team seems to consist of 3 people diff --git a/recommendations/Recommendations.py b/recommendations/Recommendations.py index 0444006d..6ca7badb 100644 --- a/recommendations/Recommendations.py +++ b/recommendations/Recommendations.py @@ -44,6 +44,9 @@ class Recommendations: """ This method runs the recommendations for the individual measures and then appends them to a list for output + + The recommendations are implemented in order of suggested phase, from fabric first to heating systems, to + renewables. :return: """ diff --git a/recommendations/SolarPvRecommendations.py b/recommendations/SolarPvRecommendations.py index 8a773570..9995bbe1 100644 --- a/recommendations/SolarPvRecommendations.py +++ b/recommendations/SolarPvRecommendations.py @@ -39,17 +39,56 @@ class SolarPvRecommendations: if not is_valid_property_type or not is_valid_roof_type or not has_no_existing_solar_pv: return - # We now have a property which is potentially suitable for solar PV - number_solar_panels = np.floor(self.property.solar_pv_roof_area / self.SOLAR_PANEL_AREA) - solar_panel_wattage = number_solar_panels * self.SOLAR_PANEL_WATTAGE + # For the solar recommendations, we produce the following scenarios: + # 1) Solar panels only, we present a high, medium and low coverage + # 2) With and without battery + roof_coverage_scenarios = [ + self.property.solar_pv_percentage - 0.1, self.property.solar_pv_percentage, + self.property.solar_pv_percentage + 0.1 + ] + # We make sure we haven't gone too low or high + roof_coverage_scenarios = [v for v in roof_coverage_scenarios if 0 <= v <= 1] + battery_scenarios = [False, True] - roof_coverage_percent = round(self.property.solar_pv_percentage * 100) + # I now produce the cross product of the scenarios + scenarios = [(roof, battery) for roof in roof_coverage_scenarios for battery in battery_scenarios] - # Given the wattage, we estimate the cost of the solar PV system. This is based on the MCS database - # of solar PV installations - cost_result = self.costs.solar_pv(wattage=solar_panel_wattage) + for roof_coverage, has_battery in scenarios: + # We now have a property which is potentially suitable for solar PV + solar_pv_roof_area = self.property.get_solar_pv_roof_area(roof_coverage) - kw = np.floor(solar_panel_wattage / 100) / 10 + number_solar_panels = np.floor(solar_pv_roof_area / self.SOLAR_PANEL_AREA) + solar_panel_wattage = number_solar_panels * self.SOLAR_PANEL_WATTAGE + + roof_coverage_percent = round(roof_coverage * 100) + + # Given the wattage, we estimate the cost of the solar PV system. This is based on the MCS database + # of solar PV installations + cost_result = self.costs.solar_pv(wattage=solar_panel_wattage, has_battery=has_battery) + + kw = np.floor(solar_panel_wattage / 100) / 10 + + if has_battery: + description = (f"Install a {kw} kilowatt-peak (kWp) solar photovoltaic (PV) panel system on " + f"{round(roof_coverage_percent * 100)}% the roof, with a battery storage system.") + else: + description = (f"Install a {kw} kilowatt-peak (kWp) solar photovoltaic (PV) p" + f"anel system on {round(roof_coverage_percent * 100)}% the roof.") + + self.recommendation.append( + { + "parts": [], + "type": "solar_pv", + "description": description, + "starting_u_value": None, + "new_u_value": None, + "sap_points": None, + **cost_result, + # This is required for simulating the SAP impact. solar_pv_percentage is between 0 & 1 so we scale + # back up here + "photo_supply": 100 * scenario + } + ) self.recommendation = [ {