adding materials to recommender

This commit is contained in:
Khalim Conn-Kowlessar 2025-08-18 18:02:56 +01:00
parent b7c913ec3f
commit 8e5388b1ea
5 changed files with 41 additions and 20 deletions

View file

@ -60,7 +60,7 @@ class GoogleSolarApi:
# Error Messages # Error Messages
ENTITY_NOT_FOUND_ERROR = 'Requested entity was not found.' ENTITY_NOT_FOUND_ERROR = 'Requested entity was not found.'
def __init__(self, api_key, max_retries=5): def __init__(self, api_key, solar_materials: list, max_retries=5):
""" """
Initialize the GoogleSolarApi class with the provided API key and maximum retries. Initialize the GoogleSolarApi class with the provided API key and maximum retries.
@ -87,6 +87,7 @@ class GoogleSolarApi:
# Indicates if we think we have both units attached to a semi-detached property # Indicates if we think we have both units attached to a semi-detached property
self.double_property = False self.double_property = False
self.solar_materials = solar_materials
def get_building_insights(self, longitude, latitude, required_quality="MEDIUM", max_retries=None): def get_building_insights(self, longitude, latitude, required_quality="MEDIUM", max_retries=None):
""" """
@ -208,13 +209,13 @@ class GoogleSolarApi:
self.optimise_solar_configuration( self.optimise_solar_configuration(
energy_consumption=energy_consumption, energy_consumption=energy_consumption,
is_building=is_building, is_building=is_building,
property_instance=property_instance property_instance=property_instance,
) )
# Finally, if we have a double property, we half the data we stored area # Finally, if we have a double property, we half the data we stored area
if self.double_property: if self.double_property:
self.roof_area = self.roof_area / 2 self.roof_area = self.roof_area / 2
self.floor_area = self.floor_area / 2 self.floor_area = float(self.floor_area) / 2
def save_to_db(self, session, uprns_to_location, scenario_type): def save_to_db(self, session, uprns_to_location, scenario_type):
if self.insights_data is None: if self.insights_data is None:
@ -279,7 +280,9 @@ class GoogleSolarApi:
installation_life_span)) / installation_life_span)) /
(1 - efficiency_depreciation_factor)) (1 - efficiency_depreciation_factor))
def optimise_solar_configuration(self, energy_consumption, is_building=False, property_instance=None): def optimise_solar_configuration(
self, energy_consumption, is_building=False, property_instance=None
):
""" """
Optimise the solar panel configuration for the building. Optimise the solar panel configuration for the building.
:return: :return:
@ -321,9 +324,25 @@ class GoogleSolarApi:
if roi_summary["n_panels"].sum() < min_panels: if roi_summary["n_panels"].sum() < min_panels:
continue continue
total_panels = roi_summary["n_panels"].sum()
# find a product which is suitable for the ROI calc, without a battery. 400 Watts for the baseline
solar_product = next(
(m for m in self.solar_materials if m["type"] == "solar_pv" and
abs(m["size"] - (400 * total_panels) / 1000) < 0.1 and not m["includes_battery"]),
None
)
if solar_product is None:
logger.info("No suitable solar product found for the configuration with %d panels.", total_panels)
continue
total_cost = Costs.solar_pv( total_cost = Costs.solar_pv(
n_panels=roi_summary["n_panels"].sum(), solar_product=solar_product,
has_battery=False, # We don't actually need scaffolding for the ROI calc
scaffolding_options=[
{"total_cost": 1000, "size": property_instance.number_of_floors},
{"total_cost": 1000, "size": 3}
],
# Assume the most amount of scaffolding # Assume the most amount of scaffolding
n_floors=3 if property_instance is None else property_instance.number_of_floors n_floors=3 if property_instance is None else property_instance.number_of_floors
)["total"] )["total"]
@ -805,7 +824,8 @@ class GoogleSolarApi:
@classmethod @classmethod
def unit_solar_analysis( def unit_solar_analysis(
cls, unit_solar_config: List, input_properties: List[Property], session, body, google_solar_api_key: str cls, unit_solar_config: List, input_properties: List[Property], session, body, google_solar_api_key: str,
solar_materials: list
): ):
if not unit_solar_config: if not unit_solar_config:
@ -844,7 +864,7 @@ class GoogleSolarApi:
) )
continue continue
solar_api_client = cls(api_key=google_solar_api_key) solar_api_client = cls(api_key=google_solar_api_key, solar_materials=solar_materials)
solar_api_client.get( solar_api_client.get(
longitude=unit["longitude"], longitude=unit["longitude"],
latitude=unit["latitude"], latitude=unit["latitude"],
@ -852,7 +872,7 @@ class GoogleSolarApi:
is_building=False, is_building=False,
session=session, session=session,
uprn=unit["uprn"], uprn=unit["uprn"],
property_instance=property_instance property_instance=property_instance,
) )
# Store the data in the database # Store the data in the database

View file

@ -689,7 +689,7 @@ async def model_engine(body: PlanTriggerRequest):
building_solar_config=building_solar_config, building_solar_config=building_solar_config,
input_properties=input_properties, input_properties=input_properties,
session=session, session=session,
google_solar_api_key=get_settings().GOOGLE_SOLAR_API_KEY google_solar_api_key=get_settings().GOOGLE_SOLAR_API_KEY,
) )
input_properties = GoogleSolarApi.unit_solar_analysis( input_properties = GoogleSolarApi.unit_solar_analysis(
@ -697,7 +697,8 @@ async def model_engine(body: PlanTriggerRequest):
input_properties=input_properties, input_properties=input_properties,
session=session, session=session,
body=body, body=body,
google_solar_api_key=get_settings().GOOGLE_SOLAR_API_KEY solar_materials=[m for m in materials if m["type"] == "solar_pv"],
google_solar_api_key=get_settings().GOOGLE_SOLAR_API_KEY,
) )
logger.info("Identifying property recommendations") logger.info("Identifying property recommendations")

View file

@ -11,7 +11,7 @@ class FireplaceRecommendations(Definitions):
def __init__( def __init__(
self, self,
property_instance: Property, property_instance: Property,
materials: list = None, materials: list,
): ):
self.property = property_instance self.property = property_instance

View file

@ -1,11 +1,11 @@
import re import re
import backend.app.assumptions as assumptions import backend.app.assumptions as assumptions
from recommendations.Costs import Costs, BOILER_UPGRADE_SCHEME_ASHP_VALUE
from recommendations.recommendation_utils import ( from recommendations.recommendation_utils import (
check_simulation_difference, override_costs, combine_recommendation_configs check_simulation_difference, override_costs, combine_recommendation_configs
) )
from backend.Property import Property from backend.Property import Property
from backend.app.plan.schemas import MEASURE_MAP from backend.app.plan.schemas import MEASURE_MAP
from recommendations.Costs import Costs
from etl.epc_clean.epc_attributes.MainheatAttributes import MainHeatAttributes from etl.epc_clean.epc_attributes.MainheatAttributes import MainHeatAttributes
from etl.epc_clean.epc_attributes.HotWaterAttributes import HotWaterAttributes from etl.epc_clean.epc_attributes.HotWaterAttributes import HotWaterAttributes
from etl.epc_clean.epc_attributes.MainFuelAttributes import MainFuelAttributes from etl.epc_clean.epc_attributes.MainFuelAttributes import MainFuelAttributes
@ -85,7 +85,7 @@ class HeatingRecommender:
} }
} }
def __init__(self, property_instance: Property, materials: list = None): def __init__(self, property_instance: Property, materials: list):
self.property = property_instance self.property = property_instance
self.costs = Costs(self.property) self.costs = Costs(self.property)
@ -573,12 +573,12 @@ class HeatingRecommender:
if has_cavity_or_loft_recommendations: if has_cavity_or_loft_recommendations:
description = description + ( description = description + (
f" You must ensure that the property has an insulated cavity and " f" You must ensure that the property has an insulated cavity and "
f"270mm+ loft insulation to qualify for the grant, to claim £" f"270mm+ loft insulation to qualify for the grant, to claim £7,500"
f"{BOILER_UPGRADE_SCHEME_ASHP_VALUE} of funding from the boiler upgrade scheme grant. " f" of funding from the boiler upgrade scheme grant. "
) )
else: else:
description = description + ( description = description + (
f" £{BOILER_UPGRADE_SCHEME_ASHP_VALUE} of funding can be claimed from the boiler upgrade scheme" f" £7,500 of funding can be claimed from the boiler upgrade scheme"
) )
simulation_config = { simulation_config = {

View file

@ -65,11 +65,11 @@ class Recommendations:
property_instance=property_instance, materials=materials property_instance=property_instance, materials=materials
) )
self.draught_proofing_recommender = DraughtProofingRecommendations(property_instance=property_instance) self.draught_proofing_recommender = DraughtProofingRecommendations(property_instance=property_instance)
self.fireplace_recommender = FireplaceRecommendations(property_instance=property_instance) self.fireplace_recommender = FireplaceRecommendations(property_instance=property_instance, materials=materials)
self.lighting_recommender = LightingRecommendations(property_instance=property_instance, materials=materials) self.lighting_recommender = LightingRecommendations(property_instance=property_instance, materials=materials)
self.windows_recommender = WindowsRecommendations(property_instance=property_instance, materials=materials) self.windows_recommender = WindowsRecommendations(property_instance=property_instance, materials=materials)
self.solar_recommender = SolarPvRecommendations(property_instance=property_instance) self.solar_recommender = SolarPvRecommendations(property_instance=property_instance, materials=materials)
self.heating_recommender = HeatingRecommender(property_instance=property_instance) self.heating_recommender = HeatingRecommender(property_instance=property_instance, materials=materials)
self.hotwater_recommender = HotwaterRecommendations(property_instance=property_instance) self.hotwater_recommender = HotwaterRecommendations(property_instance=property_instance)
self.secondary_heating_recommender = SecondaryHeating(property_instance=property_instance) self.secondary_heating_recommender = SecondaryHeating(property_instance=property_instance)