diff --git a/domain/modelling/optimisation/optimiser.py b/domain/modelling/optimisation/optimiser.py index 9b076ec4a..b47495f00 100644 --- a/domain/modelling/optimisation/optimiser.py +++ b/domain/modelling/optimisation/optimiser.py @@ -24,7 +24,7 @@ from dataclasses import dataclass from typing import Optional, Protocol, Sequence from datatypes.epc.domain.epc_property_data import EpcPropertyData -from domain.modelling.measure_type import MeasureType +from domain.modelling.measure_type import FABRIC_MEASURE_TYPES, MeasureType from domain.modelling.scoring.package_scorer import Score from domain.modelling.recommendation import MeasureOption from domain.modelling.simulation import EpcSimulation @@ -237,7 +237,26 @@ def optimise_package_fabric_first( if the truthful post-fabric score meets ``target_sap``, stop there. Otherwise optimise the remaining groups on top — the starting point for phase 2 is the dwelling with the phase-1 fabric applied — within the leftover budget.""" - raise NotImplementedError + fabric_groups: list[list[ScoredOption]] = [ + group for group in groups if _is_fabric_group(group) + ] + fabric_package: OptimisedPackage = optimise_package( + groups=fabric_groups, + scorer=scorer, + baseline_epc=baseline_epc, + budget=budget, + target_sap=target_sap, + dependencies=dependencies, + ) + return fabric_package + + +def _is_fabric_group(group: list[ScoredOption]) -> bool: + """A group belongs to phase 1 when every Option in it is a fabric measure + (groups are one Recommendation each, so they are homogeneous in kind).""" + return all( + scored.option.measure_type in FABRIC_MEASURE_TYPES for scored in group + ) def _with_role1_signals(