cleaning up data dictionary references

This commit is contained in:
Khalim Conn-Kowlessar 2026-03-19 18:47:39 +00:00
parent 737147897b
commit 69af82a5db
8 changed files with 24 additions and 38 deletions

View file

@ -378,10 +378,7 @@ class Property:
self.recommendations_scoring_data.append(scoring_dict)
simulation_epc = vars(self.epc_record).copy()
# Insert static values
simulation_epc["lodgement_date"] = simulation_lodgment_date
simulation_epc = {k.replace("_", "-"): v for k, v in simulation_epc.items()}
simulation_epc = self.epc_record.to_dict(case="kebab", source="prepared")
types = [x["type"] for x in previous_phase_representatives]
if "external_wall_insulation" in types and "internal_wall_insulation" in types:

View file

@ -3,6 +3,7 @@ import string
import secrets
import logging
from io import BytesIO
from typing import Optional
def setup_logger(log_file=None, level=logging.INFO, overwrite_handler=False):
@ -73,7 +74,7 @@ def sap_to_epc(sap_points: int | float):
return "G"
def epc_to_sap_lower_bound(epc: str):
def epc_to_sap_lower_bound(epc: Optional[str]):
"""
Given an EPC rating, returns the lower bound SAP score required
to hit that EPC rating

View file

@ -1076,8 +1076,7 @@ async def model_engine(body: PlanTriggerRequest):
property_required_measures, recommendations, p, needs_ventilation
)
gain = optimiser_functions.calculate_gain(
body=body, p=p, fixed_gain=fixed_gain, eco_packages=eco_packages,
already_installed_gain=already_installed_sap
body=body, p=p, fixed_gain=fixed_gain, already_installed_gain=already_installed_sap
)
# We insert the innovation uplift

View file

@ -858,7 +858,7 @@ class Costs:
n_radiators = self._estimate_n_radiators(
number_habitable_rooms=n_rooms,
total_floor_area=self.property.floor_area,
property_type=self.property.epc_record.property - type,
property_type=self.property.epc_record.property_type,
built_form=self.property.epc_record.built_form
)

View file

@ -1,7 +1,7 @@
import pandas as pd
import numpy as np
from backend.Property import Property
from typing import List, Mapping, Any
from typing import List, Mapping, Any, Optional
from itertools import groupby
from recommendations.FloorRecommendations import FloorRecommendations
from recommendations.WallRecommendations import WallRecommendations
@ -49,7 +49,7 @@ class Recommendations:
materials: List,
exclusions: List[str] = None,
inclusions: List[str] = None,
default_u_values: bool = False,
default_u_values: Optional[bool] = False,
):
"""
:param property_instance: Instance of the Property class, for the home associated to property_id

View file

@ -22,10 +22,10 @@ class SecondaryHeating:
# No secondary heating system, so no recommendation to remove it
return
if self.property.data['number-habitable-rooms'] > self.property.data['number-heated-rooms']:
n_rooms = self.property.data['number-habitable-rooms'] - self.property.data['number-heated-rooms']
if self.property.epc_record.number_habitable_rooms > self.property.epc_record.number_heated_rooms:
n_rooms = self.property.epc_record.number_habitable_rooms - self.property.epc_record.number_heated_rooms
else:
n_rooms = self.property.data["number-heated-rooms"]
n_rooms = self.property.epc_record.number_heated_rooms
costs = self.costs.heater_removal(n_rooms=n_rooms)

View file

@ -285,7 +285,7 @@ def optimise_with_funding_paths(
# We add in generic insulation funding paths (where there is no fixed measure)
# Heating controls are only eligible if installed as part of a heating upgrade and so we do not include them
# here. We don't have an option if the property is a C or above
if housing_type == "Social" and p.data["current-energy-rating"] not in ["C", "B", "A"]:
if housing_type == "Social" and p.epc_record.current_energy_rating not in ["C", "B", "A"]:
funding_paths = (
[
{
@ -297,7 +297,7 @@ def optimise_with_funding_paths(
)
needs_pre_eco_hhrsh_upgrade = (
(p.data["current-energy-rating"] == "D") and work_package == "solar_hhrsh_eco4"
(p.epc_record.current_energy_rating == "D") and work_package == "solar_hhrsh_eco4"
)
for path_spec in funding_paths:
@ -306,7 +306,7 @@ def optimise_with_funding_paths(
if isinstance(path_spec, dict) and path_spec.get("reference") == "fabric-only:eco4":
sub_measures = _filter_measures_by_types(optimisation_input_measures, path_spec["allowed_types"])
# If the property is EPC D and socil, we also include just innovation measures
if housing_type == "Social" and p.data["current-energy-rating"] == "D":
if housing_type == "Social" and p.epc_record.current_energy_rating == "D":
# We add in a second option which is just innovation measures
sub_measures_innovation = []
for measures in sub_measures:
@ -354,7 +354,7 @@ def optimise_with_funding_paths(
"path": path_spec,
"scheme": scheme,
"is_eligible": _is_eligible_funding_package(
scheme, float(p.data["current-energy-efficiency"]), sub_gain
scheme, p.epc_record.current_energy_efficiency, sub_gain
),
"unfunded_items": unfunded_picked,
"already_installed_gain": already_installed_gain
@ -500,9 +500,7 @@ def optimise_with_funding_paths(
"total_gain": total_gain,
"path": path_spec,
"scheme": scheme,
"is_eligible": _is_eligible_funding_package(
scheme, int(p.data["current-energy-efficiency"]), total_gain
),
"is_eligible": _is_eligible_funding_package(scheme, p.epc_record.current_energy_efficiency, total_gain),
"unfunded_items": unfunded_picked,
"already_installed_gain": already_installed_gain
})
@ -523,7 +521,7 @@ def optimise_with_funding_paths(
# logger.info("We have some packages that are fundable but do not meet the target gain")
# We now can calculate the project ABS, which subtracts from the cost, but this is only relevant for ECO4
solutions["starting_sap"] = int(p.data["current-energy-efficiency"])
solutions["starting_sap"] = p.epc_record.current_energy_efficiency
solutions["floor_area"] = p.floor_area
solutions["ending_sap"] = solutions["starting_sap"] + solutions["total_gain"]
# We flag projects that are including batteries
@ -677,7 +675,7 @@ def optimise_with_scenarios(
for x in measures:
if x["has_battery"]:
x["battery_gain"] = BatterySAPScorer.score(
starting_sap=int(p.data["current-energy-efficiency"]) + target_gain + 1,
starting_sap=p.epc_record.current_energy_efficiency + target_gain + 1,
pv_size=x["array_size"]
)
x["gain"] += x["battery_gain"]
@ -893,7 +891,7 @@ def append_solution_metrics(solutions, target_gain, p, already_installed_sap=0):
# We need the ending SAP, but we'll need to remove the battery SAP uplift first
solutions_df["ending_sap_without_battery"] = solutions_df.apply(
lambda x: int(p.data["current-energy-efficiency"]) + already_installed_sap + _get_ending_sap_without_battery(x),
lambda x: p.epc_record.current_energy_efficiency + already_installed_sap + _get_ending_sap_without_battery(x),
axis=1
)
@ -1162,7 +1160,7 @@ def _make_solar_heating_funding_paths(
p, input_measures, funding_paths, remaining_insulation_type, housing_type, funding: Funding
):
# If a property is private and EPC D or above, it's not eligible
if housing_type == "Private" and p.data["current-energy-rating"] in ["D", "C", "B", "A"]:
if housing_type == "Private" and p.epc_record.current_energy_rating in ["D", "C", "B", "A"]:
return funding_paths
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Solar PV with existing eligible heating system
@ -1288,7 +1286,7 @@ def make_funding_paths(p, input_measures, housing_type, funding: Funding, work_p
"""
# If the property is currently EPC C, there is no funding availability
if p.data["current-energy-rating"] in ["C", "B", "A"]:
if p.epc_record.current_energy_rating in ["C", "B", "A"]:
return [], input_measures
# We handle the case of minimum insulation requirements. Whenever we have a heating system recommendation,
@ -1316,7 +1314,7 @@ def make_funding_paths(p, input_measures, housing_type, funding: Funding, work_p
funding_paths = []
if housing_type == "Social" and p.data["current-energy-rating"] == "D":
if housing_type == "Social" and p.epc_record.current_energy_rating == "D":
# If the property is currently EPC D, we can only include innovation measures or measures to meet the
# minimum insulation requirements. We make an exception if we have a measure that is
# already installed, specifically a heat pump
@ -1362,7 +1360,7 @@ def make_funding_paths(p, input_measures, housing_type, funding: Funding, work_p
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 1) The package must include EWI or IWI if the property is private rental sector
# We check if we have any EWI or IWI measures available - only for EPC E or below
if p.data["current-energy-rating"] in ["E", "F", "G"]:
if p.epc_record.current_energy_rating in ["E", "F", "G"]:
ewi_or_iwi = [{"OR": []}]
reference_measures = []
# If we have EWI we add it in

View file

@ -207,7 +207,6 @@ def calculate_gain(
body: PlanTriggerRequest,
p: Property,
fixed_gain: float,
eco_packages: None | dict = None,
already_installed_gain: float = 0,
) -> float | None:
"""
@ -226,7 +225,6 @@ def calculate_gain(
Property object with EPC data (must have p.data["current-energy-efficiency"]).
fixed_gain : float
Total fixed gain from required measures (returned by calculate_fixed_gain).
eco_packages : dict, optional
already_installed_gain: float, optional
Returns
@ -235,15 +233,8 @@ def calculate_gain(
Required SAP gain for EPC, or None for non-EPC goals.
"""
if body.goal == "Increasing EPC":
current_sap = int(p.data["current-energy-efficiency"]) + already_installed_gain
if eco_packages is None:
target_sap = epc_to_sap_lower_bound(body.goal_value)
else:
target_sap = (
eco_packages.get(p.id)[1] if eco_packages.get(p.id)[1] is not None
else epc_to_sap_lower_bound(body.goal_value)
)
current_sap = p.epc_record.current_energy_efficiency + already_installed_gain
target_sap = epc_to_sap_lower_bound(body.goal_value)
if target_sap <= current_sap:
# We've already met or exceeded the target EPC