This commit is contained in:
Ross
2025-12-16 21:34:11 +00:00
parent 0b732a0b09
commit 8e580440e0
2 changed files with 22 additions and 29 deletions
+21 -29
View File
@@ -3,7 +3,7 @@ import datetime
import os import os
import sys import sys
import time import time
from rota_generator.shifts import MaxShiftsPerWeekConstraint, NoWorkers, PostShiftConstraint, PreShiftConstraint, RotaBuilder, SingleShift, WorkerRequirement, days from rota_generator.shifts import MaxShiftsPerWeekConstraint, NoWorkers, PostShiftConstraint, PreShiftConstraint, RotaBuilder, SingleShift, WorkerRequirement, days, RotaConstraintOptions
import typer import typer
import subprocess import subprocess
import pandas as pd import pandas as pd
@@ -38,11 +38,11 @@ def extract_leave_and_rota_from_calender(calender_df):
# Get rota data from the second row (index 1), starting from column 5 (index 4) # Get rota data from the second row (index 1), starting from column 5 (index 4)
# Extract the rota data for each worker from the first data row (index 0), columns 5 onwards # Extract the rota data for each worker from the first data row (index 0), columns 5 onwards
rota_row = calender_df.iloc[0] #rota_row = calender_df.iloc[0]
#rota_data = {} #rota_data = {}
for i, worker in enumerate(worker_names): #for i, worker in enumerate(worker_names):
assignment = str(rota_row.iloc[5 + i]).strip() if len(rota_row) > 5 + i else "" # assignment = str(rota_row.iloc[5 + i]).strip() if len(rota_row) > 5 + i else ""
#rota_data[worker] = f"rota {assignment.lower()}" # #rota_data[worker] = f"rota {assignment.lower()}"
leave_requests = [] leave_requests = []
work_requests = [] work_requests = []
@@ -55,6 +55,9 @@ def extract_leave_and_rota_from_calender(calender_df):
date = datetime.datetime.strptime(date_str, "%Y-%m-%d").date() date = datetime.datetime.strptime(date_str, "%Y-%m-%d").date()
except Exception: except Exception:
continue # skip rows with invalid date continue # skip rows with invalid date
if date < datetime.date(2026, 2, 16):
continue # skip rows before rota start date
for i, worker in enumerate(worker_names): for i, worker in enumerate(worker_names):
if not worker or worker.lower() == "nan": if not worker or worker.lower() == "nan":
continue continue
@@ -91,6 +94,7 @@ def extract_shift_assignments_from_rota(rota_df):
shifts = ["twilight", "oncall"] shifts = ["twilight", "oncall"]
assignments = [] assignments = []
skipped_rows = 0
for idx, row in rota_df.iloc[2:].iterrows(): for idx, row in rota_df.iloc[2:].iterrows():
week_start_str = str(row.iloc[0]).strip().split(" ")[0] # Get the week start date from the first column (index 0) week_start_str = str(row.iloc[0]).strip().split(" ")[0] # Get the week start date from the first column (index 0)
try: try:
@@ -99,6 +103,10 @@ def extract_shift_assignments_from_rota(rota_df):
print(f"Invalid date format in row {idx}: {week_start_str}") print(f"Invalid date format in row {idx}: {week_start_str}")
continue # skip rows with invalid date continue # skip rows with invalid date
if week_start < datetime.date(2026, 2, 16):
skipped_rows += 1
continue # skip rows before rota start date
for i, day in enumerate(days): for i, day in enumerate(days):
for j, shift in enumerate(shifts): for j, shift in enumerate(shifts):
if i < 5: if i < 5:
@@ -122,7 +130,7 @@ def extract_shift_assignments_from_rota(rota_df):
shift = "weekend" shift = "weekend"
assignments.append({ assignments.append({
"week_start": week_start, "week_start": week_start,
"week": idx-1, # 1-indexed week number "week": idx-1-skipped_rows, # 1-indexed week number
"day": day, "day": day,
"date": week_start + datetime.timedelta(days=i), "date": week_start + datetime.timedelta(days=i),
"shift": shift, "shift": shift,
@@ -147,7 +155,7 @@ def load_workers():
# Extract sheets # Extract sheets
days_df = xls["Days"] days_df = xls["Days"]
calender_df = xls["Calender"] calender_df = xls["Calendar"]
rota_df = xls["Rota"] rota_df = xls["Rota"]
@@ -193,7 +201,7 @@ def load_workers():
for i, day in enumerate(weekday_cols): for i, day in enumerate(weekday_cols):
val = row.iloc[3 + i] val = row.iloc[3 + i]
# You can adjust the logic below depending on your marking scheme (e.g. "Y", "Yes", "1", etc.) # You can adjust the logic below depending on your marking scheme (e.g. "Y", "Yes", "1", etc.)
available = not ("no" in str(val).strip().lower()) available = not ("no" in str(val).strip().lower() or str(val).strip().lower() == "yes*")
availability[day] = available availability[day] = available
workers_days.append({"initial": initial, "name": name, "availability": availability, "requests": requests}) workers_days.append({"initial": initial, "name": name, "availability": availability, "requests": requests})
@@ -329,14 +337,12 @@ def main(
balance_offset_modifier=bom, balance_offset_modifier=bom,
name="cons rota feb 2026", name="cons rota feb 2026",
allow_force_assignment_with_leave_conflict=True, allow_force_assignment_with_leave_conflict=True,
constraint_options=RotaConstraintOptions(
balance_weekends=True,
max_shifts_per_week=6,
max_days_per_week_block=[(4, 2), (6, 3)],
),
) )
# Rota = RotaBuilder(start_date, weeks_to_rota=20, balance_offset_modifier=1)
Rota.constraint_options["balance_weekends"] = True
#Rota.constraint_options["max_weekend_frequency"] = 2
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 #Rota.enable_unavailabilities = False
@@ -351,10 +357,6 @@ def main(
constraints=[ 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")),
PostShiftConstraint(days=1, start_date="2025-11-17", exclude_days=("Fri", "Sat", "Sun"), allow_self=True), PostShiftConstraint(days=1, start_date="2025-11-17", exclude_days=("Fri", "Sat", "Sun"), allow_self=True),
#{
# "name": "max_shifts_per_week",
# "options": 3,
#}
], ],
), ),
SingleShift( SingleShift(
@@ -365,9 +367,6 @@ def main(
days=days[5:], days=days[5:],
balance_offset=1, balance_offset=1,
constraints=[ constraints=[
#{"name": "pre", "options": 2},
#{"name": "post", "options": 1},
PostShiftConstraint(days=1, start_date="2025-11-17", ignore_shifts=["oncall"]), PostShiftConstraint(days=1, start_date="2025-11-17", ignore_shifts=["oncall"]),
], ],
display_char="a", display_char="a",
@@ -380,10 +379,6 @@ def main(
length=12.5, length=12.5,
days=days[5:], days=days[5:],
balance_offset=2, balance_offset=2,
#constraint=[
# {"name": "pre", "options": 1},
# {"name": "post", "options": 1},
#],
display_char="b", display_char="b",
), ),
SingleShift( SingleShift(
@@ -398,11 +393,8 @@ 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"], exclude_days=("Sat", "Sun")), 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), MaxShiftsPerWeekConstraint(max_shifts=1),
#{"name": "post", "options": 1},
], ],
), ),
) )
+1
View File
@@ -383,6 +383,7 @@ class RotaBuilder(object):
SHIFT_BOUNDS=SHIFT_BOUNDS, SHIFT_BOUNDS=SHIFT_BOUNDS,
name: str = "", name: str = "",
bank_holidays: dict[datetime.date, str] = bank_holiday_map, bank_holidays: dict[datetime.date, str] = bank_holiday_map,
constraint_options: RotaConstraintOptions | dict | None = None,
): ):
self.name = name self.name = name