remove some old code

This commit is contained in:
Ross
2022-02-21 16:56:41 +00:00
parent b1ce9cc447
commit 7807e3b1e8
+194 -230
View File
@@ -47,8 +47,8 @@ SHIFT_BOUNDS = {
class SingleShift(object):
"""Class to hold all details for a shift
Valid constraints
balance_across_groups (requires block assignment)
@@ -56,14 +56,14 @@ class SingleShift(object):
postclear(2) / preclear(2)
night
limit_grade_number:
options: {grade: max_number, grade2: max_number2}
minimum_grade_number:
options: (grade, min_number)
"""
def __init__(
@@ -199,8 +199,8 @@ class RotaBuilder(object):
"balance_nights_across_sites": True,
"balance_bank_holidays": True,
"balance_blocks": True,
"balance_shifts": True, # Does not use a quadratic function
"balance_shifts_quadratic": False,
"balance_shifts": True, # Does not use a quadratic function
"balance_shifts_quadratic": False,
"balance_shifts_over_workers": True,
"minimise_shift_diffs": False, # less sophisticated version of balance_shifts_over_workers
"balance_weekends": True,
@@ -266,7 +266,6 @@ class RotaBuilder(object):
self.solve_model(options=options)
def build_model(self):
# Initialize model
self.model = ConcreteModel()
@@ -283,15 +282,6 @@ class RotaBuilder(object):
initialize=0,
)
# The nights self.model is used to ensure nights are assigned as a block (and limit the number of workers required)
# self.model.nights = Var(
# ((worker.id, week, block) for worker in self.workers
# for week in self.weeks for block in self.night_blocks),
# within=Binary,
# initialize=0,
# )
# The nights self.model is used to ensure nights are assigned as a block (and limit the number of workers required)
self.model.shift_week_worker_assigned = Var(
(
(shift, week, worker.id)
@@ -347,13 +337,6 @@ class RotaBuilder(object):
within=NonNegativeIntegers,
initialize=0,
)
# self.model.bank_holiday_count = Var(
# ((worker.id, week, day) for worker in self.workers
# for week, day in self.get_week_day_combinations() if self.week_day_date_map[(week, day)] in bank_holiday_map
# ),
# within=Binary,
# initialize=0,
# )
# Used to limit number of workers on night shift per site
# if we force a binary it will in effect hard constrain to < 2
@@ -466,7 +449,10 @@ class RotaBuilder(object):
initialize=0,
)
if self.constraint_options["balance_shifts"] or self.constraint_options["balance_shifts_quadratic"]:
if (
self.constraint_options["balance_shifts"]
or self.constraint_options["balance_shifts_quadratic"]
):
self.model.shift_count_t1 = Var(
(
(worker.id, shift.name)
@@ -558,7 +544,6 @@ class RotaBuilder(object):
# initialize=0,
# )
self.model.works_weekend = Var(
((worker.id, week) for worker in self.workers for week in self.weeks),
domain=Binary,
@@ -696,16 +681,6 @@ class RotaBuilder(object):
# self.model.works[worker.id, week, day, shift]
# for worker in self.workers if worker.site not in site_required))
# THIS SHOULD NO LONGER BE REQUIRED
# Ensure no workers are assigned to shifts that are not required
#for week, day, shift in self.get_not_required_shifts():
# self.model.constraints.add(
# 0
# == sum(
# self.model.works[worker.id, week, day, shift.name]
# for worker in self.workers
# )
# )
# Constraint: total hours worked hours worked
# for worker in self.workers:
@@ -757,23 +732,29 @@ class RotaBuilder(object):
for shift in self.get_shifts_with_constraint("limit_grade_number"):
if not shift.constraint_options["limit_grade_number"]:
raise ValueError("Constraint option must be defined for 'limit_grade_number'")
raise ValueError(
"Constraint option must be defined for 'limit_grade_number'"
)
for grade in shift.constraint_options["limit_grade_number"]:
#self.model.limit_grades_constraint = Constraint(
setattr(self.model, f"limit_grades_constraint_{shift.name}_{grade}", Constraint(
[week for week in self.weeks],
#[shift for shift in self.get_shifts_with_constraint("limit_grade_number")],
[shift],
[grade],
[shift.constraint_options["limit_grade_number"][grade]],
rule=limitGradesRule,
#name=f"limit_grades_constraint_{shift.name}_{grade}"
))
# self.model.limit_grades_constraint = Constraint(
setattr(
self.model,
f"limit_grades_constraint_{shift.name}_{grade}",
Constraint(
[week for week in self.weeks],
# [shift for shift in self.get_shifts_with_constraint("limit_grade_number")],
[shift],
[grade],
[shift.constraint_options["limit_grade_number"][grade]],
rule=limitGradesRule,
# name=f"limit_grades_constraint_{shift.name}_{grade}"
),
)
def minimumGradesRule(model, week, shift, grade, limit):
workers = [w for w in self.workers if w.grade >= grade]
if len(workers) < limit:
#return Constraint.Skip
# return Constraint.Skip
raise ValueError("minimum_grade_number: not enough valid workers")
return (
sum(
@@ -785,19 +766,24 @@ class RotaBuilder(object):
for shift in self.get_shifts_with_constraint("minimum_grade_number"):
if not shift.constraint_options["minimum_grade_number"]:
raise ValueError("Constraint option must be defined for 'minimum_grade_number'")
raise ValueError(
"Constraint option must be defined for 'minimum_grade_number'"
)
grade, min_required = shift.constraint_options["minimum_grade_number"]
#self.model.limit_grades_constraint = Constraint(
setattr(self.model, f"minimum_grade_number_{shift.name}", Constraint(
[week for week in self.weeks],
#[shift for shift in self.get_shifts_with_constraint("limit_grade_number")],
[shift],
[grade],
[min_required],
rule=minimumGradesRule,
#name=f"limit_grades_constraint_{shift.name}_{grade}"
))
# self.model.limit_grades_constraint = Constraint(
setattr(
self.model,
f"minimum_grade_number_{shift.name}",
Constraint(
[week for week in self.weeks],
# [shift for shift in self.get_shifts_with_constraint("limit_grade_number")],
[shift],
[grade],
[min_required],
rule=minimumGradesRule,
# name=f"limit_grades_constraint_{shift.name}_{grade}"
),
)
# def nightShiftDerrifordRule(model, week, shift):
# derriford_workers = [w for w in self.workers if w.night_at_derriford >= 1]
@@ -831,21 +817,26 @@ class RotaBuilder(object):
>= required_number
)
for shift in self.get_shifts_with_constraint("require_home_site_presence"):
site, required_number = shift.constraint_options["require_home_site_presence"]
#self.model.require_presence_at_site_overnight_rule = Constraint(
setattr(self.model, f"require_home_site_presence_{shift.name}", Constraint(
[week for week in self.weeks],
[shift.name],
[site],
[required_number],
rule=presenceAtHomeSite,
))
site, required_number = shift.constraint_options[
"require_home_site_presence"
]
# self.model.require_presence_at_site_overnight_rule = Constraint(
setattr(
self.model,
f"require_home_site_presence_{shift.name}",
Constraint(
[week for week in self.weeks],
[shift.name],
[site],
[required_number],
rule=presenceAtHomeSite,
),
)
# # Count the number of workers from each site on each night shift
# # As 1 or 0 is optimum we can simply subtract 1 from the number
#if self.constraint_options["balance_nights_across_sites"]:
# if self.constraint_options["balance_nights_across_sites"]:
# for site in self.sites:
# for shift in self.get_shifts_with_constraint("night"):
# block = shift.name
@@ -1032,12 +1023,10 @@ class RotaBuilder(object):
print(e)
# Count number of weekends an worker works
#if self.constraint_options["balance_weekends"]:
# if self.constraint_options["balance_weekends"]:
self.model.constraints.add(
self.model.worker_weekend_count[worker.id]
== sum(
self.model.works_weekend[worker.id, week] for week in self.weeks
)
== sum(self.model.works_weekend[worker.id, week] for week in self.weeks)
)
if self.constraint_options["max_weekends"] > -1:
@@ -1102,9 +1091,11 @@ class RotaBuilder(object):
min_shifts,
sum(
self.model.works[worker.id, week, day, shift.name]
for week, day in self.get_week_day_combinations_for_shift(shift)
#for week, day in self.get_week_day_combinations()
#if shift.name in self.get_shift_names_by_week_day(week, day)
for week, day in self.get_week_day_combinations_for_shift(
shift
)
# for week, day in self.get_week_day_combinations()
# if shift.name in self.get_shift_names_by_week_day(week, day)
),
max_shifts,
)
@@ -1115,8 +1106,10 @@ class RotaBuilder(object):
0
== sum(
self.model.works[worker.id, week, day, shift.name]
for week, day in self.get_week_day_combinations_for_shift(shift)
#for week, day in self.get_week_day_combinations()
for week, day in self.get_week_day_combinations_for_shift(
shift
)
# for week, day in self.get_week_day_combinations()
)
)
@@ -1143,7 +1136,6 @@ class RotaBuilder(object):
== self.model.shift_count_diff[worker.id, shift.name]
)
if self.constraint_options["balance_shifts_quadratic"]:
# This may need to be updated
xU = 25
@@ -1182,7 +1174,6 @@ class RotaBuilder(object):
- xU * xU
)
# Define worker_shift_count_t1 and worker_shift_count_t2 constraints for the object
# Thus bypassing the need for a quadratic solver
# t1-t2 is the target
@@ -1241,9 +1232,11 @@ class RotaBuilder(object):
self.model.night_shift_count[worker.id]
== sum(
self.model.works[worker.id, week, day, shift.name]
for week, day, shift in self.get_week_day_shift_combinations_for_constraint("night")
#for week, day in self.get_week_day_combinations_for_shift(shift)
#for shift in self.get_shifts_with_constraint("night")
for week, day, shift in self.get_week_day_shift_combinations_for_constraint(
"night"
)
# for week, day in self.get_week_day_combinations_for_shift(shift)
# for shift in self.get_shifts_with_constraint("night")
)
)
@@ -1387,10 +1380,8 @@ class RotaBuilder(object):
)
)
#if self.constraint_options["balance_weekends"]:
for week_blocks in self.get_week_block_iterator(
self.max_weekend_frequency
):
# if self.constraint_options["balance_weekends"]:
for week_blocks in self.get_week_block_iterator(self.max_weekend_frequency):
# Prevent weekend shifts more than once every n weeks
self.model.constraints.add(
1
@@ -1406,14 +1397,16 @@ class RotaBuilder(object):
self.constraint_options["max_shifts_per_week"]
>= sum(
self.model.works[worker.id, week, day, shiftname]
#for shiftname in self.get_shift_names_by_week_day(week, day)
#for day in self.days
for w, day, shiftname in self.get_all_shiftname_combinations(week=week)
# for shiftname in self.get_shift_names_by_week_day(week, day)
# for day in self.days
for w, day, shiftname in self.get_all_shiftname_combinations(
week=week
)
)
)
# TODO: generic implementation
#for shift in self.get_shifts_with_constraint("max_2_shifts_per_week"):
# for shift in self.get_shifts_with_constraint("max_2_shifts_per_week"):
# self.model.constraints.add(
# 2
# >= sum(
@@ -1431,16 +1424,22 @@ class RotaBuilder(object):
self.model.constraints.add(
full_weekend_count
>= sum(
self.model.works[worker.id, week, "Sat", shift.name]
for shift in self.get_shifts()
self.model.works[worker.id, week, "Sat", shift]
for shift in self.get_shift_names_by_week_day(
week, "Sat"
)
)
+ sum(
self.model.works[worker.id, week, "Sun", shift.name]
for shift in self.get_shifts()
self.model.works[worker.id, week, "Sun", shift]
for shift in self.get_shift_names_by_week_day(
week, "Sun"
)
)
+ sum(
self.model.works[worker.id, week + 1, "Mon", shift.name]
for shift in self.get_shifts()
self.model.works[worker.id, week + 1, "Mon", shift]
for shift in self.get_shift_names_by_week_day(
week+1, "Mon"
)
)
)
if (
@@ -1454,20 +1453,31 @@ class RotaBuilder(object):
self.model.constraints.add(
full_weekend_count
>= sum(
self.model.works[worker.id, week, "Sat", shift.name]
for shift in self.get_shifts()
self.model.works[worker.id, week, "Sat", shift]
for shift in self.get_shift_names_by_week_day(
week, "Sat"
)
)
+ sum(
self.model.works[worker.id, week, "Sun", shift.name]
for shift in self.get_shifts()
self.model.works[worker.id, week, "Sun", shift]
for shift in self.get_shift_names_by_week_day(
week, "Sun"
)
# for shift in self.get_shifts()
)
+ sum(
self.model.works[worker.id, week + 1, "Mon", shift.name]
for shift in self.get_shifts()
self.model.works[worker.id, week + 1, "Mon", shift]
for shift in self.get_shift_names_by_week_day(
week + 1, "Mon"
)
# for shift in self.get_shifts()
)
+ sum(
self.model.works[worker.id, week + 1, "Tue", shift.name]
for shift in self.get_shifts()
self.model.works[worker.id, week + 1, "Tue", shift]
for shift in self.get_shift_names_by_week_day(
week + 1, "Tue"
)
# for shift in self.get_shifts()
)
)
if (
@@ -1528,12 +1538,15 @@ class RotaBuilder(object):
# self.model.works[worker.id, week, day, shift.name]
# for day in self.days[5:]
# for shift in self.get_shifts()) / 2)
#if self.constraint_options["balance_weekends"]:
# if self.constraint_options["balance_weekends"]:
self.model.constraints.add(
self.model.works_weekend[worker.id, week]
>= sum(
self.model.works[worker.id, week, day, shiftname]
for w, day, shiftname in self.get_all_shiftname_combinations(week=week) if day in self.days[5:]
for w, day, shiftname in self.get_all_shiftname_combinations(
week=week
)
if day in self.days[5:]
)
/ 2
)
@@ -1541,7 +1554,10 @@ class RotaBuilder(object):
self.model.works_weekend[worker.id, week]
<= sum(
self.model.works[worker.id, week, day, shiftname]
for w, day, shiftname in self.get_all_shiftname_combinations(week=week) if day in self.days[5:]
for w, day, shiftname in self.get_all_shiftname_combinations(
week=week
)
if day in self.days[5:]
)
)
# self.model.constraints.add(
@@ -1557,7 +1573,7 @@ class RotaBuilder(object):
# self.model.shift_week_worker_assigned[s.name, week, worker.id]
# for s in self.get_shifts_with_constraint("night")))
#for shift in self.get_shifts_with_constraint("night"):
# for shift in self.get_shifts_with_constraint("night"):
for shift in self.get_shifts():
if shift.force_as_block:
# Force nights to be assigned in blocks
@@ -1663,10 +1679,14 @@ class RotaBuilder(object):
if day in constraint_shift.shift_days:
self.model.constraints.add(
1
>= self.model.works[worker.id, week, day, constraint_shift.name]
>= self.model.works[
worker.id, week, day, constraint_shift.name
]
+ sum(
p1 * self.model.works[w.id, pweek, pday, shiftname]
for shiftname in self.get_shift_names_by_week_day(pweek, pday)
for shiftname in self.get_shift_names_by_week_day(
pweek, pday
)
if shiftname != constraint_shift.name
for w in workers
)
@@ -1676,10 +1696,14 @@ class RotaBuilder(object):
if day in constraint_shift.shift_days:
self.model.constraints.add(
1
>= self.model.works[worker.id, week, day, constraint_shift.name]
>= self.model.works[
worker.id, week, day, constraint_shift.name
]
+ sum(
p2 * self.model.works[w.id, p2week, p2day, shiftname]
for shiftname in self.get_shift_names_by_week_day(p2week, p2day)
for shiftname in self.get_shift_names_by_week_day(
p2week, p2day
)
if shiftname != constraint_shift.name
for w in workers
)
@@ -1691,10 +1715,14 @@ class RotaBuilder(object):
if day in constraint_shift.shift_days:
self.model.constraints.add(
1
>= self.model.works[worker.id, week, day, constraint_shift.name]
>= self.model.works[
worker.id, week, day, constraint_shift.name
]
+ sum(
n1 * self.model.works[w.id, nweek, nday, shiftname]
for shiftname in self.get_shift_names_by_week_day(nweek, nday)
for shiftname in self.get_shift_names_by_week_day(
nweek, nday
)
if shiftname != constraint_shift.name
for w in workers
)
@@ -1704,18 +1732,28 @@ class RotaBuilder(object):
if day in constraint_shift.shift_days:
self.model.constraints.add(
1
>= self.model.works[worker.id, week, day, constraint_shift.name]
>= self.model.works[
worker.id, week, day, constraint_shift.name
]
+ sum(
n2 * self.model.works[w.id, n2week, n2day, shiftname]
for shiftname in self.get_shift_names_by_week_day(n2week, n2day)
for shiftname in self.get_shift_names_by_week_day(
n2week, n2day
)
if shiftname != constraint_shift.name
for w in workers
)
)
# Night constraint means we won't assign a shift the day before
# an unavailability
for constraint_shift in self.get_shifts_with_constraint("night"):
#if day in constraint_shift.shift_days:
if (worker.id, pweek, pday, constraint_shift.name) in self.model.works:
if (
worker.id,
pweek,
pday,
constraint_shift.name,
) in self.model.works:
# Ensure night prior to unavalibity is not assigned
self.model.constraints.add(
self.model.available[worker.id, week, day]
@@ -1723,86 +1761,6 @@ class RotaBuilder(object):
worker.id, pweek, pday, constraint_shift.name
]
)
# # NOTE: use pre / post clear instead
# # DEPRECATED
# # if working a night ensure preceeding (1) or subsequent (2) shifts can only be nights
# if self.constraint_options["constrain_time_off_after_nights"]:
# for constraint_shift in self.get_shifts_with_constraint("night"):
# if day in constraint_shift.shift_days:
# # Ensure night prior to unavalibity is not assigned
# self.model.constraints.add(
# self.model.available[worker.id, week, day]
# >= self.model.works[
# worker.id, pweek, pday, constraint_shift.name
# ]
# )
#
# # print("Workers", workers)
#
# self.model.constraints.add(
# 1
# >= self.model.works[
# worker.id, week, day, constraint_shift.name
# ]
# + sum(
# n1 * self.model.works[w.id, nweek, nday, shift]
# for shift in self.get_shift_names_by_week_day(
# nweek, nday
# )
# if shift != constraint_shift.name
# for w in workers
# )
# )
#
# self.model.constraints.add(
# 1
# >= self.model.works[
# worker.id, week, day, constraint_shift.name
# ]
# + sum(
# n2 * self.model.works[w.id, n2week, n2day, shift]
# for shift in self.get_shift_names_by_week_day(
# n2week, n2day
# )
# if shift != constraint_shift.name
# for w in workers
# )
# )
# # self.model.constraints.add(
# # 1 >= self.model.works[worker.id, week, day,
# # constraint_shift.name] +
# # sum(n3 * self.model.works[worker.id, n3week, n3day,
# # shift]
# # for shift in self.get_shift_names_by_week_day(week, n3day)
# # if shift != constraint_shift.name))
# self.model.constraints.add(
# 1
# >= self.model.works[
# worker.id, week, day, constraint_shift.name
# ]
# + sum(
# p1 * self.model.works[w.id, pweek, pday, shift]
# for shift in self.get_shift_names_by_week_day(
# pweek, pday
# )
# if shift != constraint_shift.name
# for w in workers
# )
# )
# self.model.constraints.add(
# 1
# >= self.model.works[
# worker.id, week, day, constraint_shift.name
# ]
# + sum(
# p2 * self.model.works[w.id, p2week, p2day, shift]
# for shift in self.get_shift_names_by_week_day(
# p2week, p2day
# )
# if shift != constraint_shift.name
# for w in workers
# )
# )
self.define_objectives()
@@ -1932,7 +1890,6 @@ class RotaBuilder(object):
else:
nights_site_balancing = 0
if self.get_shifts_with_constraint("balance_across_groups"):
block_site_balancing = sum(
(
@@ -1942,7 +1899,9 @@ class RotaBuilder(object):
* 2000
# self.model.night_per_site2[week, block, site]
for week in self.weeks
for shift in self.get_shifts_with_constraint("balance_across_groups")
for shift in self.get_shifts_with_constraint(
"balance_across_groups"
)
for site in self.sites
)
else:
@@ -2112,32 +2071,34 @@ class RotaBuilder(object):
for site in s.site:
self.sites.add(site)
# # Todo replace with week_day.....
# self.day_shift_product = []
# self.day_shiftclass_product = []
# for s in self.shifts:
# for day in days:
# if day in s.shift_days:
# self.day_shift_product.append((day, s.name))
# self.day_shiftclass_product.append((day, s))
#
# def get_day_shiftname_combinations(self):
# """Returns a list of all required day / shift combinations
#
# Returns:
# list: list of in the following format (str->day, str->shift)
# """
# return self.day_shift_product
#
# def get_day_shiftclass_combinations(self) -> List[Tuple[DayStr, SingleShift]]:
# """Returns a list of all required day / shift combinations
#
# Returns:
# list: list of in the following format (str->day, shiftObject->shift)
# """
# return self.day_shiftclass_product
# # Todo replace with week_day.....
# self.day_shift_product = []
# self.day_shiftclass_product = []
# for s in self.shifts:
# for day in days:
# if day in s.shift_days:
# self.day_shift_product.append((day, s.name))
# self.day_shiftclass_product.append((day, s))
#
# def get_day_shiftname_combinations(self):
# """Returns a list of all required day / shift combinations
#
# Returns:
# list: list of in the following format (str->day, str->shift)
# """
# return self.day_shift_product
#
# def get_day_shiftclass_combinations(self) -> List[Tuple[DayStr, SingleShift]]:
# """Returns a list of all required day / shift combinations
#
# Returns:
# list: list of in the following format (str->day, shiftObject->shift)
# """
# return self.day_shiftclass_product
def get_all_shiftname_combinations(self, week: int =None, day: int =None) -> List[Tuple[WeekInt, DayStr, ShiftName]]:
def get_all_shiftname_combinations(
self, week: int = None, day: int = None
) -> List[Tuple[WeekInt, DayStr, ShiftName]]:
"""Returns a list of all possible week / day / shift combinations
Returns:
@@ -2154,7 +2115,7 @@ class RotaBuilder(object):
return week_day_shifts
def get_all_shiftclass_combinations(
self, week: int =None, day: int =None
self, week: int = None, day: int = None
) -> List[Tuple[WeekInt, DayStr, SingleShift]]:
"""Returns a list of all possible week / day / shift combinations
@@ -2181,7 +2142,11 @@ class RotaBuilder(object):
return self.weeks_days_product
def get_week_day_combinations_for_shift(self, shift) -> list:
return [(week, day) for week, day in self.get_week_day_combinations() if shift.name in self.get_shift_names_by_week_day(week, day)]
return [
(week, day)
for week, day in self.get_week_day_combinations()
if shift.name in self.get_shift_names_by_week_day(week, day)
]
def get_week_day_shift_combinations_for_constraint(self, constraint) -> list:
constraint_shifts = self.get_shifts_with_constraint(constraint)
@@ -2605,7 +2570,6 @@ class RotaBuilder(object):
bank_holiday_count = -1
bank_holiday_count_w = -1
# print(worker.name, bank_holiday_count, bank_holiday_count_w)
timetable.append(
f"<tr class='worker-row'>{worker_td}{''.join(shift_tds)}</tr>"