diff --git a/backend/app/plan/router.py b/backend/app/plan/router.py index d1578cc1..3b50d46d 100644 --- a/backend/app/plan/router.py +++ b/backend/app/plan/router.py @@ -729,6 +729,129 @@ async def trigger_plan(body: PlanTriggerRequest): scoring_epcs.extend(property_instance.updated_simulation_epcs) recommendations[property_id] = recommendations_with_impact + # For Debugging + recommendation_impact_df = [] + for property_id in recommendations.keys(): + for recs_by_type in recommendations[property_id]: + for rec in recs_by_type: + recommendation_impact_df.append( + { + "property_id": property_id, + "uprn": [p.uprn for p in input_properties if p.id == property_id][0], + "recommendation_id": rec["recommendation_id"], + "type": rec["type"], + "description": rec["description"], + "sap_points": rec["sap_points"], + "co2_equivalent_savings": rec["co2_equivalent_savings"], + "heat_demand": rec["heat_demand"] + } + ) + recommendation_impact_df = pd.DataFrame(recommendation_impact_df) + + surveyed_uprns = [ + 10024087855, 121016117, 121016124, + 10024087902, 121016121, 121016128 + ] + recommendation_impact_df = recommendation_impact_df[recommendation_impact_df["uprn"].isin(surveyed_uprns)] + recommendation_impact_df = recommendation_impact_df[recommendation_impact_df["type"] == "windows_glazing"] + + actual_impacts_df = pd.DataFrame( + [ + # 10024087855 + {"uprn": 10024087855, "type": "internal_wall_insulation", "actual_sap_points": 5}, + {"uprn": 10024087855, "type": "draught_proofing", "actual_sap_points": 2}, + {"uprn": 10024087855, "type": "low_energy_lighting", "actual_sap_points": 0}, + {"uprn": 10024087855, "type": "windows_glazing", "actual_sap_points": 4}, + # 121016117 + {"uprn": 121016117, "type": "internal_wall_insulation", "actual_sap_points": 6}, + {"uprn": 121016117, "type": "draught_proofing", "actual_sap_points": 1}, + {"uprn": 121016117, "type": "low_energy_lighting", "actual_sap_points": 1}, + {"uprn": 121016117, "type": "windows_glazing", "actual_sap_points": 4}, + # 121016124 + {"uprn": 121016124, "type": "internal_wall_insulation", "actual_sap_points": 8}, + {"uprn": 121016124, "type": "low_energy_lighting", "actual_sap_points": 2}, + {"uprn": 121016124, "type": "windows_glazing", "actual_sap_points": 5}, + # 10024087902 + {"uprn": 10024087902, "type": "room_roof_insulation", "actual_sap_points": 16}, + {"uprn": 10024087902, "type": "internal_wall_insulation", "actual_sap_points": 2}, + {"uprn": 10024087902, "type": "low_energy_lighting", "actual_sap_points": 0}, + # 121016121 + {"uprn": 121016121, "type": "internal_wall_insulation", "actual_sap_points": 5}, + {"uprn": 121016121, "type": "suspended_floor_insulation", "actual_sap_points": 2}, + {"uprn": 121016121, "type": "draught_proofing", "actual_sap_points": 1}, + {"uprn": 121016121, "type": "windows_glazing", "actual_sap_points": 3}, + # 121016128 + {"uprn": 121016128, "type": "internal_wall_insulation", "actual_sap_points": 6}, + {"uprn": 121016128, "type": "suspended_floor_insulation", "actual_sap_points": 1}, + {"uprn": 121016128, "type": "draught_proofing", "actual_sap_points": 1}, + {"uprn": 121016128, "type": "low_energy_lighting", "actual_sap_points": 1}, + {"uprn": 121016128, "type": "windows_glazing", "actual_sap_points": 3}, + ] + ) + + comparison = recommendation_impact_df.merge( + actual_impacts_df, how="inner", on=["uprn", "type"] + ) + + # from utils.s3 import read_dataframe_from_s3_parquet + # training_data = read_dataframe_from_s3_parquet( + # bucket_name="retrofit-data-dev", + # file_key="sap_change_model/2024-08-06-11-19-49/dataset_rooms.parquet" + # ) + # import pickle + # with open("delete_me.pkl", "wb") as f: + # pickle.dump(training_data, f) + + # Read in the pickle + import pickle + with open("delete_me.pkl", "rb") as f: + training_data = pickle.load(f) + + # How do we simulate windows: + ending_cols = [col for col in training_data.columns if col.endswith("_ending")] + starting = {} + for c in ending_cols: + starting_colname = c.replace("_ending", "_starting") + if starting_colname in training_data.columns: + starting[c] = starting_colname + else: + starting[c] = c.replace("_ending", "") + + allowed_to_change = [ + # Windows + "windows_energy_eff_ending", + "glazed_type_ending", + "glazing_type_ending", + "multi_glaze_proportion_ending", + + # Other + "sap_ending", + "heat_demand_ending", + "carbon_ending", + "estimated_perimeter_ending", + "lodgement_year_ending", + "lodgement_month_ending", + "days_to_ending", + "number_habitable_rooms_ending", + "number_heated_rooms_ending", + ] + fixed = [c for c in ending_cols if c not in allowed_to_change + ["uprn"]] + training_fixed = training_data.copy() + for col in fixed: + starting_col = starting[col] + training_fixed = training_fixed[training_fixed[col] == training_fixed[starting_col]] + + training_fixed = training_fixed.reset_index(drop=True) + + # Find the things that change + example = training_fixed.iloc[0] + things_that_change = [] + for c in ending_cols: + if example[c] != example[starting[c]]: + things_that_change.append(c) + # 100051011370 + example[] + # We call the API with the scoring epcs scoring_epcs = pd.DataFrame(scoring_epcs) scoring_epcs = kwh_client.transform(data=scoring_epcs, cleaned=cleaned) diff --git a/backend/app/plan/schemas.py b/backend/app/plan/schemas.py index 68f8bbf5..0ddd9761 100644 --- a/backend/app/plan/schemas.py +++ b/backend/app/plan/schemas.py @@ -90,13 +90,13 @@ class PlanTriggerRequest(BaseModel): # Validator to ensure exclusions are within the pre-defined possibilities @validator('exclusions', each_item=True) def check_exclusions(cls, v): - if v not in TYPICAL_MEASURE_TYPES + SPECIFIC_MEASURES: + if v not in TYPICAL_MEASURE_TYPES + SPECIFIC_MEASURES + NON_INVASIVE_SPECIFIC_MEASURES: raise ValueError(f"{v} is not an allowed exclusion") return v @validator('inclusions', each_item=True) def check_inclusions(cls, v): - if v not in TYPICAL_MEASURE_TYPES + SPECIFIC_MEASURES: + if v not in TYPICAL_MEASURE_TYPES + SPECIFIC_MEASURES + NON_INVASIVE_SPECIFIC_MEASURES: raise ValueError(f"{v} is not an allowed inclusion") return v diff --git a/etl/epc_clean/epc_attributes/HotWaterAttributes.py b/etl/epc_clean/epc_attributes/HotWaterAttributes.py index b0105b10..76b4e6fa 100644 --- a/etl/epc_clean/epc_attributes/HotWaterAttributes.py +++ b/etl/epc_clean/epc_attributes/HotWaterAttributes.py @@ -129,6 +129,7 @@ class HotWaterAttributes(Definitions): "heat recovery", "o r brif system, gydag ynni r haul, dim thermostat ar y silindr": "from main system, plus solar, no cylinder " "thermostat", + "o r brif system, gydag ynni r haul": "from main system, plus solar", } NODATA_DESCRIPTIONS = [ diff --git a/etl/epc_clean/epc_attributes/MainheatControlAttributes.py b/etl/epc_clean/epc_attributes/MainheatControlAttributes.py index 2759096d..eaa701da 100644 --- a/etl/epc_clean/epc_attributes/MainheatControlAttributes.py +++ b/etl/epc_clean/epc_attributes/MainheatControlAttributes.py @@ -115,6 +115,7 @@ class MainheatControlAttributes(Definitions): 't+ól un gyfradd, trvs': 'single rate heating, trvs', 'trvs a falf osgoi': 'trvs and bypass', 'rheolaeth celect': 'celect-type control', + 'rheoli r tal a llaw': 'manual charge control', } def __init__(self, description: str): diff --git a/recommendations/FloorRecommendations.py b/recommendations/FloorRecommendations.py index a1f63f96..db18a458 100644 --- a/recommendations/FloorRecommendations.py +++ b/recommendations/FloorRecommendations.py @@ -68,7 +68,8 @@ class FloorRecommendations(Definitions): measures = MEASURE_MAP["floor_insulation"] if measures is None else measures - if not measures: + # If we have no measures or none of the measures are relevant, we can't recommend anything + if not measures or not any(x in measures for x in MEASURE_MAP["floor_insulation"]): return u_value = self.property.floor["thermal_transmittance"] diff --git a/recommendations/WindowsRecommendations.py b/recommendations/WindowsRecommendations.py index ae7f7057..bc91f801 100644 --- a/recommendations/WindowsRecommendations.py +++ b/recommendations/WindowsRecommendations.py @@ -60,7 +60,8 @@ class WindowsRecommendations: return if windows_area is not None: - raise Exception("We have windows area, we should use this data for our recommendations!!!") + # TODO - we don't have a price for this so we can't recommend it + print("We have windows area, we should use this data for our recommendations!!!") # We scale the number of windows based on the proportion of existing glazing if self.property.data["multi-glaze-proportion"] != "":