import datetime import os import sys from rota.shifts import NoWorkers, RotaBuilder, SingleShift, days sites = ( "truro", "exeter", "torbay", "barnstaple", "plymouth", "taunton", "proc", "truro no proc", ) from rota.workers import ( Worker, NotAvailableToWork, NonWorkingDays, WorkRequests, PreferenceNotToWork, OutOfProgramme, ) suspend_on_finish = False solve = True time_to_run = 258500 # allow = 5000 ratio = 0.5 start_date = datetime.date(2022, 9, 5) Rota = RotaBuilder(start_date, weeks_to_rota=12) Rota.constraint_options["balance_weekends"] = True Rota.constraint_options["max_night_frequency"] = 1 Rota.constraint_options["max_weekend_frequency"] = 1 # Rota.constraint_options["avoid_st2_first_month"] = True Rota.add_shifts( SingleShift( sites=("exeter", "exeter twilights and weekends"), name="exeter_twilight", length=12.5, days=days[:5], balance_offset=3, constraint=[{"name": "max_shifts_per_week", "options": 2}], ), SingleShift( sites=("truro", "truro twilights"), name="truro_twilight", length=12.5, days=days[:4], balance_offset=3, ), SingleShift( sites=("torbay", "torbay twilights"), name="torbay_twilight", length=12.5, days=days[:4], balance_offset=5, assign_as_block=True, ), SingleShift( sites=("plymouth", "plymouth twilights"), name="plymouth_twilight", length=12.5, days=days[:5], balance_offset=3, workers_required=1, constraint=[{"name": "max_shifts_per_week", "options": 2}], ), SingleShift( sites=("exeter", "exeter twilights and weekends"), name="weekend_exeter", length=12.5, days=days[5:], balance_offset=4, rota_on_nwds=True, force_as_block=True, constraint=[{"name": "preclear2"}, {"name": "postclear2"}, {"name": "single_shift_block_per_week"}], #constraint=[{"name": "preclear2"}, {"name": "postclear2"},], ), SingleShift( sites=("truro",), name="weekend_truro", length=12.5, days=days[4:], balance_offset=4, rota_on_nwds=True, force_as_block=True, constraint=[{"name": "preclear2"}, {"name": "postclear2"}], # force_as_block_unless_nwd=True ), SingleShift( sites=("torbay",), name="weekend_torbay", length=12.5, days=days[4:], balance_offset=4, rota_on_nwds=True, force_as_block=True, constraint=[{"name": "preclear2"}, {"name": "postclear2"}], # force_as_block_unless_nwd=True ), SingleShift( sites=("plymouth",), name="weekend_plymouth1", length=8, days=days[5:], balance_offset=4, workers_required=1, rota_on_nwds=True, force_as_block=True, constraint=[{"name": "preclear2"}, {"name": "postclear2"}], ), SingleShift( sites=("plymouth",), name="weekend_plymouth2", length=8, days=days[5:], balance_offset=4, workers_required=1, rota_on_nwds=True, force_as_block=True, constraint=[{"name": "preclear2"}, {"name": "postclear2"}], ), SingleShift( sites=("plymouth", "plymouth twilights"), name="plymouth_bank_holidays", length=8, days=days[5:], balance_offset=2, workers_required=1, rota_on_nwds=True, force_as_block=False, bank_holidays_only=True, ), SingleShift( sites=(sites[:-1]), name="night_weekday", length=12.25, days=days[:4], balance_offset=4.9, balance_weighting=1, # hard_constrain_shift=False, workers_required=3, force_as_block=True, rota_on_nwds=True, constraint=[ {"name": "night"}, {"name": "preclear2"}, {"name": "postclear2"}, {"name": "require_remote_site_presence_week", "options": ("plymouth", 1)}, ], ), SingleShift( sites=(sites[:-1]), name="night_weekend", length=12.25, days=days[4:], balance_offset=3.9, balance_weighting=1, # hard_constrain_shift=False, workers_required=3, force_as_block=True, rota_on_nwds=True, constraint=[ {"name": "night"}, {"name": "preclear2"}, {"name": "postclear2"}, {"name": "require_remote_site_presence_week", "options": ("plymouth", 1)}, ], ), ) Rota.add_grade_constraint_by_week([2], [1, 2, 3, 4], ["night_weekday", "night_weekend"]) load_leave = True Rota.build_shifts() Rota.add_grade_constraint_by_week([2], [1, 2], Rota.get_shift_names()) if load_leave: import leave workers = leave.load_leave(Rota) n = 0 for worker in workers: n = n + 1 worker_name = worker w = workers[worker] site = w["site"] grade = w["grade"] fte = float(w["fte"]) * 100 nwd = w["nwd"] end_date = w["end_date"] start_date = w["start_date"] oop = w["oop"] grade = w["grade"] pair = w["pair"] # name, site, grade, fte, nwd, end_date, oop, twi, twi_we, night, night_we, pw1, pw2, extra = row # Ignore trainees if fte == 0 (what are they doing here anyway) if fte == 0 or fte == "0" or fte == "": continue nwds = [] if nwd: for i in nwd.split(","): nwd_start_date = Rota.start_date nwd_end_date = Rota.rota_end_date if "[" in i: a, b = i.split("[")[1][:-1].split("-") nwd_start_date = datetime.datetime.strptime(a, "%d/%m/%y").date() nwd_end_date = datetime.datetime.strptime(b, "%d/%m/%y").date() nwds.append( NonWorkingDays( day=i.split("[")[0].strip()[:3].capitalize(), start_date=nwd_start_date, end_date=nwd_end_date, ) ) # nwds = [i.strip()[:3].capitalize() for i in nwd.split("/")] if nwd else None end_date = end_date if end_date else None oop = oop.split("-") if oop else None previous_shifts = {} # if twi: # worked = twi.split(" ")[0] # allocated = twi.split(" ")[1][1:-1] # previous_shifts["{}_twilight".format(site.lower())] = (worked, allocated) # if twi_we: # worked = twi_we.split(" ")[0] # allocated = twi_we.split(" ")[1][1:-1] # previous_shifts["weekend_{}".format(site.lower())] = (worked, allocated) # if night: # worked = night.split(" ")[0] # allocated = night.split(" ")[1][1:-1] # previous_shifts["night_weekday".format(site.lower())] = (worked, allocated) # if night_we: # worked = night_we.split(" ")[0] # allocated = night_we.split(" ")[1][1:-1] # previous_shifts["night_weekend".format(site.lower())] = (worked, allocated) # if pw1: # worked = pw1.split(" ")[0] # allocated = pw1.split(" ")[1][1:-1] # previous_shifts["weekend_plymouth1"] = (worked, allocated) # if pw2: # worked = pw2.split(" ")[0] # allocated = pw2.split(" ")[1][1:-1] # previous_shifts["weekend_plymouth2"] = (worked, allocated) leave_requests = w["leave"] work_requests = w["requests"] site_pref = w["site_pref"] Rota.add_worker( Worker( name=worker_name, site=site.lower(), grade=int(grade[2]), id=n, fte=int(fte), nwds=nwds, start_date=start_date, end_date=end_date, opp=oop, not_available_to_work=leave_requests, work_requests=work_requests, remote_site=site_pref, previous_shifts=previous_shifts, pair=pair, shift_balance_extra=w["shift_balance_extra"], bank_holiday_extra=w["bank_holiday_extra"], ) ) Rota.build_workers() Rota.build_model() solver_options = {"ratio": ratio, "seconds": time_to_run, "threads": 10} Rota.solve_model(options=solver_options) # 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") if suspend_on_finish: os.system("systemctl suspend")