Add max days per week block constraint and exclude days for shift constraints

This commit is contained in:
Ross
2025-09-18 21:00:31 +01:00
parent fdce572f6d
commit 3f4d8068eb
2 changed files with 26 additions and 2 deletions
+8 -2
View File
@@ -3,7 +3,7 @@ import datetime
import os
import sys
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 subprocess
import pandas as pd
@@ -336,6 +336,7 @@ def main(
Rota.constraint_options["max_shifts_per_week"] = 6
Rota.constraint_options["max_days_worked_per_week"] = 3
# 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
@@ -348,6 +349,8 @@ def main(
balance_offset=2,
assign_as_block=False,
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",
# "options": 3,
@@ -395,7 +398,10 @@ def main(
days=days[:5],
balance_offset=1,
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},
],
),
+18
View File
@@ -92,10 +92,12 @@ class NightConstraint(BaseShiftConstraint):
class PreShiftConstraint(BaseShiftConstraint):
days: Any = None
ignore_shifts: List[str] = []
exclude_days: List[str] = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
class PostShiftConstraint(BaseShiftConstraint):
days: Any = None
ignore_shifts: List[str] = []
exclude_days: List[str] = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
class BalanceAcrossGroupsConstraint(BaseShiftConstraint):
""""""
@@ -385,6 +387,7 @@ class RotaBuilder(object):
"max_night_frequency_week_exclusions": [],
"max_shifts_per_week": 7,
"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
# is forced as a block and spans the time peroid
"prevent_monday_after_full_weekends": [],
@@ -1840,6 +1843,17 @@ class RotaBuilder(object):
>= -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):
# Prevent more than n number shifts per 4 weeks
try:
@@ -2922,6 +2936,8 @@ class RotaBuilder(object):
if week not in constraint.weeks:
continue
for n in range(0, constraint.days):
if day in constraint.exclude_days:
continue
if day in constraint_shift.days:
# Only apply if worker can work this shift
if worker.site not in constraint_shift.sites:
@@ -2957,6 +2973,8 @@ class RotaBuilder(object):
if week not in constraint.weeks:
continue
for n in range(0, constraint.days):
if day in constraint.exclude_days:
continue
if day in constraint_shift.days:
# Only apply if worker can work this shift
if worker.site not in constraint_shift.sites: