From b9ca76aea11ef18b52134680335c8935013a2245 Mon Sep 17 00:00:00 2001 From: Ross Date: Thu, 5 Jun 2025 23:40:17 +0100 Subject: [PATCH] update rota --- gen_cons.py | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 gen_cons.py diff --git a/gen_cons.py b/gen_cons.py new file mode 100644 index 0000000..dcb4e21 --- /dev/null +++ b/gen_cons.py @@ -0,0 +1,196 @@ +import datetime +import os +import sys +import time +from rota.shifts import NoWorkers, RotaBuilder, SingleShift, WorkerRequirement, days +import typer +import subprocess + +from rota.workers import ( + Worker, + NotAvailableToWork, + NonWorkingDays, + WorkRequests, + PreferenceNotToWork, + OutOfProgramme, +) + + +app = typer.Typer() + + +sites = ("rota a", "rota b", "rota c") + + +@app.command() +def main( + suspend: bool = False, + solve: bool = True, + time_to_run: int = 60 * 60, + ratio: float = 0.001, + start_date: datetime.datetime = "2025-09-01", + weeks: int = 16, + bom: int = 1, +): + rota_start_date = start_date.date() + suspend_on_finish = suspend + + Rota = RotaBuilder( + rota_start_date, + weeks_to_rota=weeks, + balance_offset_modifier=bom, + name="cons rota", + ) + # Rota = RotaBuilder(start_date, weeks_to_rota=20, balance_offset_modifier=1) + Rota.constraint_options["balance_shifts"] = False + Rota.constraint_options["balance_shifts_quadratic"] = True + Rota.constraint_options["balance_weekends"] = True + Rota.constraint_options["max_night_frequency"] = 3 # 3 + Rota.constraint_options["max_night_frequency_week_exclusions"] = [] + Rota.constraint_options["max_weekend_frequency"] = 4 + Rota.constraint_options["hard_constrain_pair_separation"] = True + Rota.constraint_options["max_shifts_per_week"] = 3 + # Rota.constraint_options["avoid_st2_first_month"] = True + + Rota.add_shifts( + SingleShift( + sites=("rota a",), + name="oncall weekday", + length=12.5, + days=days[:4], + balance_offset=2, + constraint=[ + { + "name": "max_shifts_per_week", + "options": 3, + } + ], + ), + SingleShift( + sites=("rota a",), + name="Oncall weekend", + length=12.5, + days=days[4:], + balance_offset=2, + assign_as_block=True, + constraint=[ + { + "name": "max_shifts_per_week", + "options": 3, + }, + {"name": "pre", "options": 2}, + {"name": "post", "options": 2}, + ], + ), + SingleShift( + sites=("rota a",), + name="weekend a", + workers_required=1, + length=12.5, + days=days[5:], + balance_offset=2, + constraint=[ + {"name": "pre", "options": 2}, + {"name": "post", "options": 2}, + ], + display_char="a", + ), + SingleShift( + sites=("rota b", "rota c"), + name="weekend b", + workers_required=1, + length=12.5, + days=days[5:], + balance_offset=2, + constraint=[ + {"name": "pre", "options": 2}, + {"name": "post", "options": 2}, + ], + display_char="b", + ), + SingleShift( + sites=( + "rota a", + "rota b", + ), + name="evening", + workers_required=1, + length=12.5, + days=days[:5], + balance_offset=2, + constraint=[], + ), + ) + + # Rota.add_grade_constraint_by_week([2], [1, 2], ["night_weekday", "night_weekend"]) + + load_leave = True + Rota.build_shifts() + # Rota.add_grade_constraint_by_week([2], [1], Rota.get_shift_names()) + + for w in range(1, 12): + w = Worker(name=f"A{w:02d}", site="rota a", grade=1) + + print(w) + + Rota.add_worker(w) + + for w in range(12, 21): + w = Worker( + name=f"B{w}", + site="rota b", + grade=1, + nwds=[ + NonWorkingDays( + day="Fri", + ) + ], + ) + + print(w) + + Rota.add_worker(w) + + # Rota.build_workers() + # Rota.build_model() + + solver_options = {"ratio": ratio, "seconds": time_to_run, "threads": 10} + # solver_options = {"seconds": time_to_run, "threads": 10} + + # start_time = time.time() + Rota.build_and_solve(solver_options, export=True, solve=solve, solver="appsi_highs") + + # Rota.solve_shifts_by_block(solver_options, block_length=13) + # Rota.solve_shifts_individually(solver_options) + # Rota.solve_model(options=solver_options) + # end_time = time.time() + + # print(f"Time taken {end_time-start_time}") + + # print(Rota.get_worker_details()) + + # optimizer = SolverFactory('cbc') + # result = optimizer.solve(prob,tee=True) + # result.Solver.Status = SolverStatus.warning + # prob.solutions.load_from(result) + + # ResultsHolder = RotaResults(Rota) + + # worker_timetable_brief = ResultsHolder.get_worker_timetable_brief( + # show_prefs=False, show_unavailable=False + # ) + # + # print(worker_timetable_brief) + + # Rota.export_rota_to_html("proc_rota") + # Rota.export_rota_to_csv("rota") + subprocess.run( + ["scp", Rota.exported_rota_file, "ross@46.101.13.46:proc/proc-rota/output/"] + ) + + if suspend_on_finish: + os.system("systemctl suspend") + + +if __name__ == "__main__": + app()