mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Merge pull request #744 from Hestia-Homes/bug/plans-not-hitting-c
Bug/plans not hitting c
This commit is contained in:
commit
cda31420e8
3 changed files with 113 additions and 15 deletions
|
|
@ -1053,14 +1053,18 @@ async def model_engine(body: PlanTriggerRequest):
|
|||
property_required_measures = [m for m in recommendations[p.id] if m[0]["type"] in body.required_measures]
|
||||
measures_to_optimise = [m for m in recommendations[p.id] if m[0]["type"] not in body.required_measures]
|
||||
|
||||
ventilation_included = "ventilation" in property_measure_types
|
||||
# TODO - formalise property measure types into an enum
|
||||
ventilation_included = (
|
||||
"ventilation" in property_measure_types or "mechanical_ventilation" in property_measure_types
|
||||
)
|
||||
|
||||
# If a measure requiring ventilation is selected, and the property does not have ventilation, we enfore
|
||||
# its inclusion
|
||||
|
||||
needs_ventilation = any(
|
||||
x in property_measure_types for x in assumptions.measures_needing_ventilation
|
||||
) and not p.has_ventilation and ventilation_included
|
||||
needs_ventilation = optimiser_functions.check_needs_ventilation(
|
||||
property_measure_types, assumptions.measures_needing_ventilation, p.has_ventilation,
|
||||
ventilation_included
|
||||
)
|
||||
|
||||
if not measures_to_optimise:
|
||||
# Nothing to do, we just reshape the recommendations
|
||||
|
|
@ -1177,8 +1181,10 @@ async def model_engine(body: PlanTriggerRequest):
|
|||
recommendations=recommendations, selected=selected,
|
||||
)
|
||||
|
||||
# Add best practice measures (ventilation/trickle vents)
|
||||
selected = optimiser_functions.add_best_practice_measures(p.id, solution, recommendations, selected)
|
||||
# Add best practice measures (ventilation/trickle vents) - pass needs_ventilation flag
|
||||
selected = optimiser_functions.add_best_practice_measures(
|
||||
p.id, solution, recommendations, selected, needs_ventilation
|
||||
)
|
||||
# Final flattening - we pass what the battery SAP score would be, regardless if the battery was selected
|
||||
recommendations[p.id] = optimiser_functions.flatten_recommendations_with_defaults(
|
||||
p.id, recommendations, selected, battery_sap_score
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import pandas as pd
|
||||
from typing import List, Dict, Any, Set
|
||||
import backend.app.assumptions as assumptions
|
||||
from backend.Property import Property
|
||||
from backend.app.plan.schemas import PlanTriggerRequest
|
||||
|
|
@ -300,7 +301,13 @@ def add_required_measures(property_id, property_required_measures, recommendatio
|
|||
]
|
||||
|
||||
|
||||
def add_best_practice_measures(property_id, solution, recommendations, selected):
|
||||
def add_best_practice_measures(
|
||||
property_id: int,
|
||||
solution: List[Dict[str, Any]],
|
||||
recommendations: Dict[int, List[List[Dict[str, Any]]]],
|
||||
selected: Set[str],
|
||||
needs_ventilation: bool
|
||||
):
|
||||
"""
|
||||
Ensures best-practice measures like ventilation and trickle vents are included
|
||||
in the selected recommendations when appropriate.
|
||||
|
|
@ -320,6 +327,8 @@ def add_best_practice_measures(property_id, solution, recommendations, selected)
|
|||
All recommendations for all properties, keyed by property id.
|
||||
selected : set
|
||||
Set of already selected recommendation IDs.
|
||||
needs_ventilation : bool
|
||||
Whether the property requires mechanical ventilation to accompany certain measures.
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
|
@ -329,12 +338,6 @@ def add_best_practice_measures(property_id, solution, recommendations, selected)
|
|||
# Check if any selected measure requires ventilation
|
||||
ventilation_selected = [r for r in solution if "+mechanical_ventilation" in r["type"]]
|
||||
|
||||
# If ventilation has been selected, or one of the measures needs ventilation, we need to ensure ventilation is
|
||||
# included
|
||||
needs_ventilation = any(
|
||||
x in [r["type"] for r in solution] for x in assumptions.measures_needing_ventilation
|
||||
) or len(ventilation_selected) > 0
|
||||
|
||||
if needs_ventilation:
|
||||
ventilation_rec = next(
|
||||
(r[0] for r in recommendations[property_id] if r[0]["type"] == "mechanical_ventilation"),
|
||||
|
|
@ -395,3 +398,30 @@ def flatten_recommendations_with_defaults(property_id, recommendations, selected
|
|||
|
||||
# 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]
|
||||
|
||||
|
||||
def check_needs_ventilation(
|
||||
property_measure_types: Set[str],
|
||||
measures_needing_ventilation: List[str],
|
||||
property_already_has_ventilation: bool,
|
||||
ventilation_in_included_measures: bool
|
||||
) -> bool:
|
||||
"""
|
||||
Function to check if we need to include ventilation based on the measures selected and the property
|
||||
features
|
||||
:param property_measure_types: The set of measure types recommended for the property
|
||||
:param measures_needing_ventilation: The set of measure types that require ventilation
|
||||
:param property_already_has_ventilation: Whether the property currently has ventilation
|
||||
:param ventilation_in_included_measures: Whether ventilation is already included in the recommended
|
||||
measures
|
||||
:return: Boolean indicating whether ventilation needs to be included in the recommendations
|
||||
|
||||
# TODO - none of the inputs of this function are well structured and so this is quite brittle - we should
|
||||
consider refactoring to make this more robust
|
||||
"""
|
||||
|
||||
needs_ventilation = any(
|
||||
x in property_measure_types for x in measures_needing_ventilation
|
||||
)
|
||||
|
||||
return needs_ventilation and not property_already_has_ventilation and ventilation_in_included_measures
|
||||
|
|
|
|||
|
|
@ -143,7 +143,9 @@ class TestAddBestPracticeMeasures:
|
|||
]
|
||||
}
|
||||
selected = set()
|
||||
updated = optimiser_functions.add_best_practice_measures(property_id, solution, recommendations, selected)
|
||||
updated = optimiser_functions.add_best_practice_measures(
|
||||
property_id, solution, recommendations, selected, True
|
||||
)
|
||||
assert "vent1" in updated
|
||||
assert "trickle1" in updated
|
||||
|
||||
|
|
@ -273,7 +275,7 @@ class TestIncreasingEpcE2e:
|
|||
total_optimised_gain = sum(m["gain"] for m in solution)
|
||||
assert total_optimised_gain == 17.6, "Total gain of optimised measures should meet or exceed target gain"
|
||||
|
||||
selected = optimiser_functions.add_best_practice_measures(p.id, solution, recommendations, selected)
|
||||
selected = optimiser_functions.add_best_practice_measures(p.id, solution, recommendations, selected, False)
|
||||
|
||||
# Flatten recommendations for output
|
||||
flattened = optimiser_functions.flatten_recommendations_with_defaults(p.id, recommendations, selected)
|
||||
|
|
@ -510,3 +512,63 @@ class TestStrategicOptimiser:
|
|||
assert opt.strategy_used.value == "case_2_solve_max_gain_under_budget"
|
||||
assert opt.solution_cost == 7787.068
|
||||
assert opt.solution_gain == 28.8
|
||||
|
||||
|
||||
class TestCheckNeedsVentilation:
|
||||
|
||||
def measure_types_includes_ventilation_no_existing_ventilation(self):
|
||||
property_measure_types = {'mechanical_ventilation', 'cavity_wall_insulation', 'suspended_floor_insulation',
|
||||
'secondary_heating', 'loft_insulation', 'heating', 'low_energy_lighting'}
|
||||
|
||||
measures_needing_ventilation = ['internal_wall_insulation', 'external_wall_insulation',
|
||||
'cavity_wall_insulation']
|
||||
|
||||
has_ventilation = False
|
||||
|
||||
ventilation_included = True
|
||||
|
||||
result = optimiser_functions.check_needs_ventilation(
|
||||
property_measure_types, measures_needing_ventilation, has_ventilation,
|
||||
ventilation_included
|
||||
)
|
||||
|
||||
assert result == True
|
||||
|
||||
def measure_types_includes_ventilation_existing_ventilation(self):
|
||||
property_measure_types = {'mechanical_ventilation', 'cavity_wall_insulation', 'suspended_floor_insulation',
|
||||
'secondary_heating', 'loft_insulation', 'heating', 'low_energy_lighting'}
|
||||
|
||||
measures_needing_ventilation = ['internal_wall_insulation', 'external_wall_insulation',
|
||||
'cavity_wall_insulation']
|
||||
|
||||
has_ventilation = True
|
||||
|
||||
ventilation_included = True
|
||||
|
||||
result = optimiser_functions.check_needs_ventilation(
|
||||
property_measure_types, measures_needing_ventilation, has_ventilation,
|
||||
ventilation_included
|
||||
)
|
||||
|
||||
assert result == False
|
||||
|
||||
def measure_types_includes_ventilation_existing_ventilation(self):
|
||||
property_measure_types_without_ventilation = {
|
||||
'cavity_wall_insulation', 'suspended_floor_insulation',
|
||||
'secondary_heating', 'loft_insulation', 'heating',
|
||||
'low_energy_lighting'
|
||||
}
|
||||
|
||||
measures_needing_ventilation = ['internal_wall_insulation', 'external_wall_insulation',
|
||||
'cavity_wall_insulation']
|
||||
|
||||
has_ventilation = False
|
||||
|
||||
ventilation_included = True
|
||||
|
||||
result = optimiser_functions.check_needs_ventilation(
|
||||
property_measure_types_without_ventilation, measures_needing_ventilation, has_ventilation,
|
||||
ventilation_included
|
||||
)
|
||||
|
||||
assert result == False
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue