Coverage for o2/actions/legacy_optimos_actions/modify_calendar_by_cost_action.py: 97%
36 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 dataclasses import dataclass
3from o2.actions.base_actions.base_action import (
4 RateSelfReturnType,
5)
6from o2.actions.base_actions.modify_calendar_base_action import (
7 ModifyCalendarBaseAction,
8 ModifyCalendarBaseActionParamsType,
9)
10from o2.models.days import DAYS
11from o2.models.solution import Solution
12from o2.models.timetable.time_period import TimePeriod
13from o2.store import Store
16class ModifyCalendarByCostActionParamsType(ModifyCalendarBaseActionParamsType):
17 """Parameter for `ModifyCalendarByCostAction`."""
20@dataclass(frozen=True)
21class ModifyCalendarByCostAction(ModifyCalendarBaseAction, str=False):
22 """`ModifyCalendarByCostAction` will modify the resource calendars based on wt.
24 This action is based on the original optimos implementation,
25 see `solution_traces_optimize_cost` in that project.
27 It will fist iterate over all resources sorted by their
28 cost (cost/hour * available_time), for each resource it will iterate over
29 each day in their calendar. For each day it will try to modify the
30 first period (shift), by either removing it (if it's only 1 hour long),
31 shrinking it from start & end, shrinking it from the start,
32 or finally shrinking it from the end.
33 """
35 @staticmethod
36 def rate_self(store: Store, input: "Solution") -> RateSelfReturnType:
37 """Generate a best set of parameters & self-evaluates this action."""
38 resources = store.solution.state.timetable.get_resources_with_cost()
39 for resource, _cost in resources:
40 calendar = store.current_timetable.get_calendar(resource.calendar)
41 if calendar is None:
42 continue
44 for day in DAYS:
45 periods = calendar.get_periods_containing_day(day)
46 for period in periods:
47 period_id = period.id
48 # We need to fix the day period to not change
49 # change the times of other days
50 fixed_day_period = TimePeriod(
51 from_=day,
52 to=day,
53 begin_time=period.begin_time,
54 end_time=period.end_time,
55 probability=period.probability,
56 )
58 # Try to remove the period if it's only 1 hour long
59 if fixed_day_period.duration == 1:
60 yield (
61 ModifyCalendarByCostAction.get_default_rating(store),
62 ModifyCalendarByCostAction(
63 params=ModifyCalendarByCostActionParamsType(
64 calendar_id=calendar.id,
65 period_id=period_id,
66 day=day,
67 remove_period=True,
68 )
69 ),
70 )
71 # Try to shrink the period from start & end
72 new_period = fixed_day_period.add_hours_after(-1)
73 if new_period is not None:
74 new_period = new_period.add_hours_before(-1)
75 if new_period is not None:
76 yield (
77 ModifyCalendarByCostAction.get_default_rating(store),
78 ModifyCalendarByCostAction(
79 params=ModifyCalendarByCostActionParamsType(
80 calendar_id=calendar.id,
81 period_id=period_id,
82 day=day,
83 add_hours_before=-1,
84 add_hours_after=-1,
85 )
86 ),
87 )
88 # Try to shrink the period from start
89 new_period = fixed_day_period.add_hours_before(-1)
90 if new_period is not None:
91 yield (
92 ModifyCalendarByCostAction.get_default_rating(store),
93 ModifyCalendarByCostAction(
94 params=ModifyCalendarByCostActionParamsType(
95 calendar_id=calendar.id,
96 period_id=period_id,
97 day=day,
98 add_hours_before=-1,
99 )
100 ),
101 )
102 # Try to shrink the period from end
103 new_period = fixed_day_period.add_hours_after(-1)
104 if new_period is not None:
105 yield (
106 ModifyCalendarByCostAction.get_default_rating(store),
107 ModifyCalendarByCostAction(
108 params=ModifyCalendarByCostActionParamsType(
109 calendar_id=calendar.id,
110 period_id=period_id,
111 day=day,
112 add_hours_after=-1,
113 )
114 ),
115 )
117 return