Files
proc-rota/gen.py
T
2022-07-14 12:12:50 +01:00

320 lines
9.4 KiB
Python

import datetime
import os
import sys
import time
from rota.shifts import NoWorkers, RotaBuilder, SingleShift, days
sites = (
"truro",
"exeter",
"torbay",
"barnstaple",
"plymouth",
"taunton",
"proc",
)
from rota.workers import (
Worker,
NotAvailableToWork,
NonWorkingDays,
WorkRequests,
PreferenceNotToWork,
OutOfProgramme,
)
suspend_on_finish = False
solve = True
time_to_run = 258500
time_to_run = 36000
time_to_run = 200
# allow = 5000
ratio = 0.05
start_date = datetime.date(2022, 9, 5)
#start_date = datetime.date(2022, 10, 10)
Rota = RotaBuilder(start_date, weeks_to_rota=26)
Rota.constraint_options["balance_weekends"] = True
Rota.constraint_options["max_night_frequency"] = 3
Rota.constraint_options["max_weekend_frequency"] = 3
# 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=2,
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": "post", "options": 2}, {"name": "pre", "options": 5}],
# #constraint=[{"name": "pre", "options": 2}, {"name": "post", "options": 2},],
#),
#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": "pre", "options": 2}, {"name": "post", "options": 2}],
# # 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": "pre", "options": 2}, {"name": "post", "options": 2}],
# # 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": "pre", "options": 2}, {"name": "post", "options": 2}],
#),
#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": "pre", "options": 2}, {"name": "post", "options": 2}],
#),
#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),
# 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": "pre", "options": 5},
# {"name": "post", "options": 5},
# {"name": "require_remote_site_presence_week", "options": ("plymouth", 1)},
# ],
#),
#SingleShift(
# sites=(sites),
# 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": "pre", "options": 5},
# {"name": "post", "options": 5},
# {"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}
start_time = time.time()
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")
if suspend_on_finish:
os.system("systemctl suspend")