a lot of changes

This commit is contained in:
Ross
2025-06-21 11:45:58 +01:00
parent e15cc6ee51
commit 06a551bb68
4 changed files with 659 additions and 154 deletions
+303 -1
View File
@@ -867,4 +867,306 @@ def test_locums():
Rota.export_rota_to_html("test_locums", timestamp_filename=False)
assert Rota.results.solver.status == "ok"
assert Rota.results.solver.status == "ok"
def test_worker_assign_as_block_preference():
"""Test that a worker's assign_as_block preference is respected over the global setting."""
Rota = generate_basic_rota(workers=4, weeks_to_rota=12)
# Worker 1 prefers to have shift 'a' assigned as a block, worker 2 does not
workers = Rota.get_workers()
worker1 = workers[0]
worker2 = workers[1]
worker3 = workers[2]
worker4 = workers[3]
# Set a strong preference for worker1 to have 'a' as a block
worker1.assign_as_block_preferences = {"a":1}
worker2.assign_as_block_preferences = {"a":-1} # Worker 2 does not want 'a' as a block
# Add a shift 'a' that is NOT globally set as assign_as_block
Rota.add_shifts(
SingleShift(
sites=("group1",),
name="a",
length=8,
days=days[:4],
workers_required=1,
assign_as_block=False, # global setting is off
),
)
Rota.build_and_solve(options={"ratio": 0.0})
Rota.export_rota_to_html("test_worker_assign_as_block_preference", folder="tests")
# Worker 1 should have their 'a' shifts grouped together (block), worker 2 may not
shift_list_1 = Rota.get_worker_shift_list_string(worker1)
assert shift_list_1.count("aaaa") == 3, "Worker 1 should have all 'a' shifts grouped together (block)"
shift_list_2 = Rota.get_worker_shift_list_string(worker2)
assert shift_list_2.count("a") == 12, "Worker 2 should have at 12 'a' shift, but may not be grouped together"
shift_list_3 = Rota.get_worker_shift_list_string(worker3)
assert shift_list_3.count("a") == 12, "Worker 3 should have at least 12 'a' shifts, but may not be grouped together"
shift_list_4 = Rota.get_worker_shift_list_string(worker4)
assert shift_list_4.count("a") == 12, "Worker 4 should have at least 12 'a' shifts, but may not be grouped together"
# Modify worker1's preference to prevent block 'a' shifts
worker1.assign_as_block_preferences = {"a":-1}
Rota.build_and_solve(options={"ratio": 0.0})
shift_list_1 = Rota.get_worker_shift_list_string(worker1)
assert shift_list_1.count("aaaa") == 0, "Worker 1 should have all 'a' shifts grouped together (block)"
worker1.assign_as_block_preferences = {"a":0}
a_shift = Rota.get_shift_by_name("a")
a_shift.assign_as_block = True # Set the shift 'a' to be assigned as a block globally
Rota.build_and_solve(options={"ratio": 0.0})
for worker in Rota.get_workers():
shift_list = Rota.get_worker_shift_list_string(worker)
# All workers should have 'a' shifts grouped together (block)
assert shift_list.count("aaaa") == 3, f"Worker {worker.name} should have all 'a' shifts grouped together (block)"
def test_worker_assign_as_block_preference_multiple_shifts():
"""Test that a worker's assign_as_block preference is respected over the global setting."""
Rota = generate_basic_rota(workers=4, weeks_to_rota=12)
# Worker 1 prefers to have shift 'a' assigned as a block, worker 2 does not
workers = Rota.get_workers()
worker1 = workers[0]
worker2 = workers[1]
worker3 = workers[2]
worker4 = workers[3]
# Set a strong preference for worker1 to have 'a' as a block
worker1.assign_as_block_preferences = {"a":100}
worker2.assign_as_block_preferences = {"b":-10000} # Worker 2 does not want 'a' as a block
# Add a shift 'a' that is NOT globally set as assign_as_block
Rota.add_shifts(
SingleShift(
sites=("group1",),
name="a",
length=8,
days=days[:4],
workers_required=1,
assign_as_block=False, # global setting is off
),
SingleShift(
sites=("group1",),
name="b",
length=8,
days=days[4:],
workers_required=1,
assign_as_block=False, # global setting is off
),
)
shift_list_1 = Rota.get_worker_shift_list_string(worker1)
assert shift_list_1.count("aaaa") == 3, "Worker 1 should have all 'a' shifts grouped together (block)"
Rota.build_and_solve(options={"ratio": 0.0})
Rota.export_rota_to_html("test_worker_assign_as_block_preference", folder="tests")
def test_worker_assign_as_block_preference_multiple_sites():
"""Test that a worker's assign_as_block preference is respected over the global setting."""
Rota = generate_basic_rota(workers=6, weeks_to_rota=12)
# Worker 1 prefers to have shift 'a' assigned as a block, worker 2 does not
workers = Rota.get_workers()
worker1 = workers[0]
worker2 = workers[1]
worker3 = workers[2]
worker4 = workers[3]
worker5 = workers[4]
worker6 = workers[5]
# Set a strong preference for worker1 to have 'a' as a block
worker1.assign_as_block_preferences = {"a":1}
worker4.site = "group2"
worker5.site = "group2"
worker6.site = "group2"
worker4.assign_as_block_preferences = {"b":-10000} # Worker 2 does not want 'a' as a block
# Add a shift 'a' that is NOT globally set as assign_as_block
Rota.add_shifts(
SingleShift(
sites=("group1",),
name="a",
length=8,
days=days[:4],
workers_required=1,
assign_as_block=False, # global setting is off
),
SingleShift(
sites=("group2",),
name="b",
length=8,
days=days[4:],
workers_required=1,
assign_as_block=False, # global setting is off
),
)
Rota.build_and_solve(options={"ratio": 0.0})
Rota.export_rota_to_html("test_worker_assign_as_block_preference", folder="tests")
def test_worker_hard_day_dependency():
Rota = generate_basic_rota(workers=6, weeks_to_rota=16)
workers = Rota.get_workers()
worker1 = workers[0]
worker2 = workers[1]
worker3 = workers[2]
worker1.add_hard_day_dependency("Mon", "Tue")
worker2.add_hard_day_dependency("Mon", "Wed")
worker3.add_hard_day_dependency("Mon", "Thu")
Rota.add_shifts(
SingleShift(
sites=("group1",),
name="a",
length=8,
days=days,
workers_required=2,
assign_as_block=False, # global setting is off
),
)
Rota.build_and_solve(options={"ratio": 0.0})
Rota.export_rota_to_html("test_worker_hard_day_depend", folder="tests")
for worker in Rota.get_workers():
shift_list = Rota.get_worker_shift_list_string(worker)
if worker.name == "worker1":
# Check that every time 'a' is assigned on Monday, it is also assigned on Tuesday
for week in range(12):
mon_idx = week * 7 + days.index("Mon")
tue_idx = week * 7 + days.index("Tue")
if shift_list[mon_idx] == "a":
assert shift_list[tue_idx] == "a", f"Worker1 assigned 'a' on Mon (week {week}) but not on Tue"
if worker.name == "worker2":
# Check that every time 'a' is assigned on Monday, it is also assigned on Wednesday
for week in range(12):
mon_idx = week * 7 + days.index("Mon")
wed_idx = week * 7 + days.index("Wed")
if shift_list[mon_idx] == "a":
assert shift_list[wed_idx] == "a", f"Worker2 assigned 'a' on Mon (week {week}) but not on Wed"
if worker.name == "worker3":
# Check that every time 'a' is assigned on Monday, it is also assigned on Thursday
for week in range(12):
mon_idx = week * 7 + days.index("Mon")
thu_idx = week * 7 + days.index("Thu")
if shift_list[mon_idx] == "a":
assert shift_list[thu_idx] == "a", f"Worker3 assigned 'a' on Mon (week {week}) but not on Thu"
def test_worker_hard_day_dependency_weekend():
Rota = generate_basic_rota(workers=4, weeks_to_rota=16)
workers = Rota.get_workers()
worker1 = workers[0]
worker2 = workers[1]
worker3 = workers[2]
worker4 = workers[3]
worker1.add_hard_day_dependency("Fri", "Sun")
worker2.add_hard_day_dependency("Fri", "Sun")
worker3.add_hard_day_dependency("Fri", "Sun")
worker4.add_hard_day_dependency("Fri", "Sun")
worker1.add_hard_day_exclusion("Fri", "Sat")
worker2.add_hard_day_exclusion("Fri", "Sat")
worker3.add_hard_day_exclusion("Fri", "Sat")
worker4.add_hard_day_exclusion("Fri", "Sat")
Rota.add_shifts(
SingleShift(
sites=("group1",),
name="a",
length=8,
days=days[4:],
workers_required=1,
assign_as_block=False, # global setting is off
),
)
Rota.build_and_solve(options={"ratio": 0.0})
Rota.export_rota_to_html("test_worker_hard_day_depend", folder="tests")
for worker in Rota.get_workers():
shift_list = Rota.get_worker_shift_list_string(worker)
# Check that every time 'a' is assigned on Monday, it is also assigned on Tuesday
for week in range(16):
fri_idx = week * 7 + days.index("Fri")
sat_idx = week * 7 + days.index("Sat")
sun_idx = week * 7 + days.index("Sun")
if shift_list[fri_idx] == "a":
assert shift_list[sun_idx] == "a", f"Worker1 assigned 'a' on Fri (week {week}) but not on Sun"
assert shift_list[sat_idx] != "a", f"Worker1 assigned 'a' on Fri (week {week}) but also on Sat, which should not happen due to hard day exclusion"
def test_worker_hard_day_dependency_weekend2():
Rota = generate_basic_rota(workers=4, weeks_to_rota=16)
workers = Rota.get_workers()
worker1 = workers[0]
worker2 = workers[1]
worker3 = workers[2]
worker4 = workers[3]
worker1.add_hard_day_dependency("Fri", "Sun")
worker2.add_hard_day_dependency("Fri", "Sun")
worker3.add_hard_day_dependency("Fri", "Sun")
worker4.add_hard_day_dependency("Fri", "Sun")
worker4.add_hard_day_dependency("Fri", "Sat")
worker1.add_hard_day_exclusion("Fri", "Sat")
worker2.add_hard_day_exclusion("Fri", "Sat")
worker3.add_hard_day_exclusion("Fri", "Sat")
Rota.add_shifts(
SingleShift(
sites=("group1",),
name="a",
length=8,
days=days[4:],
workers_required=1,
assign_as_block=False, # global setting is off
),
)
Rota.build_and_solve(options={"ratio": 0.0})
Rota.export_rota_to_html("test_worker_hard_day_depend", folder="tests")
for worker in Rota.get_workers():
shift_list = Rota.get_worker_shift_list_string(worker)
# Check that every time 'a' is assigned on Monday, it is also assigned on Tuesday
for week in range(16):
fri_idx = week * 7 + days.index("Fri")
sat_idx = week * 7 + days.index("Sat")
sun_idx = week * 7 + days.index("Sun")
if worker != worker4 and shift_list[fri_idx] == "a":
assert shift_list[sun_idx] == "a", f"Worker1 assigned 'a' on Fri (week {week}) but not on Sun"
assert shift_list[sat_idx] != "a", f"Worker1 assigned 'a' on Fri (week {week}) but also on Sat, which should not happen due to hard day exclusion"
if worker == worker4 and shift_list[fri_idx] == "a":
assert shift_list[sun_idx] == "a", f"Worker4 assigned 'a' on Fri (week {week}) but not on Sun"
assert shift_list[sat_idx] == "a", f"Worker4 assigned 'a' on Fri (week {week}) but not on Sun"