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