This commit is contained in:
Ross
2023-01-23 14:37:09 +00:00
parent aaa10d5fb8
commit 0179ed410a
8 changed files with 961 additions and 32 deletions
+50 -10
View File
@@ -3,6 +3,7 @@ import datetime
from distutils.log import debug
import itertools
from typing import Dict, Iterable, List, Sequence, Tuple, Set, Any, Type
import time
from pydantic import BaseModel, Extra, constr
@@ -190,12 +191,6 @@ class RotaBuilder(object):
self.use_shift_balance_extra = use_shift_balance_extra
self.use_bank_holiday_extra = use_bank_holiday_extra
self.unavailable_to_work = set()
self.unavailable_to_work_reason = {}
self.pref_not_to_work = {}
self.pref_not_to_work_reason = {}
self.work_requests: set[Tuple[str, WeekInt, DayStr, ShiftName]] = set()
self.work_requests_map = {}
self.workers: list[Worker] = []
@@ -243,7 +238,9 @@ class RotaBuilder(object):
self.set_rota_dates(start_date, weeks_to_rota)
def set_rota_dates(self, start_date, weeks_to_rota):
def set_rota_dates(self, start_date: datetime.datetime, weeks_to_rota: int):
self.weeks_to_rota = weeks_to_rota
self.days = days
@@ -273,6 +270,12 @@ class RotaBuilder(object):
self.week_day_date_map[(week, day)] = d
n = n + 1
self.unavailable_to_work = set()
self.unavailable_to_work_reason = {}
self.pref_not_to_work = {}
self.pref_not_to_work_reason = {}
self.work_requests: set[Tuple[str, WeekInt, DayStr, ShiftName]] = set()
self.work_requests_map = {}
def solve_model(
self,
@@ -282,7 +285,15 @@ class RotaBuilder(object):
debug_if_fail: bool = False,
):
console.print("Setting up solver")
self.opt = SolverFactory(solver)
solver = "scip"
if solver == "scip":
self.opt = SolverFactory(solver, executable="scip")
else:
self.opt = SolverFactory(solver)
console.print("Solving")
if use_neos:
@@ -313,6 +324,35 @@ class RotaBuilder(object):
if not results.solver.status:
sys.exit(0)
def solve_shifts_by_block(self, options, block_length: int, folder="block_shifts"):
shifts = self.shifts
outcomes = {}
no_blocks = self.weeks_to_rota - block_length
original_start_date = self.start_date
for block in range(0, no_blocks):
start_time = time.time()
start_date = original_start_date + datetime.timedelta(weeks=block)
self.set_rota_dates(start_date = start_date,weeks_to_rota=block_length)
console.print(f"Testing block: start_date - {self.start_date} , length {self.weeks_to_rota} weeks")
self.clear_shifts()
self.add_shifts(*shifts)
self.build_and_solve(options)
self.export_rota_to_html(filename=f"{start_date}_{self.weeks_to_rota}", folder=folder)
end_time = time.time()
time_taken = end_time - start_time
outcomes[f"{start_date}_{self.weeks}"] = (
self.results.solver.status,
self.results.solver.termination_condition,
time_taken
)
console.print(outcomes)
sys.exit(0)
def solve_shifts_individually(self, options, folder="individual_shifts"):
shifts = self.shifts
@@ -2424,7 +2464,7 @@ class RotaBuilder(object):
"""
self.shifts.append(shift)
def add_shifts(self, *shifts: SingleShift) -> None:
def add_shifts(self, *shifts: SingleShift ) -> None:
"""Add multiple shifts
Returns:
@@ -2659,7 +2699,7 @@ class RotaBuilder(object):
""" """ """
return self.shift_names
def get_week_block_iterator(self, block_length):
def get_week_block_iterator(self, block_length: int):
"""Gets a two dimensional list of week blocks in specified length
e.g. block_length = 4
+5 -1
View File
@@ -128,7 +128,10 @@ class Worker(BaseModel):
self.calculated_start_date = Rota.start_date
else:
# ? test if start date is valid
self.calculated_start_date = self.start_date
if self.start_date < Rota.start_date:
self.calculated_start_date = self.start_date
else:
self.calculated_start_date = self.start_date
if self.end_date is None:
self.calculated_end_date = Rota.rota_end_date
@@ -271,6 +274,7 @@ class Worker(BaseModel):
self.fte_adj = 0
if days_to_work > 0 and (days_to_work - len(unavailable_set)) / days_to_work < 0.3:
console.print(f"{ self.name }: {days_to_work}, {Rota.rota_days_length}")
console.print(f"Warning {self.name} has excessive unavailability [{len(unavailable_set)}/{days_to_work}, adjusted fte={self.fte_adj}] (this may be infeasible)", style="red")
def __lt__(self, other) -> bool: