Add exclude_dates option to PreShiftConstraint and PostShiftConstraint

This commit is contained in:
Ross
2025-12-17 09:19:45 +00:00
parent 108bb572a7
commit 81b2c85d61
2 changed files with 28 additions and 6 deletions
+2 -2
View File
@@ -341,7 +341,7 @@ def main(
constraint_options=RotaConstraintOptions(
balance_weekends=True,
max_shifts_per_week=6,
max_days_per_week_block=[(4, 2), (6, 3)],
max_days_per_week_block=[(4, 2), (4, 3)],
),
)
@@ -356,7 +356,7 @@ def main(
balance_offset=2,
assign_as_block=False,
constraints=[
PreShiftConstraint(days=2, start_date="2025-11-17", exclude_days=("Fri", "Sat", "Sun")),
PreShiftConstraint(days=2, start_date="2025-11-17", exclude_days=("Fri", "Sat", "Sun"), exclude_dates=["2026-04-06", "2026-05-04", "2026-05-25"]),
PostShiftConstraint(days=1, start_date="2025-11-17", exclude_days=("Fri", "Sat", "Sun"), allow_self=True),
],
),
+26 -4
View File
@@ -93,12 +93,14 @@ class PreShiftConstraint(BaseShiftConstraint):
days: Optional[int] = None
ignore_shifts: List[str] = []
exclude_days: List[str] = []
exclude_dates: List[datetime.date] = []
allow_self: bool = True
class PostShiftConstraint(BaseShiftConstraint):
days: Optional[int] = None
ignore_shifts: List[str] = []
exclude_days: List[str] = []
exclude_dates: List[datetime.date] = []
allow_self: bool = True
class BalanceAcrossGroupsConstraint(BaseShiftConstraint):
@@ -3110,10 +3112,12 @@ class RotaBuilder(object):
ignore_shifts = constraint.ignore_shifts + [constraint_shift.name]
else:
ignore_shifts = constraint.ignore_shifts
if week not in constraint.weeks:
if constraint.weeks is not None and week not in constraint.weeks:
continue
date_main = self.get_date_by_week_day(week, day)
for n in range(0, constraint.days):
if day in constraint.exclude_days:
# skip if main day excluded by weekday or specific date
if day in getattr(constraint, "exclude_days", []) or date_main in getattr(constraint, "exclude_dates", []):
continue
if day in constraint_shift.days:
# Only apply if worker can work this shift
@@ -3125,6 +3129,14 @@ class RotaBuilder(object):
]
except KeyError:
continue
# compute the pre date and skip if it's in exclude_dates
try:
pre_date = self.get_date_by_week_day(pre_map[n][1], pre_map[n][2])
except Exception:
pre_date = None
if pre_date is not None and pre_date in getattr(constraint, "exclude_dates", []):
continue
self.model.constraints.add(
1
>= works
@@ -3150,10 +3162,12 @@ class RotaBuilder(object):
ignore_shifts = constraint.ignore_shifts + [constraint_shift.name]
else:
ignore_shifts = constraint.ignore_shifts
if week not in constraint.weeks:
if constraint.weeks is not None and week not in constraint.weeks:
continue
date_main = self.get_date_by_week_day(week, day)
for n in range(0, constraint.days):
if day in constraint.exclude_days:
# skip if main day excluded by weekday or specific date
if day in getattr(constraint, "exclude_days", []) or date_main in getattr(constraint, "exclude_dates", []):
continue
if day in constraint_shift.days:
# Only apply if worker can work this shift
@@ -3165,6 +3179,14 @@ class RotaBuilder(object):
]
except KeyError:
continue
# compute the post date and skip if it's in exclude_dates
try:
post_date = self.get_date_by_week_day(post_map[n][1], post_map[n][2])
except Exception:
post_date = None
if post_date is not None and post_date in getattr(constraint, "exclude_dates", []):
continue
self.model.constraints.add(
1
>= works