from collections import defaultdict from typing import List from backend.app.db.functions.recommendations_functions import ( get_plans_by_portfolio_id, get_scenario, set_plan_default, ) from backend.app.domain.classes.plan import Plan from backend.categorisation.categorisation_logic import CategorisationLogic from utils.logger import setup_logger logger = setup_logger() def process_portfolio(portfolio_id: int) -> None: plans = _load_plans_for_portfolio(portfolio_id) plans_by_property = _group_plans_by_property(plans) for property_plans in plans_by_property.values(): cheapest_plan = _choose_cheapest_relevant_plan(property_plans) _update_default_flags(property_plans, cheapest_plan) def _load_plans_for_portfolio(portfolio_id: int) -> List[Plan]: plan_models = get_plans_by_portfolio_id(portfolio_id) plans: List[Plan] = [] for model in plan_models: if not model.scenario_id: logger.info(f"No Scenario associated with Plan of ID {model.id}") continue scenario_model = get_scenario(model.scenario_id) plans.append(Plan.from_sqlalchemy(model, scenario_model)) return plans def _group_plans_by_property(plans: List[Plan]) -> dict[int, List[Plan]]: grouped: dict[int, List[Plan]] = defaultdict(list) for plan in plans: grouped[plan.record.property_id].append(plan) return grouped def _choose_cheapest_relevant_plan(plans: List[Plan]) -> Plan: compliant_plans = CategorisationLogic.get_compliant_plans(plans) plans_to_consider = compliant_plans or plans return CategorisationLogic.get_cheapest_plan(plans_to_consider) def _update_default_flags(plans: List[Plan], cheapest_plan: Plan) -> None: for plan in plans: if plan.id is None: raise ValueError("Cannot update Plan with missing ID") set_plan_default( plan.id, plan.id == cheapest_plan.id, )