Files
proc-rota/test.py
T
2020-05-24 10:16:25 +01:00

306 lines
8.9 KiB
Python

from pyomo.environ import *
from pyomo.opt import SolverFactory
from shifts import SingleShift, RotaBuilder, RotaResults, days
from workers import Worker
from copy import deepcopy
import math
import scipy.stats as stats
import statistics
import datetime
import itertools
import operator
use_neos = False
weeks_to_rota = 12
time_to_run = 1200
allow = 3000
sites = ("truro", "exeter", "torquay", "barnstaple", "plymouth")
Rota = RotaBuilder(datetime.date(2020, 9, 7),
weeks_to_rota=weeks_to_rota,
balance_offset_modifier=8,
max_weekend_frequency=2,
max_night_frequency=2)
Rota.constraint_options["limit_to_1_st1_on_nights"] = False
Rota.constraint_options["ensure_1_st4_plus_on_nights"] = False
Rota.constraint_options["balance_nights"] = False
Rota.constraint_options["constrain_time_off_after_nights"] = False
Rota.constraint_options["balance_nights_across_sites"] = False
Rota.constraint_options["balance_blocks"] = True
Rota.constraint_options["balance_weekends"] = True
Rota.constraint_options["prevent_monday_after_full_weekends"] = False
Rota.constraint_options["prevent_fridays_before_full_weekends"] = True
Rota.add_shifts(
# SingleShift(("exeter", ), "exeter_twilight", 12.5, days[:5]),
# # SingleShift(("truro", ),
# # "truro_twilight",
# # 12.5,
# # days[:5],
# # assign_as_block=False),
# #SingleShift(("torquay", ), "torquay_twilight", 12.5, days[:5]),
# #SingleShift(("plymouth", ), "plymouth_twilight", 12.5, days[:5]),
# SingleShift(("exeter", ),
# "weekend_exeter",
# 12.5,
# days[5:],
# assign_as_block=False),
# SingleShift(("truro", ),
# "weekend_truro",
# 12.5,
# days[5:],
# force_as_block=True),
# # SingleShift(("torquay", ),
# # "weekend_torquay",
# # 12.5,
# # days[5:],
# # assign_as_block=True),
# # SingleShift(("plymouth", ),
# # "weekend_plymouth",
# # 8,
# # days[5:],
# # workers_required=2,
# # assign_as_block=True),
# SingleShift((sites),
# "night_weekday",
# 12.25,
# days[:4],
# balance_offset=2,
# balance_weighting=1,
# workers_required=3,
# force_as_block=False,
# rota_on_nwds=True,
# constraints=["night"]),
# SingleShift((sites),
# "night_weekend",
# 12.25,
# days[4:],
# balance_offset=2,
# balance_weighting=1,
# workers_required=3,
# force_as_block=False,
# rota_on_nwds=True,
# constraints=["night"]),
# SingleShift(("exeter", ), "exeter_twilight", 12.5, days[:5]),
# SingleShift(("truro", ), "truro_twilight", 12.5, days[:5]),
# SingleShift(("torquay", ), "torquay_twilight", 12.5, days[:5]),
# SingleShift(("plymouth", ), "plymouth_twilight", 12.5, days[:5]),
# SingleShift(("exeter", ),
# "weekend_exeter",
# 12.5,
# days[5:],
# assign_as_block=True),
# SingleShift(("truro", ),
# "weekend_truro",
# 12.5,
# days[5:],
# assign_as_block=True),
# SingleShift(("torquay", ),
# "weekend_torquay",
# 12.5,
# days[5:],
# assign_as_block=True),
# SingleShift(("plymouth", ),
# "weekend_plymouth",
# 8,
# days[5:],
# workers_required=2,
# assign_as_block=True),
SingleShift(
(sites),
"night_weekday",
12.25,
days[:4],
balance_offset=2,
balance_weighting=1,
#hard_constrain_shift=False,
workers_required=3,
force_as_block=False,
rota_on_nwds=True,
constraints=["night"]),
SingleShift(
(sites),
"night_weekend",
12.25,
days[4:],
balance_offset=2,
balance_weighting=1,
#hard_constrain_shift=False,
workers_required=3,
force_as_block=False,
rota_on_nwds=True,
constraints=["night"]),
)
use_test_workers = False
# Import trainee data
if use_test_workers:
# Rota.add_workers([
# Worker(Rota,
# i,
# "Truro {}".format(i),
# "truro",
# 3,
# nwd=['Fri', "Sat", "Sun"]) for i in range(1, 2)
# ])
Rota.add_workers([
Worker(
Rota,
i,
"Truro {}".format(i),
"truro",
4,
) for i in range(1, 9)
])
Rota.add_workers([
Worker(Rota, i, "Plym {}".format(i), "plymouth", 4, 50)
for i in range(9, 17)
])
Rota.add_workers([
Worker(Rota,
i,
"Plym {}".format(i),
"plymouth",
4,
80,
end_date="2020/10/05") for i in range(17, 19)
])
# Rota.add_workers([
# Worker(
# Rota,
# i,
# "Exe {}".format(i),
# "exeter",
# 4,
# ) for i in range(20, 25)
# ])
# Rota.add_workers([
# Worker(
# Rota,
# i,
# "Torq {}".format(i),
# "torquay",
# 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:
n = n + 1
#print(row)
name, site, grade, fte, nwd, end_date, oop, extra = row
# Ignore trainees if fte == 0 (what are they doing here anyway)
if int(fte) < 1:
continue
nwds = nwd.split(",").title() if nwd else None
end_date = end_date if end_date else None
oop = oop.split("-") if oop else None
#print(nwds, end_date, oop)
# Rota.add_worker(
# Worker(Rota, n, name, site.lower(), int(grade[2]), 100,
# ))
Rota.add_worker(
Worker(Rota, n, name, site.lower(), int(grade[2]), int(fte),
nwds, end_date, oop))
print(0)
Rota.build_shifts_and_workers()
Rota.build_model()
#print(Rota.get_worker_details())
print(1)
opt = SolverFactory('cbc') # choose a solver
#opt = SolverFactory('ipopt') # choose a solver
print(2)
if use_neos:
solver_manager = SolverManagerFactory('neos') # Solve in neos server
results = solver_manager.solve(Rota.model, opt=opt, logfile="test.log")
else:
results = opt.solve(
Rota.model,
tee=True,
options={
"seconds": time_to_run,
"allow": allow,
},
logfile="test.log"
) # solve the model with the, options="seconds=60" selected solver
print(results)
results.solver.status
if not results.solver.status:
sys.exit(0)
ResultsHolder = RotaResults(Rota, results)
week_table = ResultsHolder.get_work_table() # list with the required workers
worker_timetable = ResultsHolder.get_worker_timetable()
worker_timetable_brief = ResultsHolder.get_worker_timetable_brief(
show_prefs=False, show_unavailable=False)
print(worker_timetable_brief)
print(Rota.get_workers_total_fte())
ResultsHolder.get_weekend_details()
#ResultsHolder.get_night_details()
with open("test.html", "w") as f:
f.write(ResultsHolder.get_worker_timetable_html(True))
# for i in ResultsHolder.rota.model.blocks_worker_shift_count:
# n = ResultsHolder.rota.model.blocks_worker_shift_count[i].value
# if n > 0:
# print(i, n)
# #print(ResultsHolder.rota.model.blocks_assigned[i].value)
# else:
# pass
# #print("SHIT")
# for i in ResultsHolder.rota.model.blocks_assigned:
# n = ResultsHolder.rota.model.blocks_assigned[i].value
# if n:
# print(i, n)
# #print(ResultsHolder.rota.model.blocks_assigned[i].value)
# else:
# pass
# ResultsHolder.export_rota_to_csv("rotas/"+rot)
# with open("cest", "a") as f:
# f.write(rot)
# f.write("\n\n")
# for s in Rota.shifts:
# f.write(s.get_shift_summary())
# f.write("\n")
# f.write("\n\n")
# f.write(worker_timetable_brief)
# f.write("\n\n\n")
#workers_no_pref = ResultsHolder.get_no_preference(Rota.model.no_pref) # list with the non-satisfied workers (work on Sat but not on Sun)
# worker_night_block = ResultsHolder.get_night_blocks()
# worker_shift_summary = ResultsHolder.get_shift_summary()
# multi = ResultsHolder.get_multinight()
#break