From 0851b992fb3a28f7bdc7a7cbba8552589af7b0da Mon Sep 17 00:00:00 2001 From: Ross Date: Wed, 17 Dec 2025 12:13:56 +0000 Subject: [PATCH] Enhance weekend shift handling by adding per-day adjustments and improving prior allocation parsing --- gen_cons.py | 3 ++ output/timetable.js | 35 +++++++++++++++++- rota_generator/shifts.py | 76 ++++++++++++++++++++++++++++++++-------- 3 files changed, 99 insertions(+), 15 deletions(-) diff --git a/gen_cons.py b/gen_cons.py index b3a763d..cceca3e 100644 --- a/gen_cons.py +++ b/gen_cons.py @@ -222,6 +222,7 @@ def load_workers(): if prev: priors_map[initial] = prev + logger.debug("priors_map: {}", priors_map) except FileNotFoundError: print(f"Prior allocations file not found: {priors_path}") except Exception as e: @@ -348,6 +349,8 @@ def load_workers(): if prev: priors_map[initial] = prev + + logger.debug("priors_map after per-day merge: {}", priors_map) except FileNotFoundError: print(f"Per-day prior file not found: {priordays_path}") except Exception as e: diff --git a/output/timetable.js b/output/timetable.js index 9c9bc91..1d54b9f 100644 --- a/output/timetable.js +++ b/output/timetable.js @@ -339,7 +339,9 @@ $(".table-div .worker-row .worker").each((n, tr) => { }) // Build per-shift prior-adjust spans from data-previous-shifts + // Try both the raw attribute and jQuery-parsed data for robustness let prev_attr = jtr.attr("data-previous-shifts"); + if (!prev_attr) prev_attr = jtr.data("previous-shifts"); let partsHtml = ""; let sat_adj = null; let sun_adj = null; @@ -347,8 +349,10 @@ $(".table-div .worker-row .worker").each((n, tr) => { try { let prev_map = (typeof prev_attr === 'object') ? prev_attr : JSON.parse(prev_attr); let parts = []; + // Build parts for non-weekend shifts first; we'll always render weekend per-day below. for (let k in prev_map) { if (!prev_map.hasOwnProperty(k)) continue; + if (k === 'weekend') continue; let entry = prev_map[k]; let adj = null; if (entry && typeof entry === 'object' && !Array.isArray(entry)) { @@ -367,8 +371,8 @@ $(".table-div .worker-row .worker").each((n, tr) => { // compute per-day weekend adjustments if present under 'weekend' if ('weekend' in prev_map) { let w = prev_map['weekend']; + // Prefer explicit per-day entries when present if (w && typeof w === 'object' && !Array.isArray(w)) { - // case: { 'Sat': {worked:.., allocated:..}, 'Sun': {...} } OR tuples if ('Sat' in w) { let e = w['Sat']; if (e && typeof e === 'object' && 'allocated' in e && 'worked' in e) sat_adj = parseFloat(e.allocated) - parseFloat(e.worked); @@ -379,6 +383,20 @@ $(".table-div .worker-row .worker").each((n, tr) => { if (e && typeof e === 'object' && 'allocated' in e && 'worked' in e) sun_adj = parseFloat(e.allocated) - parseFloat(e.worked); else if (Array.isArray(e) && e.length >= 2) sun_adj = parseFloat(e[1]) - parseFloat(e[0]); } + + // If no per-day entries but there is an overall weekend value, split it evenly + if (sat_adj === null && sun_adj === null && 'allocated' in w && 'worked' in w) { + try { + let total = parseFloat(w.allocated) - parseFloat(w.worked); + if (isFinite(total)) { + sat_adj = sun_adj = total / 2.0; + } + } catch (err) { + // leave as null on parse error + } + } + + // Handle a '__all__' bucket (tuple or object) as fallback if (sat_adj === null && sun_adj === null && '__all__' in w) { let e = w['__all__']; if (Array.isArray(e) && e.length >= 2) { @@ -394,6 +412,16 @@ $(".table-div .worker-row .worker").each((n, tr) => { let total = parseFloat(w[1]) - parseFloat(w[0]); sat_adj = sun_adj = total / 2.0; } + + // Always append per-day prior-adjust parts for weekend so the section is per-day + if (sat_adj !== null && Math.abs(sat_adj) > 0.0001) { + let s = (sat_adj > 0 ? '+' : '') + Number(sat_adj).toFixed(2); + partsHtml += `Sat:${s}`; + } + if (sun_adj !== null && Math.abs(sun_adj) > 0.0001) { + let s = (sun_adj > 0 ? '+' : '') + Number(sun_adj).toFixed(2); + partsHtml += `Sun:${s}`; + } } } catch (e) { @@ -408,7 +436,9 @@ $(".table-div .worker-row .worker").each((n, tr) => { if (!isFinite(adjf)) return ''; let sign = (adjf > 0) ? '+' : ''; let text = `${sign}${adjf.toFixed(2)}`; + // scale alpha by magnitude but ensure a minimum so small adjustments remain visible let mag = Math.min(1.0, Math.abs(adjf) / 3.0); + if (mag < 0.12) mag = 0.12; let bg, color; if (adjf > 0) { bg = `rgba(0,128,0,${mag.toFixed(2)})`; color = '#fff'; } else if (adjf < 0) { bg = `rgba(196,0,0,${mag.toFixed(2)})`; color = '#fff'; } @@ -436,6 +466,9 @@ $(".table-div .worker-row .worker").each((n, tr) => { // fallback: leave counts at 0 } + console.log("Computed weekend counts and adjustments for worker:", jtr.data("worker")); + console.log(`sat_count: ${sat_count}, sun_count: ${sun_count}, sat_adj: ${sat_adj}, sun_adj: ${sun_adj}`); + // Insert worker summary now that we have per-day adjustment badges // Hidden by default so summaries only appear when rota days are hidden let workerSummaryHtml = `