From bdc4c213ad1925093e4744153a1d2254cd9fede0 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Fri, 5 Dec 2025 09:40:24 +0000 Subject: [PATCH] working on cleaning epc data for old records --- backend/Property.py | 11 ++++--- backend/app/assumptions.py | 9 ++++++ backend/app/db/models/recommendations.py | 11 +++++++ backend/engine/engine.py | 39 ++++++++++++++++++++++-- 4 files changed, 62 insertions(+), 8 deletions(-) diff --git a/backend/Property.py b/backend/Property.py index dd92a902..50fc865e 100644 --- a/backend/Property.py +++ b/backend/Property.py @@ -727,11 +727,12 @@ class Property: self.energy_cost_estimates = { "unadjusted": unadjusted_heating_costs, - "epc": { - "heating": float(self.data["heating-cost-current"]), - "hot_water": float(self.data["hot-water-cost-current"]), - "lighting": float(self.data["lighting-cost-current"]), - } + # Don't think we need the EPC + # "epc": { + # "heating": float(self.data["heating-cost-current"]), + # "hot_water": float(self.data["hot-water-cost-current"]), + # "lighting": float(self.data["lighting-cost-current"]), + # } } self.energy_consumption_estimates = { diff --git a/backend/app/assumptions.py b/backend/app/assumptions.py index 31acbe29..898f586b 100644 --- a/backend/app/assumptions.py +++ b/backend/app/assumptions.py @@ -101,3 +101,12 @@ measures_needing_ventilation = [ # If we have a property beyond this size, we assume it's likely large enough to have an ASHP ASHP_FLOOR_AREA_THRESHOLD = 120 # m2 + +# Is a placeholder, used for cleaning data. Is a flat average based on the estimated +AVERAGE_LIGHTING_COST = 100 + +# Average bill, based on british gas is #1,838.71. Subtract 100 for lighting, 228 for hot water. This will include +# appliances so appliances should be removed when this is used +AVERAGE_HEATING_AND_APPLIANCE_COST = 1510.71 +# Based on https://energysavingtrust.org.uk/sites/default/files/reports/AtHomewithWater%287%29.pdf +AVERAGE_HOT_WATER_COST = 228 diff --git a/backend/app/db/models/recommendations.py b/backend/app/db/models/recommendations.py index 2b7bf7c7..4c02268d 100644 --- a/backend/app/db/models/recommendations.py +++ b/backend/app/db/models/recommendations.py @@ -3,6 +3,7 @@ from sqlalchemy.orm import declarative_base from sqlalchemy.sql import func from backend.app.db.models.portfolio import Portfolio, PropertyModel from backend.app.db.models.materials import Material +from backend.app.db.models.portfolio import Epc from datatypes.enums import QuantityUnits import enum @@ -78,6 +79,16 @@ class Plan(Base): ), nullable=True, ) + post_sap_points = Column(Float) + post_epc_rating = Column(Enum(Epc)) + post_co2_emissions = Column(Float) + co2_savings = Column(Float) + post_energy_bill = Column(Float) + energy_bill_savings = Column(Float) + post_energy_consumption = Column(Float) # energy demand in kWh/year + energy_consumption_savings = Column(Float) + valuation_post_retrofit = Column(Float) + valuation_increase = Column(Float) class PlanRecommendations(Base): diff --git a/backend/engine/engine.py b/backend/engine/engine.py index 215adfe4..f92da01a 100644 --- a/backend/engine/engine.py +++ b/backend/engine/engine.py @@ -1,4 +1,3 @@ -import os import time import json from copy import deepcopy @@ -16,6 +15,7 @@ from etl.epc.Record import EPCRecord from sqlalchemy.exc import IntegrityError, OperationalError from sqlalchemy.orm import sessionmaker from starlette.responses import Response +from backend.ml_models.AnnualBillSavings import AnnualBillSavings from backend.app.config import get_settings, get_prediction_buckets from backend.app.db.connection import db_engine @@ -415,8 +415,17 @@ def averages_cleaning(prepared_epc: EPCRecord, cleaning_data: pd.DataFrame): :return: """ - if not pd.isnull(prepared_epc.prepared_epc["number_habitable_rooms"]) and not pd.isnull( - prepared_epc.prepared_epc["number_heated_rooms"]) and not pd.isnull(prepared_epc.prepared_epc["floor_height"]): + variables_to_clean = [ + "number_habitable_rooms", + "number_heated_rooms", + "floor_height", + "lighting_cost_current", + "heating_cost_current", + "hot_water_cost_current", + "energy_consumption_potential", + ] + + if not any([pd.isnull(prepared_epc.prepared_epc[k]) for k in variables_to_clean]): # Nothing to do return prepared_epc @@ -461,6 +470,30 @@ def averages_cleaning(prepared_epc: EPCRecord, cleaning_data: pd.DataFrame): prepared_epc.prepared_epc["floor_height"] = clean_floor_height prepared_epc.floor_height = clean_floor_height + if pd.isnull(prepared_epc.lighting_cost_current): + # This is a basic assumption as an average + prepared_epc.prepared_epc["lighting_cost_current"] = assumptions.AVERAGE_LIGHTING_COST + prepared_epc.lighting_cost_current = assumptions.AVERAGE_LIGHTING_COST + + if pd.isnull(prepared_epc.heating_cost_current): + # This is a basic assumption as an average + appliance_cost = AnnualBillSavings.estimate_appliances_energy_use( + total_floor_area=prepared_epc.total_floor_area + ) * AnnualBillSavings.ELECTRICITY_PRICE_CAP + heating_cleaned_value = assumptions.AVERAGE_HEATING_AND_APPLIANCE_COST - appliance_cost + prepared_epc.prepared_epc["heating_cost_current"] = heating_cleaned_value + prepared_epc.heating_cost_current = heating_cleaned_value + + if pd.isnull(prepared_epc.hot_water_cost_current): + # This is a basic assumption as an average + prepared_epc.prepared_epc["hot_water_cost_current"] = assumptions.AVERAGE_HOT_WATER_COST + prepared_epc.hot_water_cost_current = assumptions.AVERAGE_HOT_WATER_COST + + if pd.isnull(prepared_epc.energy_consumption_potential): + # Set to current + prepared_epc.prepared_epc["energy_consumption_potential"] = prepared_epc.energy_consumption_current + prepared_epc.energy_consumption_potential = prepared_epc.energy_consumption_current + return prepared_epc