diff --git a/backend/Funding.py b/backend/Funding.py index 50960eb7..5405943b 100644 --- a/backend/Funding.py +++ b/backend/Funding.py @@ -109,7 +109,7 @@ class Funding: return "73-97" if floor_area <= 199: return "98-199" - return "200" + return "200+" @staticmethod def _split_measures(measures: List[dict]): @@ -614,7 +614,7 @@ class Funding: raise ValueError("something went wrong, more than one pps for ashp") return pps.squeeze()["Cost Savings"] - if measure_type == "high_heat_retention_storage_heater": + if measure_type == "high_heat_retention_storage_heaters": pps_data = filtered_pps_matrix[ filtered_pps_matrix["Post_Main_Heating_Source"] == "High Heat Retention Storage Heaters" ] diff --git a/backend/app/db/functions/funding_functions.py b/backend/app/db/functions/funding_functions.py index 86611b9f..3c001266 100644 --- a/backend/app/db/functions/funding_functions.py +++ b/backend/app/db/functions/funding_functions.py @@ -34,9 +34,15 @@ def upload_funding(session: Session, p, plan_id, recommendations_to_upload): material_id = None if recommendation["parts"]: material_id = recommendation["parts"][0]["id"] + + part_type = part["type"] + if part_type == "extension_cavity_wall_insulation": + part_type = "cavity_wall_insulation" + if part_type == "sealing_open_fireplace": + part_type = "sealing_fireplace" funding_measures_data.append({ "funding_package_id": funding_package_id, - "measure": part["type"], + "measure": part_type, "material_id": material_id, "innovation_uplift": float(part["innovation_uplift"]), "partial_project_score": float(part["partial_project_score"]), diff --git a/backend/app/plan/schemas.py b/backend/app/plan/schemas.py index 36755665..feff11fd 100644 --- a/backend/app/plan/schemas.py +++ b/backend/app/plan/schemas.py @@ -23,7 +23,7 @@ ECO4_ELIGIBLE_HEATING_MEASURES = [ SPECIFIC_MEASURES = ( WALL_INSULATION_MEASURES + ROOF_INSULATION_MEASURES + ECO4_ELIGIBILE_FABRIC_MEASURES + ECO4_ELIGIBLE_HEATING_MEASURES + [ - "secondary_heating" "ventilation", "low_energy_lighting", "fireplace", + "secondary_heating", "ventilation", "low_energy_lighting", "fireplace", "hot_water_tank_insulation", "cylinder_thermostat" ] diff --git a/backend/engine/engine.py b/backend/engine/engine.py index e8c5884e..64bb8d65 100644 --- a/backend/engine/engine.py +++ b/backend/engine/engine.py @@ -438,6 +438,10 @@ def get_funding_data(): 'Post_Main_Heating_Source', 'Total Floor Area Band', 'Starting Band', 'Average Treatable Factor', 'Cost Savings', 'SAP Savings' ] + # Replace 200 with 200+ in floor area band + partial_project_scores_matrix["Total Floor Area Band"] = partial_project_scores_matrix[ + "Total Floor Area Band" + ].replace({"200": "200+"}) partial_project_scores_matrix["Cost Savings"] = partial_project_scores_matrix["Cost Savings"].astype(float) whlg_eligible_postcodes = read_csv_from_s3( @@ -850,9 +854,9 @@ async def model_engine(body: PlanTriggerRequest): project_scores_matrix=project_scores_matrix, partial_project_scores_matrix=partial_project_scores_matrix, whlg_eligible_postcodes=whlg_eligible_postcodes, - eco4_social_cavity_abs_rate=13, + eco4_social_cavity_abs_rate=12.5, eco4_social_solid_abs_rate=17, - eco4_private_cavity_abs_rate=13, + eco4_private_cavity_abs_rate=12.5, eco4_private_solid_abs_rate=17, gbis_social_cavity_abs_rate=21, gbis_social_solid_abs_rate=25, @@ -879,7 +883,8 @@ async def model_engine(body: PlanTriggerRequest): for group in measures_to_optimise_with_uplift: for r in group: - if r["type"] in ["mechanical_ventilation", "low_energy_lighting", "secondary_heating"]: + if r["type"] in ["mechanical_ventilation", "low_energy_lighting", "secondary_heating", + "extension_cavity_wall_insulation", "draught_proofing", "sealing_open_fireplace"]: ( r["partial_project_score"], r["partial_project_funding"], @@ -1170,6 +1175,7 @@ async def model_engine(body: PlanTriggerRequest): session.rollback() print("Failed i = %s" % str(i)) logger.error(f"An error occurred during batch starting at index {i}: {e}") + logger.error(f"property is uprn {p.uprn} id {p.id} address {p.address}") logger.info("Creating portfolio aggregations") # We implement this in the simplest way possible which will be just to query the database for all diff --git a/etl/customers/remote_assessments/app.py b/etl/customers/remote_assessments/app.py index 9b5bf7c1..5fbae1d0 100644 --- a/etl/customers/remote_assessments/app.py +++ b/etl/customers/remote_assessments/app.py @@ -4,7 +4,7 @@ from dotenv import load_dotenv from utils.s3 import save_csv_to_s3 from etl.find_my_epc.AssetListEpcData import AssetListEpcData -PORTFOLIO_ID = 212 +PORTFOLIO_ID = 235 USER_ID = 8 load_dotenv(dotenv_path="backend/.env") @@ -29,17 +29,34 @@ def app(): asset_list = [ { - "address": "7 Crawley Road", - "postcode": "N22 6AN", - "uprn": 100021169757 + "address": "9 Reeds Place", + "postcode": "PO12 3HR", + "uprn": 37017508 }, { "address": "7 Crawley Road", "postcode": "N22 6AN", "uprn": 100021169757 }, + { + "address": "20 Main Street", + "postcode": "NG32 1SE", + "uprn": 200002698370 + }, + { + "address": "19 Wolley Avenue", + "postcode": "LS12 5DX", + "uprn": 72234517 + }, + { + "address": "45 Bolton Lane, Hose", + "postcode": "LE14 4JE", + "uprn": 100030535501 + } ] + asset_list = pd.DataFrame(asset_list) + # Store the asset list in s3 filename = f"{USER_ID}/{PORTFOLIO_ID}/asset_list.csv" save_csv_to_s3( @@ -77,16 +94,24 @@ def app(): valuation_data = [ { - "valuation": 339_000, - "uprn": 200003423454, + "valuation": 201000, + "uprn": 37017508, }, { - "valuation": 374_000, - "uprn": 200003423194 + "valuation": 810000, + "uprn": 100021169757, }, { - "valuation": 719_000, - "uprn": 200003423607 + "valuation": 228_000, + "uprn": 72234517 + }, + { + "valuation": 236_000, + "uprn": 100030535501 + }, + { + "valuation": 509000, + "uprn": 200002698370 }, ] # Store valuation data to s3 @@ -97,19 +122,42 @@ def app(): file_name=valuation_filename ) - body = { + body1 = { "portfolio_id": str(PORTFOLIO_ID), - "housing_type": "Private", - "goal": "Increasing EPC", - "goal_value": "A", + "housing_type": "Social", + "goal": "EPC B", + "goal_value": "B", "trigger_file_path": filename, "already_installed_file_path": "", - "patches_file_path": patches_filename, - "non_invasive_recommendations_file_path": non_invasive_recommendations_filename, + "patches_file_path": "", + "non_invasive_recommendations_file_path": "", "valuation_file_path": "", "scenario_name": "Full package remote assessment", "multi_plan": True, "budget": None, - "inclusions": ["cavity_wall_insulation", "ventilation"] + "ashp_cop": 3.5, + "event_type": "remote_assessment", + "default_u_values": True, + } - print(body) + print(body1) + + body2 = { + "portfolio_id": str(PORTFOLIO_ID), + "housing_type": "Social", + "goal": "EPC C", + "goal_value": "C", + "trigger_file_path": filename, + "already_installed_file_path": "", + "patches_file_path": "", + "non_invasive_recommendations_file_path": "", + "valuation_file_path": "", + "scenario_name": "Full package remote assessment", + "multi_plan": True, + "budget": None, + "ashp_cop": 3.5, + "event_type": "remote_assessment", + "default_u_values": True, + + } + print(body2) diff --git a/recommendations/optimiser/funding_optimiser.py b/recommendations/optimiser/funding_optimiser.py index 060826cd..f48ce024 100644 --- a/recommendations/optimiser/funding_optimiser.py +++ b/recommendations/optimiser/funding_optimiser.py @@ -227,6 +227,19 @@ def optimise_with_funding_paths(p, input_measures, housing_type, funding: Fundin # ECO4 fabric only path = special case 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": + # We add in a second option which is just innovation measures + sub_measures_innovation = [] + for measures in sub_measures: + group = [] + for measure in measures: + if measure["innovation_uplift"]: + group.append(measure) + if group: + sub_measures_innovation.append(group) + sub_measures = deepcopy(sub_measures_innovation) + if not sub_measures: continue @@ -380,7 +393,7 @@ def optimise_with_funding_paths(p, input_measures, housing_type, funding: Fundin # If we have packages that are fundable, but do not meet the upgrade target, we can run a final optimisation pass if not solutions[solutions["is_eligible"] & ~solutions["meets_upgrade_target"]].empty: - raise NotImplementedError("Implement me") + 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"] = p.data["current-energy-efficiency"] @@ -397,6 +410,7 @@ def optimise_with_funding_paths(p, input_measures, housing_type, funding: Fundin ), axis=1 ) + rate = funding.get_eco4_abs_rate(is_cavity=p.walls["is_cavity_wall"]) solutions["full_project_funding"] = solutions["project_score"] * rate # if the scheme is not ECO4, we set the funding to 0 with iloc @@ -809,14 +823,22 @@ def make_funding_paths(p, input_measures, housing_type, funding: Funding): input_measures_innovation = [] input_gbis_measures_innovation = [] for measures in input_measures: + group_of_innovation_measures = [] + group_of_gbis_innovation_measures = [] for measure in measures: if measure["innovation_uplift"] or measure["type"] in remaining_insulation_type: - input_measures_innovation.append([measure]) + group_of_innovation_measures.append(measure) if measure["innovation_uplift"] and measure["type"] in ( remaining_insulation_type + other_gbis_insulation_measures ): - input_gbis_measures_innovation.append([measure]) + group_of_gbis_innovation_measures.append([measure]) + + if group_of_innovation_measures: + input_measures_innovation.append(group_of_innovation_measures) + + if group_of_gbis_innovation_measures: + input_gbis_measures_innovation.extend(group_of_gbis_innovation_measures) funding_paths = _make_solar_heating_funding_paths( p, input_measures_innovation, funding_paths, remaining_insulation_type, housing_type, funding