feat: add exact shifts display in worker details and update HTML export functionality
This commit is contained in:
@@ -445,9 +445,19 @@ $("#rota-table tr td:first-child").each((n, td) => {
|
|||||||
|
|
||||||
function viewWorker(worker) {
|
function viewWorker(worker) {
|
||||||
ds = worker.dataset;
|
ds = worker.dataset;
|
||||||
|
let exactShiftsText = "(none)";
|
||||||
|
if (ds.exactShifts) {
|
||||||
|
try {
|
||||||
|
let parsed = JSON.parse(ds.exactShifts);
|
||||||
|
if (Object.keys(parsed).length > 0) {
|
||||||
|
exactShiftsText = Object.entries(parsed).map(([s, c]) => `${s}: ${c}`).join(", ");
|
||||||
|
}
|
||||||
|
} catch(e) {}
|
||||||
|
}
|
||||||
dlg = $(`<div class='dialog' title='${ds.worker}'>
|
dlg = $(`<div class='dialog' title='${ds.worker}'>
|
||||||
Site: ${ds.site}</br>
|
Site: ${ds.site}</br>
|
||||||
FTE: ${ds.fte} (${ds.fte_adj})</br>
|
FTE: ${ds.fte} (${ds.fte_adj})</br>
|
||||||
|
Exact shifts: ${exactShiftsText}</br>
|
||||||
Start date: ${ds.start_date}</br>
|
Start date: ${ds.start_date}</br>
|
||||||
End date: ${ds.end_date}</br>
|
End date: ${ds.end_date}</br>
|
||||||
Non working days: ${ds.nwds}</br>
|
Non working days: ${ds.nwds}</br>
|
||||||
|
|||||||
@@ -1783,9 +1783,19 @@ $("#rota-table tr td:first-child").each((n, td) => {
|
|||||||
|
|
||||||
function viewWorker(worker) {
|
function viewWorker(worker) {
|
||||||
ds = worker.dataset;
|
ds = worker.dataset;
|
||||||
|
let exactShiftsText = "(none)";
|
||||||
|
if (ds.exactShifts) {
|
||||||
|
try {
|
||||||
|
let parsed = JSON.parse(ds.exactShifts);
|
||||||
|
if (Object.keys(parsed).length > 0) {
|
||||||
|
exactShiftsText = Object.entries(parsed).map(([s, c]) => `${s}: ${c}`).join(", ");
|
||||||
|
}
|
||||||
|
} catch(e) {}
|
||||||
|
}
|
||||||
dlg = $(`<div class='dialog' title='${ds.worker}'>
|
dlg = $(`<div class='dialog' title='${ds.worker}'>
|
||||||
Site: ${ds.site}</br>
|
Site: ${ds.site}</br>
|
||||||
FTE: ${ds.fte} (${ds.fte_adj})</br>
|
FTE: ${ds.fte} (${ds.fte_adj})</br>
|
||||||
|
Exact shifts: ${exactShiftsText}</br>
|
||||||
Start date: ${ds.start_date}</br>
|
Start date: ${ds.start_date}</br>
|
||||||
End date: ${ds.end_date}</br>
|
End date: ${ds.end_date}</br>
|
||||||
Non working days: ${ds.nwds}</br>
|
Non working days: ${ds.nwds}</br>
|
||||||
|
|||||||
@@ -5175,6 +5175,10 @@ class RotaBuilder(object):
|
|||||||
l4.extend([worker.fte for worker in self.workers])
|
l4.extend([worker.fte for worker in self.workers])
|
||||||
wr.writerow(l4)
|
wr.writerow(l4)
|
||||||
|
|
||||||
|
l5 = ["Exact Shifts"]
|
||||||
|
l5.extend([json.dumps(getattr(worker, "exact_shifts", {})) for worker in self.workers])
|
||||||
|
wr.writerow(l5)
|
||||||
|
|
||||||
for week, day in self.get_week_day_combinations():
|
for week, day in self.get_week_day_combinations():
|
||||||
d = [f"Week {week} Day {day}"]
|
d = [f"Week {week} Day {day}"]
|
||||||
|
|
||||||
@@ -5504,9 +5508,7 @@ class RotaBuilder(object):
|
|||||||
# --- Highlight force assigned shifts ---
|
# --- Highlight force assigned shifts ---
|
||||||
if force_assigned:
|
if force_assigned:
|
||||||
css_class += " force-assigned"
|
css_class += " force-assigned"
|
||||||
title = f"{shift_name} ({d}) [FORCE ASSIGNED]"
|
title = f"{title} [FORCE ASSIGNED]"
|
||||||
else:
|
|
||||||
title = f"{shift_name} ({d})"
|
|
||||||
|
|
||||||
bank_holiday = ""
|
bank_holiday = ""
|
||||||
if d in self.bank_holidays:
|
if d in self.bank_holidays:
|
||||||
@@ -5546,8 +5548,9 @@ class RotaBuilder(object):
|
|||||||
if shift.has_constraint(NightConstraint):
|
if shift.has_constraint(NightConstraint):
|
||||||
css_class = " ".join((css_class, "night-shift"))
|
css_class = " ".join((css_class, "night-shift"))
|
||||||
|
|
||||||
|
title_escaped = title.replace("'", "'").replace('"', """)
|
||||||
shift_tds.append(
|
shift_tds.append(
|
||||||
f"<td title='{','.join(shift_names)} ({d})' class='rota-day {css_class}'"
|
f"<td title='{title_escaped}' class='rota-day {css_class}'"
|
||||||
f" data-shift='{','.join(assigned_shift_names)}'"
|
f" data-shift='{','.join(assigned_shift_names)}'"
|
||||||
f" data-shift-display='{','.join(shift_names)}'"
|
f" data-shift-display='{','.join(shift_names)}'"
|
||||||
f" data-available='{available}'"
|
f" data-available='{available}'"
|
||||||
@@ -5758,6 +5761,7 @@ class RotaBuilder(object):
|
|||||||
data-weekend-sat='{weekend_sat}'
|
data-weekend-sat='{weekend_sat}'
|
||||||
data-weekend-sun='{weekend_sun}'
|
data-weekend-sun='{weekend_sun}'
|
||||||
data-previous-shifts='{previous_shifts}'
|
data-previous-shifts='{previous_shifts}'
|
||||||
|
data-exact-shifts='{exact_shifts}'
|
||||||
data-shift-diff='{shift_diff}'
|
data-shift-diff='{shift_diff}'
|
||||||
data-shift-diff-summed='{shift_diff_summed}'
|
data-shift-diff-summed='{shift_diff_summed}'
|
||||||
>
|
>
|
||||||
@@ -5781,6 +5785,7 @@ class RotaBuilder(object):
|
|||||||
weekend_sun=sun_count,
|
weekend_sun=sun_count,
|
||||||
worker_summary_html=worker_summary_html,
|
worker_summary_html=worker_summary_html,
|
||||||
previous_shifts=json.dumps(prior_map),
|
previous_shifts=json.dumps(prior_map),
|
||||||
|
exact_shifts=json.dumps(getattr(worker, "exact_shifts", {})),
|
||||||
pair=worker.pair,
|
pair=worker.pair,
|
||||||
shift_balance_extra=json.dumps(worker.shift_balance_extra),
|
shift_balance_extra=json.dumps(worker.shift_balance_extra),
|
||||||
shift_diff=json.dumps(shift_diff_dict),
|
shift_diff=json.dumps(shift_diff_dict),
|
||||||
|
|||||||
+37
-1
@@ -1832,4 +1832,40 @@ def test_exact_shifts_exceed_total_warning():
|
|||||||
|
|
||||||
warnings = Rota.get_warnings("Exact shifts exceed total shifts")
|
warnings = Rota.get_warnings("Exact shifts exceed total shifts")
|
||||||
assert len(warnings) > 0
|
assert len(warnings) > 0
|
||||||
assert "total exact shifts (4) exceeds total required shifts" in warnings[0][1]
|
assert "total exact shifts (4) exceeds total required shifts" in warnings[0][1]
|
||||||
|
|
||||||
|
|
||||||
|
def test_html_export_worker_details_and_unavailable_reason():
|
||||||
|
weeks_to_rota = 2
|
||||||
|
start_date = datetime.date(2022, 3, 7)
|
||||||
|
|
||||||
|
Rota = RotaBuilder(
|
||||||
|
start_date,
|
||||||
|
weeks_to_rota=weeks_to_rota,
|
||||||
|
)
|
||||||
|
Rota.add_workers([
|
||||||
|
Worker(
|
||||||
|
name="worker1",
|
||||||
|
site="group1",
|
||||||
|
grade=1,
|
||||||
|
fte=100,
|
||||||
|
exact_shifts={"a": 1},
|
||||||
|
start_date=datetime.date(2022, 3, 14)
|
||||||
|
),
|
||||||
|
])
|
||||||
|
Rota.add_shifts(
|
||||||
|
SingleShift(
|
||||||
|
sites=("group1",),
|
||||||
|
name="a",
|
||||||
|
length=12.5,
|
||||||
|
days=("Mon",),
|
||||||
|
workers_required=1,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
Rota.build_workers()
|
||||||
|
Rota.build_model()
|
||||||
|
|
||||||
|
html = Rota.get_worker_timetable_html()
|
||||||
|
|
||||||
|
assert 'data-exact-shifts=\'{"a": 1}\'' in html
|
||||||
|
assert 'title=\' (2022-03-07) / START DATE: 2022-03-14\'' in html
|
||||||
Reference in New Issue
Block a user