diff --git a/gen_cons.py b/gen_cons.py index d1c418b..f6bafa6 100644 --- a/gen_cons.py +++ b/gen_cons.py @@ -3,7 +3,7 @@ import datetime import os import sys 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 subprocess 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) # 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 = {} - for i, worker in enumerate(worker_names): - assignment = str(rota_row.iloc[5 + i]).strip() if len(rota_row) > 5 + i else "" - #rota_data[worker] = f"rota {assignment.lower()}" + #for i, worker in enumerate(worker_names): + # assignment = str(rota_row.iloc[5 + i]).strip() if len(rota_row) > 5 + i else "" + # #rota_data[worker] = f"rota {assignment.lower()}" leave_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() except Exception: 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): if not worker or worker.lower() == "nan": continue @@ -91,6 +94,7 @@ def extract_shift_assignments_from_rota(rota_df): shifts = ["twilight", "oncall"] assignments = [] + skipped_rows = 0 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) try: @@ -99,6 +103,10 @@ def extract_shift_assignments_from_rota(rota_df): print(f"Invalid date format in row {idx}: {week_start_str}") 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 j, shift in enumerate(shifts): if i < 5: @@ -122,7 +130,7 @@ def extract_shift_assignments_from_rota(rota_df): shift = "weekend" assignments.append({ "week_start": week_start, - "week": idx-1, # 1-indexed week number + "week": idx-1-skipped_rows, # 1-indexed week number "day": day, "date": week_start + datetime.timedelta(days=i), "shift": shift, @@ -147,7 +155,7 @@ def load_workers(): # Extract sheets days_df = xls["Days"] - calender_df = xls["Calender"] + calender_df = xls["Calendar"] rota_df = xls["Rota"] @@ -193,7 +201,7 @@ def load_workers(): for i, day in enumerate(weekday_cols): val = row.iloc[3 + i] # 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 workers_days.append({"initial": initial, "name": name, "availability": availability, "requests": requests}) @@ -329,14 +337,12 @@ def main( balance_offset_modifier=bom, name="cons rota feb 2026", 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 @@ -351,10 +357,6 @@ def main( 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"), allow_self=True), - #{ - # "name": "max_shifts_per_week", - # "options": 3, - #} ], ), SingleShift( @@ -365,9 +367,6 @@ def main( days=days[5:], balance_offset=1, constraints=[ - #{"name": "pre", "options": 2}, - - #{"name": "post", "options": 1}, PostShiftConstraint(days=1, start_date="2025-11-17", ignore_shifts=["oncall"]), ], display_char="a", @@ -380,10 +379,6 @@ def main( length=12.5, days=days[5:], balance_offset=2, - #constraint=[ - # {"name": "pre", "options": 1}, - # {"name": "post", "options": 1}, - #], display_char="b", ), SingleShift( @@ -398,11 +393,8 @@ 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"], 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}, ], ), ) diff --git a/rota_generator/shifts.py b/rota_generator/shifts.py index 4d5c44c..6bb9b3b 100644 --- a/rota_generator/shifts.py +++ b/rota_generator/shifts.py @@ -383,6 +383,7 @@ class RotaBuilder(object): SHIFT_BOUNDS=SHIFT_BOUNDS, name: str = "", bank_holidays: dict[datetime.date, str] = bank_holiday_map, + constraint_options: RotaConstraintOptions | dict | None = None, ): self.name = name