many improvements + restructure

This commit is contained in:
Ross
2022-02-21 13:53:13 +00:00
parent cbe99ea3d8
commit 0c795e518b
17 changed files with 653 additions and 1927 deletions
-372
View File
@@ -1,372 +0,0 @@
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
import datetime
import sys
import os
use_neos = False
use_cplex = False
solve = True
weeks_to_rota = 26
time_to_run = 258500
# allow = 5000
ratio = 0.001
use_live_rota = False
suspend_on_finish = True
sites = ("truro", "exeter", "torbay", "barnstaple", "plymouth", "proc", "truro no proc")
Rota = RotaBuilder(
# datetime.date(2022, 6, 6),
datetime.date(2022, 3, 7),
# Rota = RotaBuilder(datetime.date(2021, 12, 6),
# Rota = RotaBuilder(datetime.date(2021, 11, 8),
weeks_to_rota=weeks_to_rota,
balance_offset_modifier=1,
max_weekend_frequency=2,
max_night_frequency=2,
use_shift_balance_extra=True,
use_bank_holiday_extra=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["balance_shifts_over_workers"] = True
Rota.constraint_options["balance_bank_holidays"] = True
Rota.constraint_options["avoid_st2_first_month"] = False
Rota.constraint_options["hard_constrain_pair_separation"] = True
Rota.constraint_options["max_weekends"] = 7
Rota.constraint_options["prevent_monday_and_tuesday_after_full_weekends"] = [
"truro",
"torbay",
"exeter",
"plymouth",
"plymouth no proc",
"truro no proc",
]
Rota.constraint_options["prevent_monday_after_full_weekends"] = [
"plymouth",
"plymouth no proc",
]
Rota.constraint_options["prevent_fridays_before_full_weekends"] = [
"plymouth",
"plymouth no proc",
]
Rota.constraint_options["prevent_thursdays_before_full_weekends"] = [
"truro",
"torbay",
"exeter",
"truro no proc",
]
Rota.add_shifts(
SingleShift(
("exeter",),
"exeter_twilight",
12.5,
days[:4],
balance_offset=3,
constraints=["max_2_shifts_per_week"],
),
SingleShift(
("truro", "truro no proc"), "truro_twilight", 12.5, days[:4], balance_offset=3
),
SingleShift(
("torbay", "torbay twilights"),
"torbay_twilight",
12.5,
days[:4],
balance_offset=5,
assign_as_block=True,
),
SingleShift(
("plymouth", "plymouth_twilights"),
"plymouth_twilight",
12.5,
days[:5],
balance_offset=3,
workers_required=1,
constraints=["max_2_shifts_per_week"],
),
SingleShift(
("exeter",),
"weekend_exeter",
12.5,
days[4:],
balance_offset=4,
rota_on_nwds=True,
force_as_block=True,
),
SingleShift(
("truro", "truro no proc"),
"weekend_truro",
12.5,
days[4:],
balance_offset=4,
rota_on_nwds=True,
force_as_block=True,
# force_as_block_unless_nwd=True
),
SingleShift(
("torbay",),
"weekend_torbay",
12.5,
days[4:],
balance_offset=4,
rota_on_nwds=True,
force_as_block=True,
# force_as_block_unless_nwd=True
),
SingleShift(
("plymouth",),
"weekend_plymouth1",
8,
days[5:],
balance_offset=4,
workers_required=1,
rota_on_nwds=True,
force_as_block=True,
),
SingleShift(
("plymouth",),
"weekend_plymouth2",
8,
days[5:],
balance_offset=4,
workers_required=1,
rota_on_nwds=True,
force_as_block=True,
),
SingleShift(
("plymouth", "plymouth_twilights"),
"plymouth_bank_holidays",
8,
days[5:],
balance_offset=2,
workers_required=1,
rota_on_nwds=True,
force_as_block=False,
bank_holidays_only=True,
),
SingleShift(
(sites[:-1]),
"night_weekday",
12.25,
days[:4],
balance_offset=4.9,
balance_weighting=1,
# hard_constrain_shift=False,
workers_required=3,
force_as_block=False,
rota_on_nwds=True,
constraints=["night"],
),
SingleShift(
(sites[:-1]),
"night_weekend",
12.25,
days[4:],
balance_offset=3.9,
balance_weighting=1,
# hard_constrain_shift=False,
workers_required=3,
force_as_block=False,
rota_on_nwds=True,
constraints=["night"],
),
)
use_test_workers = False
load_leave = True
Rota.build_shifts()
if load_leave:
import leave
workers = leave.load_leave(Rota, use_live_rota)
# 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,
"Truro {}".format(i),
"truro",
4,
id=i,
)
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)
]
)
else:
n = 0
for worker in workers:
n = n + 1
worker_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"]
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 = None
if nwd:
nwds = []
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(
(
i.split("[")[0].strip()[:3].capitalize(),
nwd_start_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(
Rota,
worker_name,
site.lower(),
int(grade[2]),
n,
int(fte),
nwds,
end_date,
oop,
not_available_to_work=leave_requests,
work_requests=work_requests,
proc_site=site_pref,
previous_shifts=previous_shifts,
pair=pair,
shift_balance_extra=w["shift_balance_extra"],
bank_holiday_extra=w["bank_holiday_extra"],
)
)
print("Building model")
Rota.build_workers()
Rota.sites.add("plymouth_twilights")
Rota.build_model()
print(Rota.get_worker_details())
if not solve:
ResultsHolder = RotaResults(Rota)
ResultsHolder.export_rota_to_html()
sys.exit(0)
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)
ResultsHolder.export_rota_to_html()
ResultsHolder.export_rota_to_csv("rota")
if suspend_on_finish:
os.system("systemctl suspend")