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 datetime use_neos = False weeks_to_rota = 52 sites = ("truro", "exeter", "torquay", "barnstaple", "plymouth") Rota = RotaBuilder( datetime.date(2020, 9, 7), weeks_to_rota=weeks_to_rota, balance_offset_modifier=1, max_weekend_frequency=2, max_night_frequency=2, ) rota_collections = { "current": deepcopy(Rota), # "extended proc nights only": deepcopy(Rota), # "no weekends": deepcopy(Rota), # "no twighlights": deepcopy(Rota), # "no nights": deepcopy(Rota), # "nights + proc twighlights3 + normal weekends": deepcopy(Rota), # "nights + proc twighlights4 + normal weekends": deepcopy(Rota), # "nights + proc twighlights + proc weekends": deepcopy(Rota), } rota_collections["current"].add_shifts( 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( sites=("plymouth",), name="weekend_plymouth", length= 8, days=days[5:], workers_required=2, assign_as_block=True, ), SingleShift( sites=(sites), name="night_weekday", length= 12.25, days=days[:4], balance_offset=5, balance_weighting=1, workers_required=3, force_as_block=False, rota_on_nwds=True, constraints=["night"], ), SingleShift( sites=(sites), name="night_weekend", length= 12.25, days=days[4:], balance_offset=4, balance_weighting=1, workers_required=3, force_as_block=False, rota_on_nwds=True, constraints=["night"], ), ) # rota_collections['extended proc nights only'].add_shifts( # #SingleShift((sites), "night", 12.5, days, balance_weighting=1, workers_required=3, rota_on_nwds=True), # SingleShift((sites), # "night_weekday", # 13, # days[:4], # balance_offset=5, # balance_weighting=1, # workers_required=3, # force_as_block=False, # rota_on_nwds=True, # constraints=["night"]), # SingleShift((sites), # "night_weekend", # 13, # days[4:], # balance_offset=4, # balance_weighting=1, # workers_required=3, # force_as_block=False, # rota_on_nwds=True, # constraints=["night"]), # ) # rota_collections['no weekends'].add_shifts( # 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((sites), "night", 12.5, days, balance_weighting=1, workers_required=3, rota_on_nwds=True), # SingleShift((sites), # "night_weekday", # 13, # days[:4], # balance_offset=5, # balance_weighting=1, # workers_required=3, # force_as_block=False, # rota_on_nwds=True, # constraints=["night"]), # SingleShift((sites), # "night_weekend", # 13, # days[4:], # balance_offset=4, # balance_weighting=1, # workers_required=3, # force_as_block=False, # rota_on_nwds=True, # constraints=["night"]), # ) # rota_collections['no twighlights'].add_shifts( # 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", 12.5, days, balance_weighting=1, workers_required=3, rota_on_nwds=True), # SingleShift((sites), # "night_weekday", # 13, # days[:4], # balance_offset=5, # balance_weighting=1, # workers_required=3, # force_as_block=False, # rota_on_nwds=True, # constraints=["night"]), # SingleShift((sites), # "night_weekend", # 13, # days[4:], # balance_offset=4, # balance_weighting=1, # workers_required=3, # force_as_block=False, # rota_on_nwds=True, # constraints=["night"]), # ) # rota_collections['no nights'].add_shifts( # 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", 12.5, days, balance_weighting=1, workers_required=3, rota_on_nwds=True), # ) # rota_collections['nights + proc twighlights4 + normal weekends'].add_shifts( # SingleShift((sites), "proc_twilight", 12.5, days[:5], workers_required=4), # 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", 12.5, days, balance_weighting=1, workers_required=3, rota_on_nwds=True), # SingleShift((sites), # "night_weekday", # 12.25, # days[:4], # balance_offset=5, # 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=4, # balance_weighting=1, # workers_required=3, # force_as_block=False, # rota_on_nwds=True, # constraints=["night"]), # ) # rota_collections['nights + proc twighlights3 + normal weekends'].add_shifts( # SingleShift((sites), "proc_twilight", 12.5, days[:5], workers_required=3), # 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", 12.5, days, balance_weighting=1, workers_required=3, rota_on_nwds=True), # SingleShift((sites), # "night_weekday", # 12.25, # days[:4], # balance_offset=5, # 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=4, # balance_weighting=1, # workers_required=3, # force_as_block=False, # rota_on_nwds=True, # constraints=["night"]), # ) # rota_collections['nights + proc twighlights + proc weekends'].add_shifts( # SingleShift((sites), "proc_twilight", 12.5, days[:5], workers_required=3), # SingleShift((sites), "proc_weekends", 12.5, days[5:], workers_required=3), # #SingleShift((sites), "night", 12.5, days, balance_weighting=1, workers_required=3, rota_on_nwds=True), # SingleShift((sites), # "night_weekday", # 12.25, # days[:4], # balance_offset=5, # 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=4, # balance_weighting=1, # workers_required=3, # force_as_block=False, # rota_on_nwds=True, # constraints=["night"]), # ) load_leave = True if load_leave: import leave leave = leave.load_leave() # Import trainee data 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 fte == "0": continue nwds = nwd.split("/") if nwd else None end_date = end_date if end_date else None oop = oop.split("-") if oop else None if load_leave: if name not in leave: raise ValueError("{} not found it leave requests".format(name)) leave_requests = [i[0] for i in leave[name] if i[0] != ""] # print(nwds, end_date, oop) for rot in rota_collections: Rota = rota_collections[rot] Rota.add_worker( Worker( Rota, n, name, site.lower(), int(grade[2]), int(fte), nwds, end_date, oop, not_available_to_work=leave_requests, ) ) with open("rotas.html", "w") as f: f.write( """
""" ) for rot in rota_collections: print("Running rota: ", rot) Rota = rota_collections[rot] Rota.build_workers() print("-Building model") Rota.build_model() print("-Building model: complete") opt = SolverFactory("cbc") # choose a solver # opt = SolverFactory('ipopt') # choose a solver if use_neos: print("-Solving with neos") solver_manager = SolverManagerFactory("neos") # Solve in neos server results = solver_manager.solve( Rota.model, opt=opt, logfile="{}.log".format(rot) ) else: print("-Solving with local cbc") results = opt.solve( Rota.model, tee=True, options={ "seconds": 10200, "allow": 2000, }, logfile="{}.log".format(rot), ) # solve the model with the, options="seconds=60" selected solver results.solver.status 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) worker_shift_summary = ResultsHolder.get_shift_summary() ResultsHolder.export_rota_to_csv("rotas/" + rot) with open("rotas.html", "a") as f: f.write("{}".format(worker_shift_summary)
)
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()
# multi = ResultsHolder.get_multinight()
# break
with open("rotas.html", "a") as f:
f.write("")