allow plan priority to be specified for plans with identical ouput 🟥

This commit is contained in:
Daniel Roth 2026-02-18 10:06:40 +00:00
parent 3a5df1a1f3
commit 9a177065b6
3 changed files with 101 additions and 4 deletions

View file

@ -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]]

View file

@ -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:

View file

@ -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