diff --git a/output/block_shifts/timetable.js b/output/block_shifts/timetable.js
index 112736a..1fd3e23 100644
--- a/output/block_shifts/timetable.js
+++ b/output/block_shifts/timetable.js
@@ -445,9 +445,19 @@ $("#rota-table tr td:first-child").each((n, td) => {
function viewWorker(worker) {
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 = $(`
Site: ${ds.site}
FTE: ${ds.fte} (${ds.fte_adj})
+ Exact shifts: ${exactShiftsText}
Start date: ${ds.start_date}
End date: ${ds.end_date}
Non working days: ${ds.nwds}
diff --git a/output/timetable.js b/output/timetable.js
index 27e0bde..73a21fe 100644
--- a/output/timetable.js
+++ b/output/timetable.js
@@ -1783,9 +1783,19 @@ $("#rota-table tr td:first-child").each((n, td) => {
function viewWorker(worker) {
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 = $(`
Site: ${ds.site}
FTE: ${ds.fte} (${ds.fte_adj})
+ Exact shifts: ${exactShiftsText}
Start date: ${ds.start_date}
End date: ${ds.end_date}
Non working days: ${ds.nwds}
diff --git a/rota_generator/shifts.py b/rota_generator/shifts.py
index d73a771..2a571f2 100644
--- a/rota_generator/shifts.py
+++ b/rota_generator/shifts.py
@@ -5175,6 +5175,10 @@ class RotaBuilder(object):
l4.extend([worker.fte for worker in self.workers])
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():
d = [f"Week {week} Day {day}"]
@@ -5504,9 +5508,7 @@ class RotaBuilder(object):
# --- Highlight force assigned shifts ---
if force_assigned:
css_class += " force-assigned"
- title = f"{shift_name} ({d}) [FORCE ASSIGNED]"
- else:
- title = f"{shift_name} ({d})"
+ title = f"{title} [FORCE ASSIGNED]"
bank_holiday = ""
if d in self.bank_holidays:
@@ -5546,8 +5548,9 @@ class RotaBuilder(object):
if shift.has_constraint(NightConstraint):
css_class = " ".join((css_class, "night-shift"))
+ title_escaped = title.replace("'", "'").replace('"', """)
shift_tds.append(
- f"
@@ -5781,6 +5785,7 @@ class RotaBuilder(object):
weekend_sun=sun_count,
worker_summary_html=worker_summary_html,
previous_shifts=json.dumps(prior_map),
+ exact_shifts=json.dumps(getattr(worker, "exact_shifts", {})),
pair=worker.pair,
shift_balance_extra=json.dumps(worker.shift_balance_extra),
shift_diff=json.dumps(shift_diff_dict),
diff --git a/test/test_workers.py b/test/test_workers.py
index 2bb50bd..54f66e2 100644
--- a/test/test_workers.py
+++ b/test/test_workers.py
@@ -1832,4 +1832,40 @@ def test_exact_shifts_exceed_total_warning():
warnings = Rota.get_warnings("Exact shifts exceed total shifts")
assert len(warnings) > 0
- assert "total exact shifts (4) exceeds total required shifts" in warnings[0][1]
\ No newline at end of file
+ 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
\ No newline at end of file
|