diff --git a/backend/categorisation/categorisation_trigger_request.py b/backend/categorisation/categorisation_trigger_request.py index 9ef1d106..aa2b8ed3 100644 --- a/backend/categorisation/categorisation_trigger_request.py +++ b/backend/categorisation/categorisation_trigger_request.py @@ -1,5 +1,8 @@ +from typing import List, Optional from pydantic import BaseModel class CategorisationTriggerRequest(BaseModel): portfolio_id: int + + plan_priority_order: Optional[List[int]] diff --git a/backend/categorisation/processor.py b/backend/categorisation/processor.py index 97e4c5ad..539f7a68 100644 --- a/backend/categorisation/processor.py +++ b/backend/categorisation/processor.py @@ -1,5 +1,5 @@ from collections import defaultdict -from typing import Dict, List +from typing import Dict, List, Optional from backend.app.db.functions.recommendations_functions import ( bulk_update_plans, @@ -14,7 +14,9 @@ from utils.logger import setup_logger logger = setup_logger() -def process_portfolio(portfolio_id: int) -> None: +def process_portfolio( + portfolio_id: int, plan_priority_order: Optional[List[int]] = [] +) -> None: logger.info(f"Processing portfolio {portfolio_id}") plans: List[Plan] = _load_plans_for_portfolio(portfolio_id) plans_by_property: Dict[int, List[Plan]] = _group_plans_by_property(plans) @@ -24,7 +26,9 @@ def process_portfolio(portfolio_id: int) -> None: if not property_plans: raise ValueError(f"No plans for property {uprn}") - cheapest_plan = _choose_cheapest_relevant_plan(property_plans) + cheapest_plan = _choose_cheapest_relevant_plan( + property_plans, plan_priority_order + ) _update_default_flags(property_plans, cheapest_plan) @@ -64,7 +68,9 @@ def _group_plans_by_property(plans: List[Plan]) -> Dict[int, List[Plan]]: return grouped -def _choose_cheapest_relevant_plan(plans: List[Plan]) -> Plan: +def _choose_cheapest_relevant_plan( + plans: List[Plan], plan_priority_order: Optional[List[int]] = [] +) -> Plan: plans_to_consider: List[Plan] = [p for p in plans if p.is_compliant] or plans def plan_cost(plan: Plan) -> float: diff --git a/backend/categorisation/tests/test_prioritised_plan_selected.py b/backend/categorisation/tests/test_prioritised_plan_selected.py new file mode 100644 index 00000000..03bca666 --- /dev/null +++ b/backend/categorisation/tests/test_prioritised_plan_selected.py @@ -0,0 +1,88 @@ +from datetime import datetime +from typing import List +import pytest + +from backend.app.domain.classes.plan import Plan +from backend.app.domain.classes.scenario import Scenario +from backend.app.domain.records.plan_record import PlanRecord +from backend.app.domain.records.scenario_record import ScenarioRecord +from backend.app.db.models.portfolio import Epc, PortfolioGoal +from backend.categorisation.processor import _choose_cheapest_relevant_plan + + +@pytest.fixture +def created_at_datetime() -> datetime: + return datetime.now() + + +@pytest.fixture +def identical_plan_record(created_at_datetime: datetime, default: bool) -> PlanRecord: + return PlanRecord( + property_id=1, + portfolio_id=1, + created_at=created_at_datetime, + is_default=default, + post_epc_rating=Epc.C, + cost_of_works=500.0, + ) + + +def make_plan_record(created_at_datetime: datetime, default: bool) -> PlanRecord: + return PlanRecord( + property_id=1, + portfolio_id=1, + created_at=created_at_datetime, + is_default=default, + post_epc_rating=Epc.C, + cost_of_works=500.0, + ) + + +def test_prioritised_plan_selected(created_at_datetime: datetime) -> None: + # arrange + epc_c_scenario_record = ScenarioRecord( + name="EPC C", + created_at=created_at_datetime, + housing_type="", + goal=PortfolioGoal.INCREASING_EPC, + goal_value="C", + trigger_file_path="", + multi_plan=False, + is_default=True, + ) + epc_c_scenario = Scenario(record=epc_c_scenario_record, id=1) + epc_c_plan = Plan( + record=make_plan_record(created_at_datetime, True), + scenario=epc_c_scenario, + id=1, + ) + + minor_works_scenario_record = ScenarioRecord( + name="EPC C - Minor Works", + created_at=created_at_datetime, + housing_type="", + goal=PortfolioGoal.INCREASING_EPC, + goal_value="C", + trigger_file_path="", + multi_plan=False, + is_default=False, + ) + minor_works_scenario = Scenario(record=minor_works_scenario_record, id=2) + minor_works_plan = Plan( + record=make_plan_record(created_at_datetime, False), + scenario=minor_works_scenario, + id=2, + ) + + plan_priority_order: List[int] = [2, 1] + + expected_default_plan_id = 2 + + # act + actual_default_plan = _choose_cheapest_relevant_plan( + plans=[epc_c_plan, minor_works_plan], + plan_priority_order=plan_priority_order, + ) + + # assert + assert actual_default_plan.id == expected_default_plan_id