mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
added restrictions on heating systems only for ESH, fixed bug in funding solutiosn
This commit is contained in:
parent
3edf5549af
commit
19a766f442
3 changed files with 47 additions and 12 deletions
|
|
@ -156,6 +156,7 @@ class SearchEpc:
|
|||
size=None,
|
||||
property_type=None,
|
||||
fast=False,
|
||||
heating_system: [str, None] = None
|
||||
):
|
||||
"""
|
||||
Address lines 1 and postcode are mandatory fields. The other address lines are optional
|
||||
|
|
@ -180,6 +181,9 @@ class SearchEpc:
|
|||
self.house_number = self.get_house_number(self.address1)
|
||||
self.numeric_house_number = self.extract_numeric_housenumber_part(self.house_number)
|
||||
|
||||
# property attributes
|
||||
self.heating_system = heating_system
|
||||
|
||||
self.max_retries = max_retries if max_retries is not None else self.MAX_RETRIES
|
||||
|
||||
self.client = EpcClient(auth_token=auth_token)
|
||||
|
|
@ -571,7 +575,8 @@ class SearchEpc:
|
|||
lmks_to_drop: list[str] | None = None,
|
||||
built_form: str = "",
|
||||
property_type: str = "",
|
||||
exclude_old: bool = False
|
||||
exclude_old: bool = False,
|
||||
heating_system: [str, None] = None
|
||||
):
|
||||
"""
|
||||
Fetches and processes EPC data for a given initial postcode, applying successive trimming
|
||||
|
|
@ -591,6 +596,7 @@ class SearchEpc:
|
|||
:param built_form: The 'built-form' value to be used for filtering the EPC data.
|
||||
:param property_type: The 'property-type' value to be used for filtering the EPC data.
|
||||
:param exclude_old: Flag to exclude EPC data older than 10 years.
|
||||
:param heating_system: Optional heating system type for additional filtering.
|
||||
:return:
|
||||
"""
|
||||
|
||||
|
|
@ -703,6 +709,11 @@ class SearchEpc:
|
|||
epc_data["property-type"] == estimation_property_type)
|
||||
]
|
||||
|
||||
if heating_system is not None:
|
||||
epc_data = epc_data[
|
||||
epc_data["mainheat-description"] == heating_system
|
||||
]
|
||||
|
||||
if not epc_data.empty:
|
||||
return epc_data # Return the filtered data if it's not empty
|
||||
|
||||
|
|
@ -712,7 +723,7 @@ class SearchEpc:
|
|||
# If loop finishes without a valid response, raise an exception
|
||||
raise Exception("Unable to find postcode data after trimming - investigate me")
|
||||
|
||||
def estimate_epc(self, property_type, built_form, lmks_to_drop=None, exclude_old=False):
|
||||
def estimate_epc(self, property_type, built_form, lmks_to_drop=None, exclude_old=False, heating_system=None):
|
||||
"""
|
||||
For a property that does not have an EPC, we retrieve the EPC data for the closest properties
|
||||
and estimate the EPC for the property in question.
|
||||
|
|
@ -726,6 +737,8 @@ class SearchEpc:
|
|||
:param lmks_to_drop: This is a list of LMK keys that should be dropped from the estimation process. This
|
||||
is used as an override for testing, to drop EPCs for the property we are testing
|
||||
:param exclude_old: Used to drop any expired EPCs (more than 10 years old)
|
||||
:param heating_system: The heating system of the property we are estimating, if known. Will aim to filter EPCs
|
||||
to matching heating systems
|
||||
:return:
|
||||
"""
|
||||
|
||||
|
|
@ -736,7 +749,8 @@ class SearchEpc:
|
|||
lmks_to_drop=lmks_to_drop,
|
||||
built_form=built_form,
|
||||
property_type=property_type,
|
||||
exclude_old=exclude_old
|
||||
exclude_old=exclude_old,
|
||||
heating_system=heating_system
|
||||
)
|
||||
|
||||
# Check if it's a new build EPC. A property that doesn't have an EPC is not going to be a new build
|
||||
|
|
@ -906,7 +920,8 @@ class SearchEpc:
|
|||
# We can try and estimate
|
||||
estimated_epc = self.estimate_epc(
|
||||
property_type=self.ordnance_survey_client.property_type,
|
||||
built_form=self.ordnance_survey_client.built_form
|
||||
built_form=self.ordnance_survey_client.built_form,
|
||||
heating_system=self.heating_system
|
||||
)
|
||||
self.newest_epc = estimated_epc
|
||||
self.older_epcs = []
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import json
|
|||
from copy import deepcopy
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import Nullable
|
||||
from tqdm import tqdm
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
|
@ -59,7 +60,7 @@ from recommendations.recommendation_utils import convert_thickness_to_numeric, g
|
|||
logger = setup_logger()
|
||||
|
||||
BATCH_SIZE = 5
|
||||
SCORING_BATCH_SIZE = 100
|
||||
SCORING_BATCH_SIZE = 300
|
||||
|
||||
|
||||
def extract_portfolio_aggregation_data(
|
||||
|
|
@ -373,6 +374,24 @@ def get_funding_data():
|
|||
return project_scores_matrix, partial_project_scores_matrix, whlg_eligible_postcodes
|
||||
|
||||
|
||||
def parse_heating_system(config):
|
||||
"""
|
||||
Helper function to extract a heating system, which can be used to estimate EPC. This is a very limited,
|
||||
placeholder function to cover some initial immediate cases.
|
||||
:return:
|
||||
"""
|
||||
|
||||
ll_heating = config.get("landlord_heating_system", None)
|
||||
if not ll_heating:
|
||||
return None
|
||||
|
||||
if ll_heating == "electric storage heaters":
|
||||
# Return with the same format at the EPC
|
||||
return "Electric storage heaters"
|
||||
|
||||
return None
|
||||
|
||||
|
||||
async def model_engine(body: PlanTriggerRequest):
|
||||
logger.info("Model Engine triggered with body: %s", json.loads(body.model_dump_json()))
|
||||
|
||||
|
|
@ -502,8 +521,8 @@ async def model_engine(body: PlanTriggerRequest):
|
|||
address1 = config.get("domna_full_address", None)
|
||||
|
||||
address1 = str(int(address1)) if isinstance(address1, float) else str(address1)
|
||||
|
||||
full_address = config["domna_full_address"] if body.file_format == "domna_asset_list" else None
|
||||
heating_system = parse_heating_system(config)
|
||||
|
||||
epc_searcher = SearchEpc(
|
||||
address1=address1,
|
||||
|
|
@ -511,7 +530,8 @@ async def model_engine(body: PlanTriggerRequest):
|
|||
uprn=uprn,
|
||||
auth_token=get_settings().EPC_AUTH_TOKEN,
|
||||
os_api_key="",
|
||||
full_address=full_address
|
||||
full_address=full_address,
|
||||
heating_system=heating_system
|
||||
)
|
||||
epc_searcher.ordnance_survey_client.built_form = config.get("built_form", None)
|
||||
epc_searcher.ordnance_survey_client.property_type = config.get("property_type", None)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ In the future, we will adapt this into a class-based structure to allow for more
|
|||
|
||||
from copy import deepcopy
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
from backend.app.plan.schemas import (
|
||||
WALL_INSULATION_MEASURES, ROOF_INSULATION_MEASURES, ECO4_ELIGIBILE_FABRIC_MEASURES
|
||||
|
|
@ -401,9 +402,7 @@ def optimise_with_funding_paths(
|
|||
# If we have a budget, we need to ensure the subproblem respects it so we remove the fixed cost (which
|
||||
# may already be over budget) and the fixed gain (which may not be achievable)
|
||||
|
||||
if fixed_gain > target_gain:
|
||||
picked, sub_cost, sub_gain = ([], 0.0, 0.0)
|
||||
elif fixed_gain <= target_gain and not sub_measures:
|
||||
if (fixed_gain > target_gain) or (fixed_gain <= target_gain and not sub_measures):
|
||||
picked, sub_cost, sub_gain = ([], 0.0, 0.0)
|
||||
else:
|
||||
picked, sub_cost, sub_gain = run_optimizer(
|
||||
|
|
@ -412,8 +411,9 @@ def optimise_with_funding_paths(
|
|||
sub_target_gain=target_gain - fixed_gain if target_gain is not None else None
|
||||
)
|
||||
|
||||
if picked is None:
|
||||
continue
|
||||
# if picked is None:
|
||||
# # If we have something in sub_measures, then we have a partial solution, just not enough to
|
||||
# continue
|
||||
|
||||
scheme = _path_scheme(path_spec)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue