import json

import pandas as pd
from tqdm import tqdm
from utils.s3 import read_dataframe_from_s3_parquet, save_data_to_s3, save_dataframe_to_s3_parquet
from backend.Property import Property

# This is the github pr number
MODEL_VERSION = "101"


def app():
    dataset = read_dataframe_from_s3_parquet(
        bucket_name="retrofit-data-dev",
        file_key="sap_change_model/dataset.parquet"
    )

    thresholds = dataset["total_floor_area_starting"].quantile(
        [0.3, 0.6, 0.9]
    ).values

    dataset["floor_area_quantile"] = pd.cut(
        dataset["total_floor_area_starting"],
        bins=[0] + list(thresholds) + [float('inf')],
        labels=False,
        include_lowest=True
    )

    # We want to set up some tests to deduce the following:
    # For different property types, of various sizes, what is the impact of the various measures that we recommend
    # 1) Insulating the loft. We test the impact of bringing the loft to 270mm insulation and 300mm insulation
    property_types = dataset[
        ["property_type", "built_form", "floor_area_quantile", "construction_age_band"]
    ].drop_duplicates()

    property_types = property_types.sort_values(
        ["property_type", "built_form", "floor_area_quantile", "construction_age_band"]
    )

    # For each property type congifuration, we take an example property with different starting loft thresholds. We take
    # the value with the lowest U-value, since when simulating, we often work with particularly low u-values

    # TODOS
    # 1) When simulating with loft insulation, make sure is_loft is definitely true, because the roof could start as
    #    pitched, but is_loft false

    # TODO: We have a description: "Pitched, loft insulation", which seems to have its insulation thickness set to
    #  "none"
    #       Example UPRN: 100021359753, 10001204228

    # TODO: For windows, we have glazing_type and glazed_type. When simulating, we don't set glazed_type_ending which
    #       could be set to "double glazing installed during or after 2002" (THIS HAS BEEN ADDED!)

    # TODO: When simulating external wall insulation vs internal wall insulation, I need to set the external_insulation
    #       or internal_insulation boolean values to true (THIS HAS BEEN ADDED!)

    # TODO: We could probably re-map some of the values of glazed_type_ending

    # For simulating
    # 1) loft insulation - we take the lowest u-value when loft insulation is 270mm and 300mm, the values we most
    # commonly simulate to - For loft insulation, these values are in-line with
    best_270mm_uvalue = dataset[dataset["roof_insulation_thickness"] == "270"]["roof_thermal_transmittance"].min()
    best_300mm_uvalue = dataset[dataset["roof_insulation_thickness"] == "300"]["roof_thermal_transmittance"].min()

    # 2) Intenal wall insulation - we take the lowest u-value when simulating internal wall insulation
    best_internal_wall_uvalue = dataset[
        dataset["internal_insulation"] & dataset["is_solid_brick"]
        ]["walls_thermal_transmittance"].min()

    # 3) External wall insulation - we take the lowest u-value when simulating external wall insulation
    best_external_wall_uvalue = dataset[
        dataset["external_insulation"] & dataset["is_solid_brick"]
        ]["walls_thermal_transmittance"].min()

    # 4) Cavity wall insulation - we take the lowest u-value when simulating cavity wall insulation
    # This is 0.28, which is a sufficiently low value
    best_cavity_wall_uvalue = dataset[
        dataset["is_cavity_wall"] & dataset["is_filled_cavity"] & (~dataset["external_insulation"]) & (
            ~dataset["internal_insulation"])
        ]["walls_thermal_transmittance"].min()

    ending_colums = [col for col in dataset.columns if col.endswith("_ending")]
    # For the purpose of scoring, we want to simulate JUST the impact of the measure we're testing. We therefore
    # need to make sure that every "_ending" column is equal to its starting value
    column_config = {}
    for ending_col in ending_colums:
        base_col = ending_col.replace("_ending", "")
        # We check if the starting column ends with _starting or is just the base col
        if base_col + "_starting" in dataset.columns:
            column_config[ending_col] = base_col + "_starting"
        elif base_col in dataset.columns:
            column_config[ending_col] = base_col
        else:
            raise ValueError("something went wrong")

    loft_insulation_testing_data = []
    solid_wall_testing_data = []
    cavity_wall_testing_data = []
    solid_floor_testing_data = []
    suspended_floor_testing_data = []
    single_glazed_testing_data = []
    partial_double_glazed_testing_data = []
    partial_secondary_glazed_testing_data = []
    pitched_roof_solar = []
    flat_roof_solar = []
    for property_config in tqdm(property_types.itertuples(), total=property_types.shape[0]):

        config_hash = hash(str(property_config))

        # Take a sample row
        population = dataset[
            (dataset["property_type"] == property_config.property_type) &
            (dataset["built_form"] == property_config.built_form) &
            (dataset["floor_area_quantile"] == property_config.floor_area_quantile) &
            (dataset["construction_age_band"] == property_config.construction_age_band)
            ].copy()

        # Re-set all of the ending columns
        for col in ending_colums:
            population[col] = population[column_config[col]]

        # 1) Loft insulation

        # For loft insulation, there are two scenarios we test.
        # 1) Loft insulation to 270mm
        # 2) Lost insulation to 300mm

        for insulation_thickness in ["none", "12", "50", "75", "100", "150", "200", "250"]:
            if insulation_thickness == "none":
                row = population[
                    (population["roof_insulation_thickness"] == "none") &
                    (population["is_pitched"])
                    ]

            else:
                row = population[
                    (population["roof_insulation_thickness"] == insulation_thickness) &
                    (population["is_pitched"])
                    ]

            if row.empty:
                continue

            row = row.sample(1)

            loft_insulation_270mm_simulation = Property.create_recommendation_scoring_data(
                property_id=row["uprn"].values[0],
                recommendation_record=row.copy().to_dict("records")[0],
                recommendation={
                    "recommendation_id": f"loft_insulation_{insulation_thickness}_270mm_{config_hash}",
                    "type": "loft_insulation",
                    "new_u_value": best_270mm_uvalue,
                    "parts": [
                        {"depth": 270}
                    ]
                }
            )

            loft_insulation_300mm_simulation = Property.create_recommendation_scoring_data(
                property_id=row["uprn"].values[0],
                recommendation_record=row.copy().to_dict("records")[0],
                recommendation={
                    "recommendation_id": f"loft_insulation_{insulation_thickness}_300mm_{config_hash}",
                    "type": "loft_insulation",
                    "new_u_value": best_300mm_uvalue,
                    "parts": [
                        {"depth": 300}
                    ]
                }
            )

            # Insert simulation specific configuration details
            loft_insulation_270mm_simulation = {
                "simulation_ending_insulation_thickness": "270",
                "simulation_starting_insulation_thickness": insulation_thickness,
                **loft_insulation_270mm_simulation
            }

            loft_insulation_300mm_simulation = {
                "simulation_ending_insulation_thickness": "300",
                "simulation_starting_insulation_thickness": insulation_thickness,
                **loft_insulation_300mm_simulation
            }

            loft_insulation_testing_data.append(loft_insulation_270mm_simulation)
            loft_insulation_testing_data.append(loft_insulation_300mm_simulation)

        # 2) Solid wall insulation
        solid_wall_sample = population[
            population["is_solid_brick"] & (population["walls_insulation_thickness"] == "none")
            ]

        # We take 1 sample for each value of walls_thermal_transmittance
        for uvalue in solid_wall_sample["walls_thermal_transmittance"].unique():
            row = solid_wall_sample[
                solid_wall_sample["walls_thermal_transmittance"] == uvalue
                ].sample(1)

            # Simulated IWI
            internal_wall_insulation_simulation = Property.create_recommendation_scoring_data(
                property_id=row["uprn"].values[0],
                recommendation_record=row.copy().to_dict("records")[0],
                recommendation={
                    "recommendation_id": f"internal_wall_insulation_uvalue_{uvalue}_{config_hash}",
                    "type": "internal_wall_insulation",
                    "new_u_value": best_internal_wall_uvalue,
                    "parts": []
                }
            )

            # Simulated EWI
            external_wall_insulation_simulation = Property.create_recommendation_scoring_data(
                property_id=row["uprn"].values[0],
                recommendation_record=row.copy().to_dict("records")[0],
                recommendation={
                    "recommendation_id": f"external_wall_insulation_uvalue_{uvalue}_{config_hash}",
                    "type": "external_wall_insulation",
                    "new_u_value": best_external_wall_uvalue,
                    "parts": []
                }
            )

            # The iww/ewi simulations will be next to each other, so we can see how they differ for the same property
            solid_wall_testing_data.append(internal_wall_insulation_simulation)
            solid_wall_testing_data.append(external_wall_insulation_simulation)

        # 3) Cavity wall insulation
        cavity_wall_sample = population[
            population["is_cavity_wall"] & (~population["is_filled_cavity"]) & (
                ~population["external_insulation"]
            ) & (~population["internal_insulation"])
            ]

        # We take 1 sample for each value of walls_thermal_transmittance
        for uvalue in cavity_wall_sample["walls_thermal_transmittance"].unique():
            row = cavity_wall_sample[
                cavity_wall_sample["walls_thermal_transmittance"] == uvalue
                ].sample(1)

            # Simulated filled cavity
            filled_cavity_wall_insulation_simulation = Property.create_recommendation_scoring_data(
                property_id=row["uprn"].values[0],
                recommendation_record=row.copy().to_dict("records")[0],
                recommendation={
                    "recommendation_id": f"cavity_wall_insulation_uvalue_{uvalue}_{config_hash}",
                    "type": "cavity_wall_insulation",
                    "new_u_value": best_cavity_wall_uvalue,
                    "parts": []
                }
            )

            cavity_wall_testing_data.append(filled_cavity_wall_insulation_simulation)

        # 4) Solid floor insulation
        solid_floor_sample = population[
            population["is_solid"] & (population["floor_insulation_thickness"] == "none")
            ]

        solid_floor_uvalues = solid_floor_sample["floor_thermal_transmittance"].quantile([0.25, 0.5, 0.75]).values
        solid_floor_uvalues = {v for v in solid_floor_uvalues if not pd.isnull(v)}

        # We have many different values of u-value for solid floors, we we'll take a sample at the 25%, 50% and 75%
        # values
        # We must take a value that is in one of the unique values for floor_thermal_transmittance
        for uvalue in solid_floor_uvalues:
            nearest_value = solid_floor_sample['floor_thermal_transmittance'].sub(uvalue).abs().idxmin()
            nearest_row = solid_floor_sample.loc[[nearest_value]].sample(1)

            # Simulated solid floor insulation
            solid_floor_insulation_simulation = Property.create_recommendation_scoring_data(
                property_id=nearest_row["uprn"].values[0],
                recommendation_record=nearest_row.copy().to_dict("records")[0],
                recommendation={
                    "recommendation_id": f"solid_floor_insulation_uvalue_{uvalue}_{config_hash}",
                    "type": "solid_floor_insulation",
                    "new_u_value": None,  # This doesn't matter at the moment
                    "parts": []
                }
            )

            solid_floor_testing_data.append(solid_floor_insulation_simulation)

        # 5) Suspended floor insulation
        suspended_floor_sample = population[
            population["is_suspended"] & (population["floor_insulation_thickness"] == "none")
            ]

        suspended_floor_uvalues = suspended_floor_sample["floor_thermal_transmittance"].quantile(
            [0.25, 0.5, 0.75]
        ).values
        suspended_floor_uvalues = {v for v in suspended_floor_uvalues if not pd.isnull(v)}

        # We take the same approach as for solid floors
        for uvalue in suspended_floor_uvalues:
            nearest_value = suspended_floor_sample['floor_thermal_transmittance'].sub(uvalue).abs().idxmin()
            nearest_row = suspended_floor_sample.loc[[nearest_value]].sample(1)

            # Simulated suspended floor insulation
            suspended_floor_insulation_simulation = Property.create_recommendation_scoring_data(
                property_id=nearest_row["uprn"].values[0],
                recommendation_record=nearest_row.copy().to_dict("records")[0],
                recommendation={
                    "recommendation_id": f"suspended_floor_insulation_uvalue_{uvalue}_{config_hash}",
                    "type": "suspended_floor_insulation",
                    "new_u_value": None,  # This doesn't matter at the moment
                    "parts": []
                }
            )

            suspended_floor_testing_data.append(suspended_floor_insulation_simulation)

        # 6) Windows - single glazing
        single_glazing_sample = population[
            (population["glazing_type"] == "single")
        ]

        if not single_glazing_sample.empty:
            row = single_glazing_sample.sample(1)

            # For single glazed windows, we can recommend double glazing or secondary glazing

            # Simulated double glazing
            double_glazing_simulation = Property.create_recommendation_scoring_data(
                property_id=row["uprn"].values[0],
                recommendation_record=row.copy().to_dict("records")[0],
                recommendation={
                    "recommendation_id": f"windows_glazing_single_to_double_{config_hash}",
                    "type": "windows_glazing",
                    "new_u_value": None,  # This doesn't matter at the moment
                    "parts": [],
                    "is_secondary_glazing": False
                }
            )

            # Simulated secondary glazing
            secondary_glazing_simulation = Property.create_recommendation_scoring_data(
                property_id=row["uprn"].values[0],
                recommendation_record=row.copy().to_dict("records")[0],
                recommendation={
                    "recommendation_id": f"windows_glazing_single_to_secondary_{config_hash}",
                    "type": "windows_glazing",
                    "new_u_value": None,  # This doesn't matter at the moment
                    "parts": [],
                    "is_secondary_glazing": True
                }
            )

            # Add in simulation specific details
            # Add to the beginning of the dictionary
            double_glazing_simulation = {
                "simulation_ending_window_finish": "double",
                **double_glazing_simulation
            }
            secondary_glazing_simulation = {
                "simulation_ending_window_finish": "secondary",
                **secondary_glazing_simulation
            }

            single_glazed_testing_data.append(double_glazing_simulation)
            single_glazed_testing_data.append(secondary_glazing_simulation)

        # 7) Windows - partial double glazed
        partial_double_glazing_sample = population[
            (population["glazing_type"] == "double") & (population["multi_glaze_proportion_starting"] > 0) & (
                population["multi_glaze_proportion_starting"] < 100
            )
            ]

        partial_double_glazed_values = partial_double_glazing_sample["multi_glaze_proportion_starting"].quantile(
            [0.25, 0.5, 0.75]
        ).values
        # Take non-null values
        partial_double_glazed_values = [v for v in partial_double_glazed_values if not pd.isnull(v)]
        partial_double_glazed_values = set(partial_double_glazed_values)

        for value in partial_double_glazed_values:
            nearest_value = partial_double_glazing_sample['multi_glaze_proportion_starting'].sub(value).abs().idxmin()
            nearest_row = partial_double_glazing_sample.loc[[nearest_value]].sample(1)
            # If we start with partial double glazing, we recommend completing the job
            # Simulated double glazing
            double_glazing_simulation = Property.create_recommendation_scoring_data(
                property_id=nearest_row["uprn"].values[0],
                recommendation_record=nearest_row.copy().to_dict("records")[0],
                recommendation={
                    "recommendation_id": f"windows_glazing_partial_double_to_double_{value}_{config_hash}",
                    "type": "windows_glazing",
                    "new_u_value": None,  # This doesn't matter at the moment
                    "parts": [],
                    "is_secondary_glazing": False
                }
            )

            partial_double_glazed_testing_data.append(double_glazing_simulation)

        # 8) Windows - partial secondary glazed
        partial_secondary_glazing_sample = population[
            (population["glazing_type"] == "secondary") & (population["multi_glaze_proportion_starting"] > 0) & (
                population["multi_glaze_proportion_starting"] < 100
            )
            ]

        partial_secondary_glazed_values = partial_secondary_glazing_sample["multi_glaze_proportion_starting"].quantile(
            [0.25, 0.5, 0.75]
        ).values
        # Take non-null values
        partial_secondary_glazed_values = [v for v in partial_secondary_glazed_values if not pd.isnull(v)]
        partial_secondary_glazed_values = set(partial_secondary_glazed_values)

        for value in partial_secondary_glazed_values:
            nearest_value = partial_secondary_glazing_sample['multi_glaze_proportion_starting'].sub(
                value).abs().idxmin()
            nearest_row = partial_secondary_glazing_sample.loc[[nearest_value]].sample(1)

            # If we start with partial secondary glazing, we recommend completing the job
            # Simulated secondary glazing
            secondary_glazing_simulation = Property.create_recommendation_scoring_data(
                property_id=nearest_row["uprn"].values[0],
                recommendation_record=nearest_row.copy().to_dict("records")[0],
                recommendation={
                    "recommendation_id": f"windows_glazing_partial_secondary_to_secondary_{value}_{config_hash}",
                    "type": "windows_glazing",
                    "new_u_value": None,  # This doesn't matter at the moment
                    "parts": [],
                    "is_secondary_glazing": True
                }
            )

            partial_secondary_glazed_testing_data.append(secondary_glazing_simulation)

        # 9) Solar PV

        # We only recommend solar for properties that have flat or pitched roofs, and no existing solar
        pitched_roof_no_solar = population[
            (population["is_pitched"]) & (population["photo_supply_starting"] == 0)
            ]

        if not pitched_roof_no_solar.empty:
            pitched_roof_no_solar = pitched_roof_no_solar.sample(1)

        flat_roof_no_solar = population[
            (population["is_flat"]) & (population["photo_supply_starting"] == 0)
            ]

        if not flat_roof_no_solar.empty:
            flat_roof_no_solar = flat_roof_no_solar.sample(1)

        # We simulate 30%, 40% and 50% coverage
        for coverage in [30, 40, 50]:

            if not pitched_roof_no_solar.empty:
                solar_simulation_pitched = Property.create_recommendation_scoring_data(
                    property_id=pitched_roof_no_solar["uprn"].values[0],
                    recommendation_record=pitched_roof_no_solar.copy().to_dict("records")[0],
                    recommendation={
                        "recommendation_id": f"pitched_solar_pv_coverage_{coverage}_percent_{config_hash}",
                        "type": "solar_pv",
                        "new_u_value": None,  # This doesn't matter at the moment
                        "parts": [],
                        "photo_supply": coverage
                    }
                )
                pitched_roof_solar.append(solar_simulation_pitched)

            if not flat_roof_no_solar.empty:
                solar_simulation_flat = Property.create_recommendation_scoring_data(
                    property_id=flat_roof_no_solar["uprn"].values[0],
                    recommendation_record=flat_roof_no_solar.copy().to_dict("records")[0],
                    recommendation={
                        "recommendation_id": f"flat_solar_pv_coverage_{coverage}_percent_{config_hash}",
                        "type": "solar_pv",
                        "new_u_value": None,  # This doesn't matter at the moment
                        "parts": [],
                        "photo_supply": coverage
                    }
                )
                flat_roof_solar.append(solar_simulation_flat)

    # We store all of this data in s3, as it is
    save_data_to_s3(
        bucket_name="retrofit-datalake-dev",
        s3_file_name="sap_change_model/simulation-pipeline-data.json",
        data=json.dumps(
            {
                "loft_insulation_testing_data": loft_insulation_testing_data,
                "solid_wall_testing_data": solid_wall_testing_data,
                "cavity_wall_testing_data": cavity_wall_testing_data,
                "solid_floor_testing_data": solid_floor_testing_data,
                "suspended_floor_testing_data": suspended_floor_testing_data,
                "single_glazed_testing_data": single_glazed_testing_data,
                "partial_double_glazed_testing_data": partial_double_glazed_testing_data,
                "partial_secondary_glazed_testing_data": partial_secondary_glazed_testing_data,
                "pitched_roof_solar": pitched_roof_solar,
                "flat_roof_solar": flat_roof_solar
            }
        )
    )

    # For each simulation type, we score against the model
    from backend.ml_models.api import ModelApi
    from datetime import datetime

    created_at = datetime.now().isoformat()
    model_api = ModelApi(portfolio_id="simulation-testing-pipeline", timestamp=created_at)
    model_api.MODEL_PREFIXES = ["sap_change_predictions"]

    # 1) Loft insulation
    # We chunk up the data into 200 rows
    loft_insulation_testing_df = pd.DataFrame(loft_insulation_testing_data)

    loft_insulation_predictions = []
    loft_to_loop_over = range(0, loft_insulation_testing_df.shape[0], 200)
    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_testing_df.iloc[chunk:chunk + 200],
            bucket="retrofit-data-dev",
            prediction_buckets={
                "sap_change_predictions": "retrofit-sap-predictions-dev",
            }
        )

        loft_insulation_predictions.append(loft_insulation_predictions_dict["sap_change_predictions"])

    loft_insulation_predictions = pd.concat(loft_insulation_predictions)
    # Store final parquet in s3
    save_dataframe_to_s3_parquet(
        df=loft_insulation_predictions,
        bucket_name="retrofit-datalake-dev",
        file_key=f"sap_change_model/simulation-pipeline-loft-insulation-predictions_{MODEL_VERSION}.parquet"
    )

    # We now merge the loft insulation predictions onto the scoring data and calculate exactly how much the insulation
    # is worth

    loft_insulation_comparison_matrix = loft_insulation_testing_df[
        ["simulation_starting_insulation_thickness", "simulation_ending_insulation_thickness", "uprn", "id",
         "sap_starting"]
    ].merge(
        loft_insulation_predictions.drop(columns=["recommendation_id"]),
        left_on="id",
        right_on="id",
        how="left"
    )

    loft_insulation_comparison_matrix["measure_impact"] = loft_insulation_comparison_matrix["predictions"] - \
                                                          loft_insulation_comparison_matrix["sap_starting"]

    # We create a sap band grouping, for every 10 points of sap. So 1-10, 11-20, 21-30 etc
    loft_insulation_comparison_matrix["sap_band"] = pd.cut(
        loft_insulation_comparison_matrix["sap_starting"],
        bins=range(0, 101, 10),
        labels=range(1, 11)
    )

    # Perform a group by describe
    loft_insulation_describe = loft_insulation_comparison_matrix.groupby(
        ["sap_band", "simulation_starting_insulation_thickness", "simulation_ending_insulation_thickness"]
    )[["measure_impact"]].describe().reset_index()

    for col in ["simulation_starting_insulation_thickness", "simulation_ending_insulation_thickness"]:
        loft_insulation_describe[col] = loft_insulation_describe[col].str.replace('none', "0")
        loft_insulation_describe[col] = loft_insulation_describe[col].astype(int)

    loft_insulation_describe = loft_insulation_describe.sort_values(
        ["simulation_ending_insulation_thickness", "simulation_starting_insulation_thickness"], ascending=True
    )

    # In the training data, try and get just the rows that are loft insulation only
    # Things that change:
    # 1) roof_insulation_thickness
    # 3) roof_thermal_transmittance
    # 4) roof_energy_eff_ending
    loft_insulation_training_data = dataset.copy()
    loft_insulation_columns_we_need_the_same = [c for c in column_config.keys() if c not in [
        "roof_insulation_thickness_ending", "roof_thermal_transmittance_ending", "roof_energy_eff_ending",
        "transaction_type_ending", "days_to_ending", "sap_ending", "heat_demand_ending", "carbon_ending",
        "total_floor_area_ending", "floor_height_ending", "estimated_perimeter_ending"
    ]]

    for ending_col in tqdm(loft_insulation_columns_we_need_the_same):
        starting_col = column_config[ending_col]
        loft_insulation_training_data = loft_insulation_training_data[
            loft_insulation_training_data[ending_col] == loft_insulation_training_data[starting_col]
            ]

    # We get rows where the insulation starts at 200mm
    insulation_200mm_starting = loft_insulation_training_data[
        (loft_insulation_training_data["roof_insulation_thickness"] == "200") &
        (loft_insulation_training_data["roof_insulation_thickness_ending"] == "300")
        ]

    # 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",
        auth_token="a2Nvbm5rb3dsZXNzYXJAZ21haWwuY29tOjY5MGJiMWM0NmIyOGI5ZDUxYzAxMzQzYzNiZGNlZGJjZDNmODQwMzA=",
        os_api_key=""
    )
    searcher.uprn = "10009320092"
    searcher.find_property(skip_os=True)

    newest_epc = searcher.newest_epc
    older_epc = [epc for epc in searcher.older_epcs if
                 epc["lmk-key"] == "5ae2f073004839510f9eeb1886160776a05697f8518b8b3b63d45f65686c4757"][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_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"
    # The testing row has 4 rooms
    # Score in the model to see what we get

    baseline_prediction = testing_model_api.predict_all(
        df=testing_row,
        bucket="retrofit-data-dev",
        prediction_buckets={
            "sap_change_predictions": "retrofit-sap-predictions-dev",
        }
    )

    baseline_pred_df = baseline_prediction["sap_change_predictions"]
    impact = baseline_pred_df["predictions"].values[0] - testing_row["sap_starting"].values[0]

    # Changing this from 4 rooms to 5 rooms has NO impact!!
    testing_row_5_rooms = testing_row.copy()
    testing_row_5_rooms["id"] = "testing-200mm-loft-insulation-starting-baseline+recommendation_id_5_rooms"
    testing_row_5_rooms["recommendation_id"] = "recommendation_id_5_rooms"
    testing_row_5_rooms["number_habitable_rooms"] = float(5)
    testing_row_5_rooms["number_heated_rooms"] = float(5)

    prediction_5_rooms = testing_model_api.predict_all(
        df=testing_row_5_rooms,
        bucket="retrofit-data-dev",
        prediction_buckets={
            "sap_change_predictions": "retrofit-sap-predictions-dev",
        }
    )

    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

        # We set wall recommendations to phase 0, loft to phase 1, floor to phase 2 and solar to phase 3
        for rec in wall_recommendations:
            rec["phase"] = 0

        for rec in loft_recommendations:
            rec["phase"] = 1

        for rec in floor_recommendations:
            rec["phase"] = 2

        for rec in solar_recommendations:
            rec["phase"] = 3

        # 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, recommendations=[wall_rec],
                primary_recommendation_id=wall_rec["recommendation_id"]
            )
            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, recommendations=[roof_rec],
                primary_recommendation_id=roof_rec["recommendation_id"]
            )
            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, recommendations=[floor_rec],
                primary_recommendation_id=floor_rec["recommendation_id"]
            )
            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, recommendations=[solar_rec],
                primary_recommendation_id=solar_rec["recommendation_id"]
            )
            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(),
            recommendations=[wall_recommendations[0]],
            primary_recommendation_id=wall_recommendations[0]["recommendation_id"]
        ) 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(),
            recommendations=[wall_recommendations[0], loft_recommendations[0], ],
            primary_recommendation_id=loft_recommendations[0]["recommendation_id"]
        ) 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(),
            recommendations=[wall_recommendations[0], loft_recommendations[0], floor_recommendations[0]],
            primary_recommendation_id=floor_recommendations[0]["recommendation_id"]
        ) 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(),
            recommendations=[wall_recommendations[0], loft_recommendations[0], floor_recommendations[0],
                             solar_recommendations[0]],
            primary_recommendation_id=solar_recommendations[0]["recommendation_id"]
        ) 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)
