diff --git a/asset_list/utils.py b/asset_list/utils.py index c7d0cc0a..8746c03a 100644 --- a/asset_list/utils.py +++ b/asset_list/utils.py @@ -2,7 +2,6 @@ import time import random import pandas as pd -from adhoc.investigation import newest_epc from backend.SearchEpc import SearchEpc from etl.find_my_epc.RetrieveFindMyEpc import RetrieveFindMyEpc from tqdm import tqdm diff --git a/backend/app/config.py b/backend/app/config.py index 98e1c447..dd3f5db1 100644 --- a/backend/app/config.py +++ b/backend/app/config.py @@ -3,7 +3,6 @@ from pydantic_settings import BaseSettings from typing import Optional - class Settings(BaseSettings): API_KEY: str API_KEY_NAME: str = "X-API-KEY" @@ -43,7 +42,8 @@ class Settings(BaseSettings): AWS_DEFAULT_REGION: Optional[str] = None class Config: - env_file = "backend.env" + env_file = "backend/.env" + @lru_cache() def get_settings(): diff --git a/backend/app/db/connection.py b/backend/app/db/connection.py index fbec9102..2ac9bd02 100644 --- a/backend/app/db/connection.py +++ b/backend/app/db/connection.py @@ -14,6 +14,7 @@ db_string = connection_string.format( db_engine = create_engine(db_string, pool_size=5, max_overflow=5) + def get_db_session(): if db_engine is None: raise RuntimeError("Database is not configured. Set DATABASE_URL in environment variables.") diff --git a/backend/app/plan/utils.py b/backend/app/plan/utils.py index 67b7bce1..ea328d5b 100644 --- a/backend/app/plan/utils.py +++ b/backend/app/plan/utils.py @@ -64,7 +64,7 @@ def extract_property_request_data( x for x in already_installed if (x["address"] == config["address"]) and (x["postcode"] == config["postcode"]) ), []) - + # Because we have some non-invasive recommendations that match on address and postcode, but not UPRN # we need to check existence of uprn has_uprn = "uprn" in non_invasive_recommendations[0] if non_invasive_recommendations else False diff --git a/backend/engine/engine.py b/backend/engine/engine.py index 1cd379b9..6e90a297 100644 --- a/backend/engine/engine.py +++ b/backend/engine/engine.py @@ -392,6 +392,26 @@ def parse_heating_system(config): return None +def check_duplicate_uprns(plan_input): + """ + Simple function to check if the input data contains duplicated UPRNS. + If there are duplicates, an exception will be rasied + :return: + """ + # Check for duplicate UPRNS + input_uprns = [x.get("uprn") for x in plan_input if "uprn" in x and x.get("uprn")] + + if input_uprns: + # Check for dupes + if len(input_uprns) != len(set(input_uprns)): + # Find the duplicate UPRNs + duplicates = set([x for x in input_uprns if input_uprns.count(x) > 1]) + # de-dupe input_uprns + raise ValueError(f"Duplicate UPRNs in the input data: {duplicates}") + + return True + + async def model_engine(body: PlanTriggerRequest): logger.info("Model Engine triggered with body: %s", json.loads(body.model_dump_json())) @@ -480,16 +500,8 @@ async def model_engine(body: PlanTriggerRequest): if body.index_start is not None and body.index_end is not None: plan_input = plan_input[body.index_start:body.index_end] - # Check for duplicate UPRNS - input_uprns = [x.get("uprn") for x in plan_input if "uprn" in x and x.get("uprn")] - - if input_uprns: - # Check for dupes - if len(input_uprns) != len(set(input_uprns)): - # Find the duplicate UPRNs - duplicates = set([x for x in input_uprns if input_uprns.count(x) > 1]) - # de-dupe input_uprns - raise ValueError(f"Duplicate UPRNs in the input data: {duplicates}") + # Confirm no duplicate UPRNS + check_duplicate_uprns(plan_input) # If we have patches or overrides, we should read them in here patches, already_installed, non_invasive_recommendations, valuation_data = get_request_property_data(body) @@ -528,9 +540,7 @@ async def model_engine(body: PlanTriggerRequest): if (body.event_type == "remote_assessment") and config.get("property_type") == "Flat": # We're running a remote assessment for a flat - we go and grab the associated # UPRNS for other units in the same building - associated_uprns = get_associated_uprns( - session, postcode=config["postcode"], uprn=uprn - ) + associated_uprns = get_associated_uprns(session, postcode=config["postcode"], uprn=uprn) epc_searcher = SearchEpc( address1=address1, @@ -1140,6 +1150,7 @@ async def model_engine(body: PlanTriggerRequest): ) property_value_increase_ranges[p.id] = valuations + # TODO - this is not right, especially if the existing run failed if p.is_new: property_details_epc = p.get_property_details_epc( portfolio_id=body.portfolio_id, rating_lookup=rating_lookup, diff --git a/etl/customers/peabody/Nov 2025 Consulting Project/data_prep.py b/etl/customers/peabody/Nov 2025 Consulting Project/data_prep.py index c68a0b58..6dd71b98 100644 --- a/etl/customers/peabody/Nov 2025 Consulting Project/data_prep.py +++ b/etl/customers/peabody/Nov 2025 Consulting Project/data_prep.py @@ -77,6 +77,14 @@ archetypes = sustainability_data[ "Floor Area Band"] ].drop_duplicates() +# Potential reductions: +# 1) Split roof insulation into > 100mm loft and <= 100mm loft +# 2) Group all of the glazed together (e.g. double glazed, secondary glazed, triple glazed) +# 3) Group up boiler efficiency A-C, D - F, G? or someting like this +# 4) Group up main fuel into gas, electric, oil, other? +# 5) Wall Construction - group up Sandstone and Granite into one category +# 6) Reduce or remove floor construction + # Maps the property types to the format recognised by the EPC api property_type_map = {} # Maps the build form to the format recognised by the OS api diff --git a/etl/find_my_epc/RetrieveFindMyEpc.py b/etl/find_my_epc/RetrieveFindMyEpc.py index c9cca011..c57f9ca8 100644 --- a/etl/find_my_epc/RetrieveFindMyEpc.py +++ b/etl/find_my_epc/RetrieveFindMyEpc.py @@ -398,6 +398,7 @@ class RetrieveFindMyEpc: extracted_address_cleaned = ( extracted_address.replace(",", "").replace(" ", "").lower() ) + if not extracted_address_cleaned.startswith(self.address_cleaned): continue