pre repalce

This commit is contained in:
Ross
2022-05-26 15:56:28 +01:00
parent c9ba2a098c
commit 0cebbfddf6
4 changed files with 75 additions and 81 deletions
+38 -44
View File
@@ -3,6 +3,8 @@ import datetime
import itertools
from typing import Dict, Iterable, List, Sequence, Tuple, Set
from pydantic import BaseModel, Extra
from pathlib import Path
import datetime
@@ -45,7 +47,7 @@ SHIFT_BOUNDS = {
}
class SingleShift(object):
class SingleShift(BaseModel):
"""Class to hold all details for a shift
@@ -68,56 +70,49 @@ class SingleShift(object):
"""
sites: Iterable[str]
name: str
length: float
shift_days: Sequence[str]
balance_offset: float | None = None,
balance_weighting: float = 1,
workers_required: float = 1,
rota_on_nwds: bool = False,
assign_as_block: bool = False,
force_as_block: bool = False,
force_as_block_unless_nwd: bool = False,
hard_constrain_shift: bool = True,
bank_holidays_only: bool = False,
constraint: Iterable[str] = [],
def __init__(
self,
sites: Iterable[str],
name: str,
length: float,
shift_days: Sequence[str],
# balance_by_site=True,
balance_offset: float | None = None,
balance_weighting: float = 1,
workers_required: float = 1,
rota_on_nwds: bool = False,
assign_as_block: bool = False,
force_as_block: bool = False,
force_as_block_unless_nwd: bool = False,
hard_constrain_shift: bool = True,
bank_holidays_only: bool = False,
constraints: Iterable[str] = [],
):
self.site = sites
self.name = name
self.length = length
self.shift_days = shift_days
class Config:
extra = Extra.allow
def __init__(self, **data: Any):
super().__init__(**data)
#self.site = sites
#self.name = name
#self.length = length
#self.shift_days = shift_days
# self.balance_by_site = balance_by_site
# balance_offset defines the max difference in allocated shifts
# versus target shifts (hard constraint)
# if 0 exactly equal shifts must be assigend (unlikely to be possible)
if balance_offset is None:
self.balance_offset = len(shift_days)
else:
self.balance_offset = balance_offset
# weight the shift for balancing, default is 1
self.balance_weighting = balance_weighting
self.hard_constrain_shift = hard_constrain_shift
self.workers_required = workers_required
self.rota_on_nwds = rota_on_nwds
self.force_as_block = force_as_block
self.force_as_block_unless_nwd = force_as_block_unless_nwd
if self.balance_offset is None:
self.balance_offset = len(self.shift_days)
if force_as_block_unless_nwd:
if self.force_as_block_unless_nwd:
self.assign_as_block = True
self.assign_as_block = assign_as_block
#self.assign_as_block = assign_as_block
self.constraint_options = {}
self.constraints = []
for c in constraints:
for c in self.constraint:
match c:
case (constraint, options):
self.constraints.append(constraint)
@@ -125,9 +120,6 @@ class SingleShift(object):
case constraint:
self.constraints.append(constraint)
self.bank_holidays_only = bank_holidays_only
# self.total_shifts = length * len(days) * len(shift_days)
def __str__(self):
return f"name: {self.name}, site: {self.site}, length: {self.length}, balance weighting: {self.balance_weighting}, balance offset: {self.balance_offset}, hard_constrain: {self.hard_constrain_shift}, workers required : {self.workers_required}, rota on nwds {self.rota_on_nwds}, assign as block: {self.assign_as_block}, force as block: {self.force_as_block}, constraints: {self.constraints}, constraint_options: {self.constraint_options}" # , total shifts: {self.total_shifts}"
@@ -154,6 +146,7 @@ class RotaBuilder(object):
use_previous_shifts: bool = False,
use_shift_balance_extra: bool = False,
use_bank_holiday_extra: bool = False,
SHIFT_BOUNDS = SHIFT_BOUNDS
):
print(f"Start time: {datetime.datetime.now()}")
@@ -161,6 +154,7 @@ class RotaBuilder(object):
print(f"Use previous shifts {use_previous_shifts}")
print(f"Use previous shifts (balance_extra) {use_shift_balance_extra}")
self.SHIFT_BOUNDS = SHIFT_BOUNDS
self.shifts = [] # type List[SingleShift]
self.days = days
@@ -341,7 +335,7 @@ class RotaBuilder(object):
((worker.id) for worker in self.workers),
within=NonNegativeIntegers,
initialize=0,
bounds=SHIFT_BOUNDS["bank_holiday"],
bounds=self.SHIFT_BOUNDS["bank_holiday"],
)
self.model.bank_holiday_count_w = Var(
((worker.id) for worker in self.workers),
@@ -428,7 +422,7 @@ class RotaBuilder(object):
),
domain=NonNegativeReals,
initialize=0,
bounds=SHIFT_BOUNDS["shift_count"],
bounds=self.SHIFT_BOUNDS["shift_count"],
)
self.model.shift_count_diff = Var(
@@ -501,7 +495,7 @@ class RotaBuilder(object):
((worker.id) for worker in self.workers),
domain=NonNegativeReals,
initialize=0,
bounds=SHIFT_BOUNDS["night_shift_count"],
bounds=self.SHIFT_BOUNDS["night_shift_count"],
)
self.model.night_shift_count_t1 = Var(
@@ -565,7 +559,7 @@ class RotaBuilder(object):
((worker.id) for worker in self.workers),
domain=NonNegativeIntegers,
initialize=0,
bounds=SHIFT_BOUNDS["weekend_count"],
bounds=self.SHIFT_BOUNDS["weekend_count"],
)
if self.constraint_options["balance_weekends"]:
+5 -5
View File
@@ -75,7 +75,7 @@ class TestBalancing:
balance_weighting=4,
workers_required=1,
force_as_block=False,
constraints=["preclear2", "postclear2"],
constraint=["preclear2", "postclear2"],
),
SingleShift(
("group1", "group2"),
@@ -118,7 +118,7 @@ class TestBalancing:
#balance_weighting=4,
workers_required=1,
force_as_block=False,
#constraints=["preclear2", "postclear2"],
#constraint=["preclear2", "postclear2"],
),
SingleShift(
("group1", "group2"),
@@ -163,7 +163,7 @@ class TestBalancing:
#balance_weighting=4,
workers_required=1,
force_as_block=False,
#constraints=["preclear2", "postclear2"],
#constraint=["preclear2", "postclear2"],
),
SingleShift(
("group1", "group2"),
@@ -214,7 +214,7 @@ class TestBalancing:
balance_weighting=8,
workers_required=2,
force_as_block=False,
#constraints=["preclear2", "postclear2"],
#constraint=["preclear2", "postclear2"],
),
SingleShift(
("group2","group3"),
@@ -255,7 +255,7 @@ class TestBalancing:
#balance_weighting=8,
workers_required=2,
force_as_block=False,
#constraints=["preclear2", "postclear2"],
#constraint=["preclear2", "postclear2"],
),
SingleShift(
("group2","group3"),
+5 -5
View File
@@ -37,7 +37,7 @@ class TestRemoteRotas:
workers_required=1,
balance_offset=100,
force_as_block=False,
constraints=[("require_remote_site_presence_week", ("group2", 2))],
constraint=[("require_remote_site_presence_week", ("group2", 2))],
),
)
@@ -76,7 +76,7 @@ class TestRemoteRotas:
workers_required=1,
balance_offset=10,
force_as_block=True,
constraints=[("require_remote_site_presence_week", ("group2", 1))],
constraint=[("require_remote_site_presence_week", ("group2", 1))],
),
SingleShift(
("group1", "group2"),
@@ -86,7 +86,7 @@ class TestRemoteRotas:
workers_required=1,
balance_offset=10,
force_as_block=False,
#constraints=[("require_remote_site_presence_week", ("group2", 1))],
#constraint=[("require_remote_site_presence_week", ("group2", 1))],
#constraint=["balance_shifts_over_workers"]
),
)
@@ -142,7 +142,7 @@ class TestRemoteRotas:
workers_required=1,
balance_offset=10,
force_as_block=False,
constraints=[("require_remote_site_presence", ("group2", 1))],
constraint=[("require_remote_site_presence", ("group2", 1))],
),
SingleShift(
("group1", "group2"),
@@ -152,7 +152,7 @@ class TestRemoteRotas:
workers_required=1,
balance_offset=10,
force_as_block=False,
#constraints=[("require_remote_site_presence_week", ("group2", 1))],
#constraint=[("require_remote_site_presence_week", ("group2", 1))],
#constraint=["balance_shifts_over_workers"]
),
)
+27 -27
View File
@@ -228,8 +228,8 @@ class TestDemoRotaNights:
12.5,
days[:4],
workers_required=1,
constraints=["preclear2", "postclear2"],
# constraints=["night"],
constraint=["preclear2", "postclear2"],
# constraint=["night"],
force_as_block=True
# We use a different balance weighting for each shift as otherwise
# the shifts can be treated equally (and no target diff)
@@ -241,8 +241,8 @@ class TestDemoRotaNights:
12.5,
days[4:],
# balance_weighting=0.5
constraints=["preclear2", "postclear2"],
# constraints=["night"],
constraint=["preclear2", "postclear2"],
# constraint=["night"],
force_as_block=True,
),
SingleShift(
@@ -252,12 +252,12 @@ class TestDemoRotaNights:
days[:5],
# balance_weighting=0.5
workers_required=2,
# constraints=["preclear2", "postclear2"],
# constraint=["preclear2", "postclear2"],
),
# SingleShift(("group1","group2"), "atwilight", 12.5, days[:5], balance_offset=40,
##balance_weighting=0.5
# workers_required=2,
# #constraints=["postclear2"],
# #constraint=["postclear2"],
# ),
)
@@ -329,7 +329,7 @@ class TestDemoRotaClear:
days[:5],
balance_offset=40,
workers_required=2,
constraints=["preclear"],
constraint=["preclear"],
),
SingleShift(
("group1", "group2"),
@@ -346,7 +346,7 @@ class TestDemoRotaClear:
days[3],
balance_offset=40,
workers_required=1,
constraints=["preclear2", "postclear2"],
constraint=["preclear2", "postclear2"],
),
)
@@ -486,7 +486,7 @@ class TestDemoRotaBalanceShiftSites:
balance_offset=40,
workers_required=2,
force_as_block=True,
constraints=["balance_across_groups"],
constraint=["balance_across_groups"],
),
)
self.Rota.constraint_options["balance_nights_across_sites"] = False
@@ -522,7 +522,7 @@ class TestDemoRotaBalanceShiftSites:
balance_offset=40,
workers_required=3,
force_as_block=True,
constraints=["balance_across_groups"],
constraint=["balance_across_groups"],
),
)
self.Rota.constraint_options["balance_nights_across_sites"] = False
@@ -556,7 +556,7 @@ class TestDemoRotaBalanceShiftSites:
balance_offset=40,
workers_required=3,
force_as_block=True,
constraints=["balance_across_groups"],
constraint=["balance_across_groups"],
),
)
self.Rota.constraint_options["balance_nights_across_sites"] = False
@@ -614,7 +614,7 @@ class TestLimitConstraints:
balance_offset=40,
workers_required=4,
force_as_block=True,
constraints=[("limit_grade_number", {2: 1})],
constraint=[("limit_grade_number", {2: 1})],
),
)
# self.Rota.constraint_options["balance_nights_across_sites"] = False
@@ -650,7 +650,7 @@ class TestLimitConstraints:
balance_offset=40,
workers_required=4,
force_as_block=True,
constraints=[("limit_grade_number", {3: 2})],
constraint=[("limit_grade_number", {3: 2})],
),
)
# self.Rota.constraint_options["balance_nights_across_sites"] = False
@@ -688,7 +688,7 @@ class TestLimitConstraints:
balance_offset=40,
workers_required=4,
force_as_block=True,
constraints=[("limit_grade_number", {2: 3, 3: 1})],
constraint=[("limit_grade_number", {2: 3, 3: 1})],
),
)
# self.Rota.constraint_options["balance_nights_across_sites"] = False
@@ -726,7 +726,7 @@ class TestLimitConstraints:
balance_offset=40,
workers_required=4,
force_as_block=True,
constraints=[("limit_grade_number", {2: 4, 3: 0})],
constraint=[("limit_grade_number", {2: 4, 3: 0})],
),
)
# self.Rota.constraint_options["balance_nights_across_sites"] = False
@@ -747,7 +747,7 @@ class TestLimitConstraints:
balance_offset=40,
workers_required=1,
force_as_block=True,
constraints=[("minimum_grade_number", (3, 1))],
constraint=[("minimum_grade_number", (3, 1))],
),
)
@@ -783,7 +783,7 @@ class TestLimitConstraints:
balance_offset=40,
workers_required=4,
force_as_block=True,
constraints=[("minimum_grade_number", (3, 3))],
constraint=[("minimum_grade_number", (3, 3))],
),
)
@@ -819,7 +819,7 @@ class TestLimitConstraints:
balance_offset=40,
workers_required=4,
force_as_block=True,
constraints=[("minimum_grade_number", (3, 0))],
constraint=[("minimum_grade_number", (3, 0))],
),
)
@@ -841,7 +841,7 @@ class TestLimitConstraints:
balance_offset=40,
workers_required=4,
force_as_block=True,
constraints=[("minimum_grade_number", (4, 1))],
constraint=[("minimum_grade_number", (4, 1))],
),
)
@@ -860,7 +860,7 @@ class TestLimitConstraints:
balance_offset=40,
workers_required=2,
force_as_block=True,
constraints=[("require_remote_site_presence_week", ("group1", 2))],
constraint=[("require_remote_site_presence_week", ("group1", 2))],
),
)
self.Rota.build_and_solve(options={"ratio": 0.00})
@@ -898,7 +898,7 @@ class TestLimitConstraints:
balance_offset=20,
workers_required=3,
force_as_block=True,
constraints=[("require_remote_site_presence_week", ("group2", 2))],
constraint=[("require_remote_site_presence_week", ("group2", 2))],
),
)
self.Rota.build_and_solve(options={"ratio": 0.00})
@@ -930,7 +930,7 @@ class TestLimitConstraints:
balance_offset=20,
workers_required=3,
force_as_block=True,
constraints=[("require_remote_site_presence_week", ("group2", 1))],
constraint=[("require_remote_site_presence_week", ("group2", 1))],
),
)
self.Rota.build_and_solve(options={"ratio": 0.00})
@@ -995,7 +995,7 @@ class TestNightUnavailable:
# Add a weekday and weekend shift
self.Rota.add_shifts(
SingleShift(
("group1",), "night_weekend", 12.5, days[5:], constraints=["night"]
("group1",), "night_weekend", 12.5, days[5:], constraint=["night"]
),
)
@@ -1018,7 +1018,7 @@ class TestNightUnavailable:
# Add a weekday and weekend shift
self.Rota.add_shifts(
SingleShift(
("group1",), "night_weekend", 12.5, days[5:], workers_required=2, constraints=["night"]
("group1",), "night_weekend", 12.5, days[5:], workers_required=2, constraint=["night"]
),
)
@@ -1032,7 +1032,7 @@ class TestNightUnavailable:
self.Rota.add_shifts(
SingleShift(
("group1",), "night_weekend", 12.5, days[5:], workers_required=2, constraints=[]
("group1",), "night_weekend", 12.5, days[5:], workers_required=2, constraint=[]
),
)
@@ -1053,7 +1053,7 @@ class TestNightUnavailable:
self.Rota.add_shifts(
SingleShift(
("group1",), "night_weekend", 12.5, days[5:], workers_required=3, constraints=[]
("group1",), "night_weekend", 12.5, days[5:], workers_required=3, constraint=[]
),
)
@@ -1079,7 +1079,7 @@ class TestNightUnavailable:
#
# self.Rota.add_shifts(
# SingleShift(
# ("group1",), "night_weekend", 12.5, days[5:], workers_required=3, constraints=["night"]
# ("group1",), "night_weekend", 12.5, days[5:], workers_required=3, constraint=["night"]
# ),
# )
#