From 0cebbfddf6ba5bfe8f71b5d47397946969ee81f8 Mon Sep 17 00:00:00 2001 From: Ross Date: Thu, 26 May 2022 15:56:28 +0100 Subject: [PATCH] pre repalce --- rota/shifts.py | 82 ++++++++++++++++------------------ test/test_general_balancing.py | 10 ++--- test/test_remote.py | 10 ++--- test/test_rota.py | 54 +++++++++++----------- 4 files changed, 75 insertions(+), 81 deletions(-) diff --git a/rota/shifts.py b/rota/shifts.py index a523c5b..84f4b7e 100644 --- a/rota/shifts.py +++ b/rota/shifts.py @@ -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"]: diff --git a/test/test_general_balancing.py b/test/test_general_balancing.py index 2058d2f..836a61f 100644 --- a/test/test_general_balancing.py +++ b/test/test_general_balancing.py @@ -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"), diff --git a/test/test_remote.py b/test/test_remote.py index 83c4a5a..17b75b1 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -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"] ), ) diff --git a/test/test_rota.py b/test/test_rota.py index d10974b..952d40a 100644 --- a/test/test_rota.py +++ b/test/test_rota.py @@ -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"] # ), # ) #