add custom display char for shift

This commit is contained in:
Ross
2025-06-05 21:59:26 +01:00
parent 242a664f49
commit e46c24df88
2 changed files with 54 additions and 20 deletions
+24 -11
View File
@@ -143,6 +143,7 @@ class SingleShift(BaseModel):
start_date: datetime.date | None = None start_date: datetime.date | None = None
end_date: datetime.date | None = None end_date: datetime.date | None = None
pair_proxy: bool = False pair_proxy: bool = False
display_char: str | None = None # Add this line
model_config = ConfigDict( model_config = ConfigDict(
extra="allow", extra="allow",
@@ -160,6 +161,9 @@ class SingleShift(BaseModel):
# versus target shifts (hard constraint) # versus target shifts (hard constraint)
# if 0 exactly equal shifts must be assigend (unlikely to be possible) # if 0 exactly equal shifts must be assigend (unlikely to be possible)
if self.display_char is not None and len(self.display_char) > 1:
raise ValueError("display_char must be a single character or None")
if self.balance_offset is None: if self.balance_offset is None:
self.balance_offset = len(self.days) self.balance_offset = len(self.days)
@@ -215,6 +219,12 @@ class SingleShift(BaseModel):
return 0 return 0
def get_display_char(self):
"""Returns the display character for the shift"""
if self.display_char is not None:
return self.display_char
return self.name[0]
class RotaBuilder(object): class RotaBuilder(object):
"""Class to hold and manipulate shifts""" """Class to hold and manipulate shifts"""
@@ -873,8 +883,6 @@ class RotaBuilder(object):
def locum_request_init(model, wid, week, day, shift): def locum_request_init(model, wid, week, day, shift):
print("locum rquest init")
print(locals())
if (wid, week, day, shift) in self.locum_availability: if (wid, week, day, shift) in self.locum_availability:
self.locum_availability_map[(wid, week, day)] = shift self.locum_availability_map[(wid, week, day)] = shift
return 1 return 1
@@ -1896,6 +1904,8 @@ class RotaBuilder(object):
worker.id, week, day, shift.name worker.id, week, day, shift.name
] ]
) )
if self.get_workers_who_require_locums():
if not worker.locum_on_nwds and day == n: if not worker.locum_on_nwds and day == n:
if start <= self.week_day_date_map[(week, day)] < end: if start <= self.week_day_date_map[(week, day)] < end:
self.model.constraints.add( self.model.constraints.add(
@@ -3497,9 +3507,10 @@ class RotaBuilder(object):
for week, day in self.get_week_day_combinations(): for week, day in self.get_week_day_combinations():
a = "-" a = "-"
for shift in self.get_shift_names_by_week_day(week, day): for shift in self.get_shift_names_by_week_day(week, day):
shift_obj = self.get_shift_by_name(shift)
if model.works[worker.id, week, day, shift].value > 0: if model.works[worker.id, week, day, shift].value > 0:
shifts.append(shift) shifts.append(shift)
a = shift[0] a = shift_obj.get_display_char()
w.append(a) w.append(a)
shift_count = "" shift_count = ""
@@ -3616,15 +3627,16 @@ class RotaBuilder(object):
# if the worker has been assigned # if the worker has been assigned
locum = False locum = False
for shift in self.get_shift_names_by_week_day(week, day): for shift in self.get_shift_names_by_week_day(week, day):
shift_obj = self.get_shift_by_name(shift)
if model.works[worker.id, week, day, shift].value > 0.8: if model.works[worker.id, week, day, shift].value > 0.8:
shifts.append(shift) shifts.append(shift)
a = shift[0] a = shift_obj.get_display_char()
shift_name = shift shift_name = shift
break break
if self.get_workers_who_require_locums(): if self.get_workers_who_require_locums():
if model.locum_works[worker.id, week, day, shift].value > 0.8: if model.locum_works[worker.id, week, day, shift].value > 0.8:
locum_shifts.append(shift) locum_shifts.append(shift)
a = shift[0] a = shift_obj.get_display_char()
shift_name = shift shift_name = shift
locum = True locum = True
@@ -3872,9 +3884,9 @@ class RotaBuilder(object):
} }
for worker in self.workers: for worker in self.workers:
for shift in self.get_shift_names(): for shift in self.get_shift_names():
timetable[worker.name][shift] = self.model.shift_count[ timetable[worker.name][shift] = round(self.model.shift_count[
worker.id, shift worker.id, shift
].value ].value)
return timetable return timetable
@@ -3915,7 +3927,7 @@ class RotaBuilder(object):
for shift in self.get_shift_names_by_week_day(week, day): for shift in self.get_shift_names_by_week_day(week, day):
if self.model.works[worker.id, week, day, shift].value > 0.90: if self.model.works[worker.id, week, day, shift].value > 0.90:
temp = shift temp = shift
if include_locums and self.model.locum_works[worker.id, week, day, shift].value > 0.90: elif include_locums and self.model.locum_works[worker.id, week, day, shift].value > 0.90:
temp = shift temp = shift
shifts.append(temp) shifts.append(temp)
@@ -3925,7 +3937,7 @@ class RotaBuilder(object):
shifts = self.get_worker_shift_list(worker) shifts = self.get_worker_shift_list(worker)
# Convert shift to a string representation # Convert shift to a string representation
return "".join([i if i != "" else "-" for i in shifts]) return "".join([self.get_shift_by_name(i).get_display_char() if i != "" else "-" for i in shifts])
def get_shift_summary(self): def get_shift_summary(self):
works = self.model.works works = self.model.works
@@ -3957,7 +3969,7 @@ class RotaBuilder(object):
def get_shift_summary_html(self): def get_shift_summary_html(self):
works = self.model.works works = self.model.works
timetable = { timetable = {
worker.get_details(): {shift[0]: "" for shift in self.get_shift_names()} worker.get_details(): {shift.get_display_char(): "" for shift in self.get_shifts()}
for worker in self.workers for worker in self.workers
} }
# timetable = { worker.get_details() : { shift : "" for shift in ["truro_twilight"] } for worker in workers } # timetable = { worker.get_details() : { shift : "" for shift in ["truro_twilight"] } for worker in workers }
@@ -3977,7 +3989,8 @@ class RotaBuilder(object):
l.append(f"{shift} ({c})") l.append(f"{shift} ({c})")
total_shifts = total_shifts + c total_shifts = total_shifts + c
# print(worker.id, shift) # print(worker.id, shift)
timetable[worker.get_details()][shift[0]] = c shift_obj = self.get_shift_by_name(shift)
timetable[worker.get_details()][shift_obj.get_display_char()] = c
t.append(f"{worker.get_full_details()} [{total_shifts}]: {', '.join(l)}") t.append(f"{worker.get_full_details()} [{total_shifts}]: {', '.join(l)}")
return "\n".join(t) return "\n".join(t)
+21
View File
@@ -709,3 +709,24 @@ class TestShiftWorkerRequirements:
for worker in Rota.get_workers(): for worker in Rota.get_workers():
assert total_shifts[worker.name] == 30 assert total_shifts[worker.name] == 30
class TestDisplayChar:
def test_display_char_in_output(monkeypatch):
Rota = generate_basic_rota(workers=1)
shift = SingleShift(
sites=("group1",),
name="testshift",
length=8,
days=days[:1],
display_char="Z"
)
Rota.add_shifts(shift)
assert shift.display_char == "Z"
Rota.build_and_solve(options={"ratio": 0.1})
assert Rota.results.solver.status == "ok"
for worker in Rota.get_workers():
assert Rota.get_worker_shift_list_string(worker).startswith("Z------" * 5)