Coverage for o2/actions/base_actions/modify_resource_base_action.py: 82%
50 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-05-16 11:18 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-05-16 11:18 +0000
1from abc import ABC, abstractmethod
2from dataclasses import dataclass, replace
3from typing import TYPE_CHECKING
5from typing_extensions import NotRequired, override
7from o2.actions.base_actions.base_action import (
8 BaseAction,
9 BaseActionParamsType,
10 RateSelfReturnType,
11)
12from o2.models.legacy_approach import LegacyApproach
13from o2.models.self_rating import RATING
14from o2.models.solution import Solution
15from o2.models.state import State
17if TYPE_CHECKING:
18 from o2.store import Store
21class ModifyResourceBaseActionParamsType(BaseActionParamsType):
22 """Parameter for `ModifyResourceBaseAction`."""
24 resource_id: str
25 task_id: NotRequired[str]
26 clone_resource: NotRequired[bool]
27 remove_resource: NotRequired[bool]
28 remove_task_from_resource: NotRequired[bool]
31@dataclass(frozen=True)
32class ModifyResourceBaseAction(BaseAction, ABC):
33 """`ModifyResourceBaseAction` will modify a resource or it's tasks.
35 It's rating function will be implemented by it's subclasses.
36 """
38 params: ModifyResourceBaseActionParamsType
40 @override
41 def apply(self, state: State, enable_prints: bool = True) -> State:
42 if "remove_resource" in self.params and self.params["remove_resource"]:
43 new_timetable = state.timetable.remove_resource(self.params["resource_id"])
44 return replace(state, timetable=new_timetable)
45 elif "clone_resource" in self.params and self.params["clone_resource"]:
46 new_timetable = state.timetable.clone_resource(
47 self.params["resource_id"],
48 [self.params["task_id"]] if "task_id" in self.params else None,
49 )
50 return replace(state, timetable=new_timetable)
51 elif (
52 "remove_task_from_resource" in self.params
53 and self.params["remove_task_from_resource"]
54 and "task_id" in self.params
55 ):
56 new_timetable = state.timetable.remove_task_from_resource(
57 self.params["resource_id"],
58 self.params["task_id"],
59 )
60 return replace(state, timetable=new_timetable)
61 return state
63 @override
64 @staticmethod
65 @abstractmethod
66 def rate_self(store: "Store", input: "Solution") -> RateSelfReturnType["ModifyResourceBaseAction"]:
67 pass
69 def __str__(self) -> str:
70 """Return a string representation of the action."""
71 if "remove_resource" in self.params:
72 return f"{self.__class__.__name__}(Resource '{self.params['resource_id']}' -- Remove)" # noqa: E501
73 elif "clone_resource" in self.params:
74 return f"{self.__class__.__name__}(Resource '{self.params['resource_id']}' -- Clone)" # noqa: E501
75 elif "remove_task_from_resource" in self.params and "task_id" in self.params:
76 return f"{self.__class__.__name__}(Resource '{self.params['resource_id']}' -- Remove Task '{self.params['task_id']}')" # noqa: E501
77 return f"{self.__class__.__name__}(Resource '{self.params['resource_id']}' -- Unknown)"
79 @staticmethod
80 def get_default_rating(store: "Store") -> RATING:
81 """Return the default rating for this action."""
82 if store.settings.legacy_approach == LegacyApproach.COMBINED:
83 # We start with Calendar actions, so we start with LOW rating
84 if store.solution.is_base_solution: # noqa: SIM114
85 return RATING.LOW
86 elif isinstance(store.solution.last_action, ModifyResourceBaseAction):
87 return RATING.LOW
88 else:
89 return RATING.HIGH
90 elif store.settings.legacy_approach == LegacyApproach.RESOURCES_FIRST:
91 return RATING.HIGH
92 elif store.settings.legacy_approach == LegacyApproach.CALENDAR_FIRST:
93 return RATING.LOW
94 elif store.settings.legacy_approach.calendar_is_disabled:
95 return RATING.NOT_APPLICABLE
96 return RATING.MEDIUM