Add max days per week block constraint and exclude days for shift constraints
This commit is contained in:
+8
-2
@@ -3,7 +3,7 @@ import datetime
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
from rota.shifts import NoWorkers, PostShiftConstraint, PreShiftConstraint, RotaBuilder, SingleShift, WorkerRequirement, days
|
from rota.shifts import MaxShiftsPerWeekConstraint, NoWorkers, PostShiftConstraint, PreShiftConstraint, RotaBuilder, SingleShift, WorkerRequirement, days
|
||||||
import typer
|
import typer
|
||||||
import subprocess
|
import subprocess
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
@@ -336,6 +336,7 @@ def main(
|
|||||||
Rota.constraint_options["max_shifts_per_week"] = 6
|
Rota.constraint_options["max_shifts_per_week"] = 6
|
||||||
Rota.constraint_options["max_days_worked_per_week"] = 3
|
Rota.constraint_options["max_days_worked_per_week"] = 3
|
||||||
# Rota.constraint_options["avoid_st2_first_month"] = True
|
# Rota.constraint_options["avoid_st2_first_month"] = True
|
||||||
|
Rota.constraint_options["max_days_per_week_block"] = [(4, 2), (6, 3)] # (max_days, week_block)
|
||||||
|
|
||||||
#Rota.enable_unavailabilities = False
|
#Rota.enable_unavailabilities = False
|
||||||
|
|
||||||
@@ -348,6 +349,8 @@ def main(
|
|||||||
balance_offset=2,
|
balance_offset=2,
|
||||||
assign_as_block=False,
|
assign_as_block=False,
|
||||||
constraints=[
|
constraints=[
|
||||||
|
PreShiftConstraint(days=2, start_date="2025-11-17", exclude_days=("Fri", "Sat", "Sun")),
|
||||||
|
PostShiftConstraint(days=1, start_date="2025-11-17", exclude_days=("Fri", "Sat", "Sun")),
|
||||||
#{
|
#{
|
||||||
# "name": "max_shifts_per_week",
|
# "name": "max_shifts_per_week",
|
||||||
# "options": 3,
|
# "options": 3,
|
||||||
@@ -395,7 +398,10 @@ def main(
|
|||||||
days=days[:5],
|
days=days[:5],
|
||||||
balance_offset=1,
|
balance_offset=1,
|
||||||
constraints=[
|
constraints=[
|
||||||
PreShiftConstraint(days=1, start_date="2025-11-17", ignore_shifts=["oncall"]),
|
#PreShiftConstraint(days=1, start_date="2025-11-17", ignore_shifts=["oncall"]),
|
||||||
|
PreShiftConstraint(days=1, start_date="2025-11-17", ignore_shifts=["oncall"], exclude_days=("Sat", "Sun")),
|
||||||
|
#PostShiftConstraint(days=1, start_date="2025-11-17", ignore_shifts=["oncall", "weekend a", "weekend b"], include_weekends=False),
|
||||||
|
MaxShiftsPerWeekConstraint(max_shifts=1),
|
||||||
#{"name": "post", "options": 1},
|
#{"name": "post", "options": 1},
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -92,10 +92,12 @@ class NightConstraint(BaseShiftConstraint):
|
|||||||
class PreShiftConstraint(BaseShiftConstraint):
|
class PreShiftConstraint(BaseShiftConstraint):
|
||||||
days: Any = None
|
days: Any = None
|
||||||
ignore_shifts: List[str] = []
|
ignore_shifts: List[str] = []
|
||||||
|
exclude_days: List[str] = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
|
||||||
|
|
||||||
class PostShiftConstraint(BaseShiftConstraint):
|
class PostShiftConstraint(BaseShiftConstraint):
|
||||||
days: Any = None
|
days: Any = None
|
||||||
ignore_shifts: List[str] = []
|
ignore_shifts: List[str] = []
|
||||||
|
exclude_days: List[str] = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
|
||||||
|
|
||||||
class BalanceAcrossGroupsConstraint(BaseShiftConstraint):
|
class BalanceAcrossGroupsConstraint(BaseShiftConstraint):
|
||||||
""""""
|
""""""
|
||||||
@@ -385,6 +387,7 @@ class RotaBuilder(object):
|
|||||||
"max_night_frequency_week_exclusions": [],
|
"max_night_frequency_week_exclusions": [],
|
||||||
"max_shifts_per_week": 7,
|
"max_shifts_per_week": 7,
|
||||||
"max_shifts_per_month": 40,
|
"max_shifts_per_month": 40,
|
||||||
|
"max_days_per_week_block": [(3, 2)], # (max_days, week_block)
|
||||||
# The following will cause an unsolvable problem if a shift
|
# The following will cause an unsolvable problem if a shift
|
||||||
# is forced as a block and spans the time peroid
|
# is forced as a block and spans the time peroid
|
||||||
"prevent_monday_after_full_weekends": [],
|
"prevent_monday_after_full_weekends": [],
|
||||||
@@ -1840,6 +1843,17 @@ class RotaBuilder(object):
|
|||||||
>= -self.constraint_options["maximum_allowed_shift_diff"]
|
>= -self.constraint_options["maximum_allowed_shift_diff"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
for max_days, weeks in self.constraint_options["max_days_per_week_block"]:
|
||||||
|
for week_blocks in self.get_week_block_iterator(weeks):
|
||||||
|
self.model.constraints.add(
|
||||||
|
sum(
|
||||||
|
self.model.works_day[worker.id, week, day]
|
||||||
|
for week, day in self.get_week_day_combinations()
|
||||||
|
if week in week_blocks
|
||||||
|
)
|
||||||
|
<= max_days
|
||||||
|
)
|
||||||
|
|
||||||
for week_blocks in self.get_week_block_iterator(4):
|
for week_blocks in self.get_week_block_iterator(4):
|
||||||
# Prevent more than n number shifts per 4 weeks
|
# Prevent more than n number shifts per 4 weeks
|
||||||
try:
|
try:
|
||||||
@@ -2922,6 +2936,8 @@ class RotaBuilder(object):
|
|||||||
if week not in constraint.weeks:
|
if week not in constraint.weeks:
|
||||||
continue
|
continue
|
||||||
for n in range(0, constraint.days):
|
for n in range(0, constraint.days):
|
||||||
|
if day in constraint.exclude_days:
|
||||||
|
continue
|
||||||
if day in constraint_shift.days:
|
if day in constraint_shift.days:
|
||||||
# Only apply if worker can work this shift
|
# Only apply if worker can work this shift
|
||||||
if worker.site not in constraint_shift.sites:
|
if worker.site not in constraint_shift.sites:
|
||||||
@@ -2957,6 +2973,8 @@ class RotaBuilder(object):
|
|||||||
if week not in constraint.weeks:
|
if week not in constraint.weeks:
|
||||||
continue
|
continue
|
||||||
for n in range(0, constraint.days):
|
for n in range(0, constraint.days):
|
||||||
|
if day in constraint.exclude_days:
|
||||||
|
continue
|
||||||
if day in constraint_shift.days:
|
if day in constraint_shift.days:
|
||||||
# Only apply if worker can work this shift
|
# Only apply if worker can work this shift
|
||||||
if worker.site not in constraint_shift.sites:
|
if worker.site not in constraint_shift.sites:
|
||||||
|
|||||||
Reference in New Issue
Block a user