start working on shift paring balancing
This commit is contained in:
+61
-25
@@ -125,6 +125,7 @@ class SingleShift(BaseModel):
|
||||
constraint: List[ShiftConstraint] = []
|
||||
start_date: datetime.date | None = None
|
||||
end_date: datetime.date | None = None
|
||||
pair_proxy: bool = False
|
||||
|
||||
model_config = ConfigDict(
|
||||
extra = "allow",
|
||||
@@ -280,6 +281,8 @@ class RotaBuilder(object):
|
||||
|
||||
self.paired_shifts = []
|
||||
|
||||
self.exported_rota_file = None
|
||||
|
||||
def set_rota_dates(self, start_date: datetime.date, weeks_to_rota: int):
|
||||
self.weeks_to_rota = weeks_to_rota
|
||||
|
||||
@@ -1118,23 +1121,26 @@ class RotaBuilder(object):
|
||||
print(shift_name)
|
||||
print(week, self.get_week_start_date(week), shift.start_date, shift.end_date)
|
||||
|
||||
if self.get_week_start_date(week) <= shift.start_date:
|
||||
continue
|
||||
#if self.get_week_start_date(week) <= shift.start_date:
|
||||
# continue
|
||||
|
||||
if self.get_week_start_date(week) > shift.end_date:
|
||||
continue
|
||||
#if self.get_week_start_date(week) > shift.end_date:
|
||||
# continue
|
||||
|
||||
for worker in self.get_workers_for_shift(shift):
|
||||
self.model.constraints.add(
|
||||
8
|
||||
* self.model.blocks_worker_shift_assigned[
|
||||
worker.id, week, shift_name
|
||||
]
|
||||
>= sum(
|
||||
self.model.works[worker.id, week, day, shift_name]
|
||||
for day in shift.days
|
||||
try:
|
||||
self.model.constraints.add(
|
||||
8
|
||||
* self.model.blocks_worker_shift_assigned[
|
||||
worker.id, week, shift_name
|
||||
]
|
||||
>= sum(
|
||||
self.model.works[worker.id, week, day, shift_name]
|
||||
for day in shift.days
|
||||
)
|
||||
)
|
||||
)
|
||||
except KeyError:
|
||||
pass
|
||||
self.model.constraints.add(
|
||||
self.model.blocks_assigned[week, shift_name]
|
||||
== sum(
|
||||
@@ -1203,7 +1209,7 @@ class RotaBuilder(object):
|
||||
worker.id, week, shift.name
|
||||
]
|
||||
for worker in self.workers
|
||||
)
|
||||
)
|
||||
<= shift.workers_required
|
||||
)
|
||||
|
||||
@@ -2161,15 +2167,23 @@ class RotaBuilder(object):
|
||||
# shift spans 7 days you may get >7 allocations in a row
|
||||
# as it only checks for a different shift allocation
|
||||
for constraint_shift in self.get_shifts_with_constraints(
|
||||
"pre", week=week
|
||||
"pre"#, week=week
|
||||
):
|
||||
for n in range(0, constraint_shift.constraint_options["pre"]):
|
||||
if day in constraint_shift.days:
|
||||
self.model.constraints.add(
|
||||
1
|
||||
>= self.model.works[
|
||||
#week_start_date = self.get_week_start_date(week)
|
||||
#try:
|
||||
#if constraint_shift.start_date <= week_start_date < constraint_shift.end_date:
|
||||
# works = 1
|
||||
try:
|
||||
works = self.model.works[
|
||||
worker.id, week, day, constraint_shift.name
|
||||
]
|
||||
except KeyError:
|
||||
continue
|
||||
self.model.constraints.add(
|
||||
1
|
||||
>= works
|
||||
+ sum(
|
||||
pre_map[n][0]
|
||||
* self.model.works[
|
||||
@@ -2209,15 +2223,25 @@ class RotaBuilder(object):
|
||||
# )
|
||||
|
||||
for constraint_shift in self.get_shifts_with_constraints(
|
||||
"post", week=week
|
||||
"post"#, week=week
|
||||
):
|
||||
for n in range(0, constraint_shift.constraint_options["post"]):
|
||||
if day in constraint_shift.days:
|
||||
self.model.constraints.add(
|
||||
1
|
||||
>= self.model.works[
|
||||
#week_start_date = self.get_week_start_date(week)
|
||||
#if constraint_shift.start_date <= week_start_date < constraint_shift.end_date:
|
||||
#works = 1
|
||||
#else:
|
||||
try:
|
||||
works = self.model.works[
|
||||
worker.id, week, day, constraint_shift.name
|
||||
]
|
||||
except KeyError:
|
||||
# If we can't find the key the shift is not assigned
|
||||
# so we don't need to worry about it
|
||||
continue
|
||||
self.model.constraints.add(
|
||||
1
|
||||
>= works
|
||||
+ sum(
|
||||
post_map[n][0]
|
||||
* self.model.works[
|
||||
@@ -2230,6 +2254,8 @@ class RotaBuilder(object):
|
||||
for w in workers
|
||||
)
|
||||
)
|
||||
#except KeyError:
|
||||
# pass
|
||||
|
||||
# for constraint_shift in self.get_shifts_with_constraints(
|
||||
# "preclear", "preclear2"
|
||||
@@ -2664,7 +2690,7 @@ class RotaBuilder(object):
|
||||
def pair_shifts(self, shift1: SingleShift, shift2: SingleShift):
|
||||
# NOTE: currently designed to pair two shifts
|
||||
# this could be extended...
|
||||
self.pair_shifts.append(set(shift1, shift2))
|
||||
self.paired_shifts.append([shift1, shift2])
|
||||
|
||||
def build_shifts(self):
|
||||
"""
|
||||
@@ -2684,6 +2710,11 @@ class RotaBuilder(object):
|
||||
for s in self.shifts:
|
||||
pass
|
||||
|
||||
## For paired shifts we create proxy shifts
|
||||
#self.paired_shift_map = {}
|
||||
#for shifts in self.paired_shifts:
|
||||
|
||||
|
||||
for s in self.shifts:
|
||||
if s.name in self.shift_names:
|
||||
raise InvalidShift(f"Duplicate shift: {s.name}")
|
||||
@@ -2746,6 +2777,7 @@ class RotaBuilder(object):
|
||||
for shift in self.get_shifts_with_constraint("post"):
|
||||
self.max_post = max(shift.constraint_options["post"], self.max_post)
|
||||
|
||||
|
||||
# # Todo replace with week_day.....
|
||||
# self.day_shift_product = []
|
||||
# self.day_shiftclass_product = []
|
||||
@@ -3038,6 +3070,7 @@ class RotaBuilder(object):
|
||||
f.write(self.get_worker_timetable_html(True))
|
||||
url = urllib.parse.quote(str(output_file.resolve()))
|
||||
print(f"Rota exported [bold blue]('[link url={url}]{url}')[/bold blue]")
|
||||
self.exported_rota_file = output_file
|
||||
|
||||
def export_rota_to_csv(self, filename: str = "rota"):
|
||||
output_file = Path("output", f"{filename}.csv")
|
||||
@@ -3272,6 +3305,8 @@ class RotaBuilder(object):
|
||||
shift = self.get_shift_by_name(shift_name)
|
||||
if "require_remote_site_presence_week" in shift.constraints:
|
||||
remote_site = f" data-shift-remote-site='{shift.constraint_options['require_remote_site_presence_week'][0]}'"
|
||||
if "night" in shift.constraints:
|
||||
css_class = " ".join((css_class, "night-shift"))
|
||||
|
||||
shift_tds.append(
|
||||
f"<td title='{title}' class='rota-day {css_class}' data-shift='{shift_name}' data-available='{available}' data-unavailable_reason='{unavailable_reason}' data-date='{d}' data-week='{week}' data-day='{day}'{remote_site}{requests}{bank_holiday}>{a}</td>"
|
||||
@@ -3453,7 +3488,7 @@ class RotaBuilder(object):
|
||||
|
||||
return timetable
|
||||
|
||||
def get_worker_shifts_by_date(self, worker: Worker) -> Dict:
|
||||
def get_worker_shifts_by_date(self, worker: Worker) -> Dict[datetime.date, str]:
|
||||
shifts = {}
|
||||
|
||||
n = 0
|
||||
@@ -3472,6 +3507,7 @@ class RotaBuilder(object):
|
||||
shifts = {}
|
||||
|
||||
for worker in self.workers:
|
||||
print("*", worker.name)
|
||||
shifts[worker.name] = len(
|
||||
[i for i in self.get_worker_shift_list(worker) if i != ""]
|
||||
)
|
||||
@@ -3487,7 +3523,7 @@ class RotaBuilder(object):
|
||||
|
||||
temp = ""
|
||||
for shift in self.get_shift_names_by_week_day(week, day):
|
||||
if self.model.works[worker.id, week, day, shift].value > 0:
|
||||
if self.model.works[worker.id, week, day, shift].value > 0.90:
|
||||
temp = shift
|
||||
shifts.append(temp)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user