diff --git a/backend/app/BatterySapScorer.py b/backend/app/BatterySapScorer.py index f5e485c4..923c5498 100644 --- a/backend/app/BatterySapScorer.py +++ b/backend/app/BatterySapScorer.py @@ -25,6 +25,6 @@ class BatterySAPScorer: + cls.COEF_PV_SIZE * pv_size ) - # Round + clamp to [0,5] - sap_uplift = int(np.round(np.clip(sap_uplift, 0, 5))) + # Round + clamp to [1,5] - there are only a small number of cases with 0 points + sap_uplift = int(np.round(np.clip(sap_uplift, 1, 5))) return sap_uplift diff --git a/backend/engine/engine.py b/backend/engine/engine.py index b04cb2f5..1f2b3976 100644 --- a/backend/engine/engine.py +++ b/backend/engine/engine.py @@ -1136,7 +1136,12 @@ async def model_engine(body: PlanTriggerRequest): # This is the uplift score ABS uplift_project_score = optimal_solution["total_uplift_score"] # This is the SAP score associated to a battery - battery_sap_score = optimal_solution["battery_sap_uplift"] + pv_size = next( + (m["array_size"] for m in optimal_solution["items"] if m["type"] == "solar_pv"), 0 + ) + battery_sap_score = BatterySAPScorer.score( + starting_sap=optimal_solution["ending_sap"], pv_size=pv_size + ) else: # We optimise and then we determine eligibility for funding, based on the measures selected optimiser = ( diff --git a/etl/customers/lincs_rural/prepare_data.py b/etl/customers/lincs_rural/prepare_data.py new file mode 100644 index 00000000..db7a9087 --- /dev/null +++ b/etl/customers/lincs_rural/prepare_data.py @@ -0,0 +1,26 @@ +""" +Rough script to prepare the data for Lincs Rural project +""" +import pandas as pd +from etl.find_my_epc.RetrieveFindMyEpc import RetrieveFindMyEpc + +data = pd.read_excel( + "/Users/khalimconn-kowlessar/Downloads/MASTER LIST EPCS UPDATED November 2025 Domna Homes.xlsx", + sheet_name="PROPERTY EPC RATINGS" +) + +# We have property RRNs - we need UPRN + +for _, x in data.iterrows(): + rrn = x["EPC Ref."] + + # Fetch from find my epc + retriever = RetrieveFindMyEpc( + address="", + postcode="", + rrn=rrn, + address_postal_town="", + sap_rating=x["Actual"] + ) + + find_epc_data = retriever.retrieve_all_find_my_epc_data() diff --git a/recommendations/optimiser/funding_optimiser.py b/recommendations/optimiser/funding_optimiser.py index 0aa69f39..a8b998ae 100644 --- a/recommendations/optimiser/funding_optimiser.py +++ b/recommendations/optimiser/funding_optimiser.py @@ -533,18 +533,19 @@ def optimise_with_funding_paths( # For properties that are including batteries, we need to adjust the starting SAP to include the battery SAP uplift # Note: We score on ending sap, as the battery SAP uplift is based on the ending SAP after fabric/heat/solar # upgrades of each package is applied + # NB: The battery SAP uplift is used to potentially prioritise packages that include batteries, it does NOT impact + # the eventual SAP score at this point. Once the package is included, we'll re-calculate battery SAP score outside + # of this. This is because solutions["battery_sap_uplift"] = solutions.apply( lambda x: BatterySAPScorer.score(starting_sap=x["ending_sap"], pv_size=x["array_size"]) if x["has_battery"] else 0, axis=1 ) - # We add this on to ending SAP - solutions["ending_sap"] = solutions["ending_sap"] + solutions["battery_sap_uplift"] solutions["starting_band"] = (solutions["starting_sap"] + solutions["already_installed_gain"]).apply( funding.get_sap_band ) - solutions["ending_band"] = solutions["ending_sap"].apply(funding.get_sap_band) + solutions["ending_band"] = (solutions["ending_sap"] + solutions["battery_sap_uplift"]).apply(funding.get_sap_band) solutions["floor_area_band"] = solutions["floor_area"].apply(funding.get_floor_area_band) solutions["project_score"] = solutions.apply( lambda x: funding._calculate_full_project_abs( diff --git a/recommendations/optimiser/optimiser_functions.py b/recommendations/optimiser/optimiser_functions.py index d7705456..0eec35dc 100644 --- a/recommendations/optimiser/optimiser_functions.py +++ b/recommendations/optimiser/optimiser_functions.py @@ -371,11 +371,14 @@ def flatten_recommendations_with_defaults(property_id, recommendations, selected final_recommendations = [] for recommendations_by_type in recommendations[property_id]: + recs_by_type = [] for rec in recommendations_by_type: rec_copy = {**rec, "default": rec["recommendation_id"] in selected} if rec_copy.get("has_battery", False): rec_copy["sap_points"] += battery_sap_score - final_recommendations.append(rec_copy) + recs_by_type.append(rec_copy) + + final_recommendations.append(recs_by_type) # Flatten the nested list of lists into a single list return [rec for recommendations_by_type in final_recommendations for rec in recommendations_by_type]