356 lines
11 KiB
JavaScript
356 lines
11 KiB
JavaScript
Object.defineProperties(Array.prototype, {
|
|
count: {
|
|
value: function (query) {
|
|
/*
|
|
Counts number of occurrences of query in array, an integer >= 0
|
|
Uses the javascript == notion of equality.
|
|
*/
|
|
var count = 0;
|
|
for (let i = 0; i < this.length; i++)
|
|
if (this[i] == query)
|
|
count++;
|
|
return count;
|
|
}
|
|
}
|
|
});
|
|
|
|
const mean = arr => arr.reduce((p, c) => p + c, 0) / arr.length;
|
|
|
|
tables = $(".table-div");
|
|
|
|
|
|
function generateExtra() {
|
|
$(".auto-generated").remove();
|
|
global_shifts = new Set();
|
|
|
|
tables.each((n, table) => {
|
|
|
|
rows = $(table).find(".worker-row");
|
|
|
|
workers = {};
|
|
|
|
rows.each((n, tr) => {
|
|
|
|
|
|
worker_td = $(tr).find("td:first");
|
|
|
|
worker = worker_td.attr("data-worker");
|
|
|
|
shift_tds = $(tr).find(".rota-day");
|
|
shifts = []
|
|
|
|
shift_tds.each((n, td) => {
|
|
shifts.push($(td).attr("data-shift"));
|
|
})
|
|
|
|
shifts_unique = new Set(shifts);
|
|
shifts_unique.delete("");
|
|
global_shifts = new Set([...global_shifts, ...shifts_unique]);
|
|
|
|
weekends_worked = 0;
|
|
|
|
weeks = shift_tds.length / 7;
|
|
|
|
whole_weekends_worked = 0;
|
|
|
|
for (var i = 1; i <= weeks; i++) {
|
|
if ($(tr).find(`[data-week='${i}'][data-day='Sat']`).attr("data-shift") != "" || $(tr).find(`[data-week='${i}'][data-day='Sun']`).attr("data-shift") != "") {
|
|
weekends_worked = weekends_worked + 1;
|
|
}
|
|
if ($(tr).find(`[data-week='${i}'][data-day='Sat']`).attr("data-shift") != "" && $(tr).find(`[data-week='${i}'][data-day='Sun']`).attr("data-shift") != "") {
|
|
whole_weekends_worked = whole_weekends_worked + 1;
|
|
}
|
|
}
|
|
|
|
shift_counts = {};
|
|
|
|
total_shifts = 0;
|
|
days_lost = 0;
|
|
shifts_unique.forEach(el => {
|
|
shift_counts[el] = shifts.count(el);
|
|
total_shifts = total_shifts + shifts.count(el);
|
|
|
|
// why like this?
|
|
if (el == "night_weekend") {
|
|
whole_weekends_worked = whole_weekends_worked - shifts.count(el) / 3
|
|
}
|
|
|
|
if ($("#global-settings").length == 1) {
|
|
|
|
n = $(`#${el}-tdl`).val();
|
|
if (!isNaN(n)) {
|
|
days_lost = days_lost + shifts.count(el) * n;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
// // 5 days for weekday nights
|
|
// if(el == "night_weekday") { days_lost = days_lost + shifts.count(el) / 4 * 5 }
|
|
// // 4 days for weekend nights
|
|
// if(el == "night_weekend") {
|
|
// days_lost = days_lost + shifts.count(el) / 3 * 4
|
|
// //whole_weekends_worked = whole_weekends_worked - shifts.count(el) / 3
|
|
// }
|
|
// // 0.5 for twilights
|
|
// if(el.includes("twilight")) {
|
|
// twilight_days_lost = shifts.count(el) / 2
|
|
// days_lost = days_lost + twilight_days_lost
|
|
|
|
// }
|
|
|
|
});
|
|
|
|
// Also 1 day for a whole weekend
|
|
days_lost = days_lost + whole_weekends_worked
|
|
|
|
//days_lost = days_lost + whole_weekends_worked;
|
|
//console.log(shift_counts);
|
|
|
|
nwds = worker_td.attr("data-nwds");
|
|
|
|
console.log(nwds);
|
|
|
|
if (nwds != "None") {
|
|
nwds.split(", ").forEach((nwd) => {
|
|
console.log(worker, nwd, shift_tds);
|
|
$(shift_tds).filter(`.${nwd}`).addClass("nwd");
|
|
})
|
|
}
|
|
|
|
workers[worker] = {
|
|
"site": worker_td.attr("data-site"),
|
|
"nwds": worker_td.attr("data-nwds"),
|
|
"fte": worker_td.attr("data-fte_adj"),
|
|
"end_date": worker_td.attr("data-end_date"),
|
|
"shift_diff": worker_td.attr("data-shift-diff"),
|
|
"shifts": shifts,
|
|
"shift_types": shifts_unique,
|
|
"weekends_worked": weekends_worked,
|
|
"shift_counts": shift_counts,
|
|
"days_lost": days_lost,
|
|
//"shifts"
|
|
}
|
|
|
|
sc = JSON.stringify(shift_counts);
|
|
|
|
|
|
bank_holidays = shift_tds.filter(".bank-holiday").filter(function (i) {
|
|
return $(this).attr('data-shift') != "";
|
|
});
|
|
console.log("BH", bank_holidays.length)
|
|
|
|
bank_holidays_worked = bank_holidays.length;
|
|
|
|
shift_diff = JSON.parse(worker_td.attr("data-shift-diff"));
|
|
summed_shift_diff = Object.values(shift_diff).reduce((a, b) => a + b);
|
|
|
|
worker_td.after(`<div class='worker-summary auto-generated'>
|
|
<span>Total shifts: ${total_shifts}, </span>
|
|
<span>Weekends: ${weekends_worked}, </span>
|
|
<span>Bank holidays: ${bank_holidays_worked}, </span>
|
|
<span>Summed shift diff: ${summed_shift_diff}, </span>
|
|
|
|
</div>`)
|
|
|
|
})
|
|
|
|
sites = {};
|
|
sites_days_lost = {}
|
|
for (const worker in workers) {
|
|
//console.log(worker, workers[worker]);
|
|
w = workers[worker];
|
|
if (w.fte == 100) {
|
|
//console.log(w.shift_types);
|
|
for (let shift of w.shift_types) {
|
|
//console.log(shift);
|
|
if (!(w.site in sites)) {
|
|
sites[w.site] = {};
|
|
|
|
}
|
|
if (!(shift in sites[w.site])) {
|
|
sites[w.site][shift] = [w.shift_counts[shift]];
|
|
} else {
|
|
sites[w.site][shift].push(w.shift_counts[shift]);
|
|
}
|
|
}
|
|
if (!(w.site in sites_days_lost)) {
|
|
sites_days_lost[w.site] = [w.days_lost];
|
|
|
|
} else {
|
|
sites_days_lost[w.site].push(w.days_lost);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Calculate site shift summaries
|
|
// $(table).append("<div class='site-summary auto-generated'>Average shifts for a 100% FTE trainee</div>");
|
|
|
|
// for(const site in sites) {
|
|
// $(table).find(".site-summary").append(`<div class='site site-${site}'><span class='site-name'>${site}</span></div>`);
|
|
|
|
// for(const shift in sites[site]) {
|
|
// average = mean(sites[site][shift])
|
|
// $(table).find(`.site-${site}`).append(`<div>${shift}: ${average.toFixed(2)}</div>`);
|
|
// }
|
|
|
|
// days_lost_avg = mean(sites_days_lost[site]);
|
|
// $(table).find(`.site-${site}`).append(`<div class='training-days-lost'>Training days lost: ${days_lost_avg.toFixed(2)}</div>`);
|
|
// }
|
|
|
|
summary_button = $("<button class='auto-generated'>Toggle Summary</button>").click(() => {
|
|
$(table).find(".rota-day").toggleClass("hidden");
|
|
$(table).find(".worker-summary").toggle();
|
|
|
|
});
|
|
|
|
|
|
$(table).before(summary_button);
|
|
|
|
|
|
|
|
});
|
|
return global_shifts
|
|
}
|
|
|
|
shifts = generateExtra();
|
|
console.log(shifts)
|
|
|
|
if (shifts.size < 1) {
|
|
shifts = $("#shifts-container").data("shifts");
|
|
}
|
|
console.log(shifts)
|
|
|
|
oshifts = Array.from(shifts).sort()
|
|
|
|
$("body").append("<div><table class='tsummary'></table></div>")
|
|
|
|
$("table.tsummary").append("<tr class='header'></tr>");
|
|
h = $("table.tsummary tr.header");
|
|
h.append(`<th>Worker</th>`);
|
|
h.append(`<th>Site</th>`);
|
|
h.append(`<th>FTE</th>`);
|
|
h.append(`<th>FTE (adj)</th>`);
|
|
|
|
|
|
oshifts.forEach((s) => {
|
|
h.append(`<th>${s}</th>`);
|
|
|
|
});
|
|
|
|
|
|
|
|
$(".table-div .worker-row .worker").each((n, tr) => {
|
|
//worker_td = $(tr).find("td").first();
|
|
jtr = $(tr);
|
|
row = $("<tr></tr>");
|
|
row.append(`<td>${jtr.data("worker")}</td>`)
|
|
row.append(`<td>${jtr.data("site")}</td>`)
|
|
row.append(`<td>${jtr.data("fte")}</td>`)
|
|
row.append(`<td>${parseFloat(jtr.data("fte_adj")).toFixed(2)}</td>`)
|
|
|
|
shift_counts = jtr.data("shift-counts")
|
|
shift_targets = jtr.data("worker-targets")
|
|
|
|
oshifts.forEach((s) => {
|
|
if (s in shift_targets && shift_targets[s] > 0) {
|
|
if (s in shift_counts) {
|
|
c = shift_counts[s];
|
|
} else {
|
|
c = 0;
|
|
}
|
|
row.append(`<td>${c} (${shift_targets[s].toFixed(2)})</td>`)
|
|
|
|
} else {
|
|
row.append(`<td>-</td>`)
|
|
}
|
|
})
|
|
|
|
$("table.tsummary").append(row)
|
|
})
|
|
|
|
//$("table.summary")
|
|
|
|
// $("body").prepend("<div id='global-settings'>Settings:<br />Customise training days lost per shift (you need to press recalculate for changes to take effect)<form></form></div>")
|
|
|
|
// shifts.forEach((s) => {
|
|
|
|
// disable = false;
|
|
// if(s.includes("twilight")) {
|
|
// value = 0.5;
|
|
// } else if(s == "night_weekend") {
|
|
// value = 4 / 3;
|
|
// } else if(s == "night_weekday") {
|
|
// value = 5 / 4;
|
|
// } else {
|
|
// disable = true;
|
|
// value = 0;
|
|
// }
|
|
|
|
// if(!disable) {
|
|
// $("#global-settings form").append(`<span class='tdl-settings'><label for="${s}-tdl">${s}</label><input type="number" id="${s}-tdl" name="${s}-tdl" value='${value}'></span>`);
|
|
// }
|
|
|
|
|
|
// })
|
|
// $("#global-settings").append($("<button >Recalculate</button>").click(() => {
|
|
// generateExtra();
|
|
// }))
|
|
|
|
// generateExtra();
|
|
|
|
$(".table-div + pre").each((n, pre) => {
|
|
|
|
$(pre).before($("<button class='shift-breakdown-button'>Show shift breakdown</button>").click(() => {
|
|
$(pre).toggle();
|
|
}));
|
|
|
|
})
|
|
|
|
$("td").hover((e) => {
|
|
//start hover
|
|
pair = $(e.target).closest('tr').find('td:first')[0].dataset.pair;
|
|
index = $(e.target).index() - 1;
|
|
if (pair > 0) {
|
|
$(`td[data-pair=${pair}]`).each((n, el) => {
|
|
$($(el).closest('tr').find('td').get(index)).addClass("pair-match")
|
|
})
|
|
}
|
|
if (e.target.dataset.shift != undefined && e.target.dataset.shift.length > 0) {
|
|
$(`td[data-shift=${e.target.dataset.shift}]`).addClass("shift-highlight")
|
|
|
|
if (e.target.dataset.shift.includes("night")) {
|
|
$(`td[data-night-at-derriford=1]`).siblings().addClass("derriford-night")
|
|
}
|
|
}
|
|
|
|
}, (e) => {
|
|
|
|
//end hover
|
|
$("td.pair-match").removeClass("pair-match")
|
|
$("td.shift-highlight").removeClass("shift-highlight")
|
|
$("td.derriford-night").removeClass("derriford-night")
|
|
|
|
})
|
|
|
|
$("#rota-table tr td:first-child").each((n, td) => {
|
|
$(td).click(() => {
|
|
viewWorker(td);
|
|
})
|
|
})
|
|
|
|
function viewWorker(worker) {
|
|
ds = worker.dataset;
|
|
dlg = $(`<div class='dialog' title='${ds.worker}'>
|
|
Site: ${ds.site}</br>
|
|
FTE: ${ds.fte} (${ds.fte_adj})</br>
|
|
End date: ${ds.end_date}
|
|
</div>`)
|
|
$("body").append(dlg)
|
|
|
|
dlg.dialog()
|
|
|
|
|
|
console.log(worker);
|
|
|
|
} |