.
This commit is contained in:
+50
-10
@@ -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
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user