This commit is contained in:
Ross
2021-06-07 23:23:12 +01:00
parent 2847e1c987
commit 5c54257065
6 changed files with 717 additions and 561 deletions
+77 -63
View File
@@ -2,6 +2,7 @@ from pyomo.environ import *
from pyomo.opt import SolverFactory
from pyomo.opt.results import SolverStatus
from shifts import SingleShift, RotaBuilder, RotaResults, days
from workers import Worker
@@ -16,45 +17,49 @@ import statistics
import datetime
import itertools
import operator
import sys
use_neos = True
solve = True
weeks_to_rota = 26
time_to_run = 28500
#allow = 1000
allow = 10000
sites = ("truro", "exeter", "torquay", "barnstaple", "plymouth")
use_cplex = True
Rota = RotaBuilder(datetime.date(2021, 3, 8),
sites = ("truro", "exeter", "torbay", "barnstaple", "plymouth")
Rota = RotaBuilder(datetime.date(2021, 9, 6),
weeks_to_rota=weeks_to_rota,
balance_offset_modifier=1,
max_weekend_frequency=2,
max_night_frequency=2)
Rota.constraint_options["limit_to_1_st1_on_nights"] = True
Rota.constraint_options["limit_to_1_st2_on_nights"] = True
Rota.constraint_options["ensure_1_st4_plus_on_nights"] = True
Rota.constraint_options["balance_nights"] = True
Rota.constraint_options["constrain_time_off_after_nights"] = True
Rota.constraint_options["balance_nights_across_sites"] = True
Rota.constraint_options["balance_blocks"] = True
Rota.constraint_options["balance_weekends"] = True
Rota.constraint_options["avoid_st1_first_month"] = False
Rota.constraint_options["max_weekends"] = 10
Rota.constraint_options["avoid_st1_first_month"] = True
Rota.constraint_options["max_weekends"] = 20
Rota.constraint_options["prevent_monday_and_tuesday_after_full_weekends"] = [
"truro", "torquay", "exeter"
"truro", "torbay", "exeter"
]
Rota.constraint_options["prevent_monday_after_full_weekends"] = ["plymouth"]
Rota.constraint_options["prevent_fridays_before_full_weekends"] = ["plymouth"]
Rota.constraint_options["prevent_thursdays_before_full_weekends"] = [
"truro", "torquay", "exeter"
"truro", "torbay", "exeter"
]
Rota.add_shifts(
SingleShift(("exeter", ), "exeter_twilight", 12.5, days[:4]),
SingleShift(("truro", ), "truro_twilight", 12.5, days[:4]),
SingleShift(("torquay", ),
"torquay_twilight",
SingleShift(("torbay", ),
"torbay_twilight",
12.5,
days[:4],
balance_offset=3,
@@ -80,8 +85,8 @@ Rota.add_shifts(
balance_offset=3,
rota_on_nwds=True,
force_as_block=True),
SingleShift(("torquay", ),
"weekend_torquay",
SingleShift(("torbay", ),
"weekend_torbay",
12.5,
days[4:],
balance_offset=3,
@@ -137,7 +142,8 @@ Rota.build_shifts_and_workers()
if load_leave:
import leave
leave, requests, site_prefs = leave.load_leave(Rota)
workers = leave.load_leave(Rota)
# Import trainee data
if use_test_workers:
@@ -185,69 +191,65 @@ if use_test_workers:
# Rota,
# i,
# "Torq {}".format(i),
# "torquay",
# "torbay",
# 2,
# 50,
# ) for i in range(25, 29)
# ])
else:
import csv
with open('trainees.csv', newline='') as f:
reader = csv.reader(f, delimiter=',', quotechar='|')
next(reader) # skip header
n = 0
for row in reader:
for worker in workers:
n = n + 1
name, site, grade, fte, nwd, end_date, oop, twi, twi_we, night, night_we, pw1, pw2, extra = row
name = worker
w = workers[worker]
site = w["site"]
grade = w["grade"]
fte = w["fte"]
nwd = w["nwd"]
end_date = w["end_date"]
oop = w["oop"]
grade = w["grade"]
#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 int(fte) < 1:
if fte == 0 or fte == "0" or fte == "":
continue
nwds = nwd.split("/") if nwd else None
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)
#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 = []
work_requests = []
site_pref = 1
if load_leave:
if name not in leave:
raise ValueError(
"{} not found it leave requests".format(name))
leave_requests = w["leave"]
work_requests = w["requests"]
site_pref = w["site_pref"]
leave_requests = [i[0] for i in leave[name] if i[0] != ""]
work_requests = [i for i in requests[name] if i[0] != ""]
site_pref = int(site_prefs[name][0])
#print(nwds, end_date, oop)
# Rota.add_worker(
# Worker(Rota, n, name, site.lower(), int(grade[2]), 100,
@@ -285,13 +287,13 @@ else:
name,
site.lower(),
int(grade[2]),
int(fte),
float(fte),
nwds,
end_date,
oop,
not_available_to_work=leave_requests,
work_requests=work_requests,
night_at_derriford=site_pref,
proc_site=site_pref,
previous_shifts=previous_shifts))
print("Building model")
@@ -299,10 +301,22 @@ Rota.build_shifts_and_workers()
Rota.build_model()
print(Rota.get_worker_details())
if not solve:
sys.exit(0)
#print(Rota.get_worker_details())
print("Setting up solver")
opt = SolverFactory('cplex') # choose a solver
#opt = SolverFactory('ipopt') # choose a solver
if use_cplex:
opt = SolverFactory('cplex')
else:
opt = SolverFactory('cbc') # choose a solver
#opt = SolverFactory('ipopt') # choose a solver
opt.options['allow'] = allow
opt.options['seconds'] = time_to_run
print("Solving")
if use_neos:
@@ -347,7 +361,7 @@ ResultsHolder.get_weekend_details()
with open("test.html", "w") as f:
f.write(ResultsHolder.get_worker_timetable_html(True))
ResultsHolder.export_rota_to_csv()
ResultsHolder.export_rota_to_csv("rota")
# for i in ResultsHolder.rota.model.blocks_worker_shift_count:
# n = ResultsHolder.rota.model.blocks_worker_shift_count[i].value