Coverage for o2/actions/legacy_optimos_actions/modify_calendar_by_wt_action.py: 94%
34 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 RateSelfReturnType
4from o2.actions.base_actions.modify_calendar_base_action import (
5 ModifyCalendarBaseAction,
6 ModifyCalendarBaseActionParamsType,
7)
8from o2.models.solution import Solution
9from o2.models.timetable.time_period import TimePeriod
10from o2.store import Store
13class ModifyCalendarByWTActionParamsType(ModifyCalendarBaseActionParamsType):
14 """Parameter for `ModifyCalendarByWTAction`."""
17@dataclass(frozen=True)
18class ModifyCalendarByWTAction(ModifyCalendarBaseAction, str=False):
19 """`ModifyCalendarByWTAction` will modify the resource calendars based on wt.
21 This action is based on the original optimos implementation,
22 see `solution_traces_sorting_by_waiting_times` in that project.
24 It will first find the tasks with the most waiting time, then find the days where
25 those are executed the most, and then find the resources
26 that does this task the most. (Although not for that day, as it was in Optimos).
27 Then it will look at the resource calendars for those resources on those days,
28 and first try to add hours to the start of the shifts, and if that is not possible,
29 it will try to shift the shifts to start earlier.
30 """
32 @staticmethod
33 def rate_self(store: Store, input: Solution) -> RateSelfReturnType:
34 """Generate a best set of parameters & self-evaluates this action."""
35 evaluation = input.evaluation
36 tasks = evaluation.get_task_names_sorted_by_waiting_time_desc()
37 for task in tasks:
38 days = evaluation.get_most_frequent_enablement_weekdays(task)
39 resources = evaluation.get_most_frequent_resources(task)
40 for day in days:
41 for resource in resources:
42 calendar = store.current_timetable.get_calendar_for_resource(resource)
43 if calendar is None:
44 continue
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 add hours to the start of the shift
59 new_period = fixed_day_period.add_hours_before(1)
60 if new_period is None:
61 continue
63 yield (
64 ModifyCalendarByWTAction.get_default_rating(store),
65 ModifyCalendarByWTAction(
66 ModifyCalendarByWTActionParamsType(
67 calendar_id=calendar.id,
68 period_id=period_id,
69 day=day,
70 add_hours_before=1,
71 )
72 ),
73 )
75 # Try to shift the shift to start earlier
76 new_period = fixed_day_period.shift_hours(-1)
77 if new_period is None:
78 continue
80 yield (
81 ModifyCalendarByWTAction.get_default_rating(store),
82 ModifyCalendarByWTAction(
83 ModifyCalendarByWTActionParamsType(
84 calendar_id=calendar.id,
85 period_id=period_id,
86 day=day,
87 add_hours_before=0,
88 shift_hours=-1,
89 )
90 ),
91 )
93 return