job history

Signed-off-by: Vladimir Mandic <mandic00@live.com>
This commit is contained in:
Vladimir Mandic
2025-09-12 22:14:07 -04:00
parent 8cd5fbc926
commit 1c5b3a47e2
9 changed files with 250 additions and 58 deletions
+48
View File
@@ -0,0 +1,48 @@
function refreshHistory() {
log('refreshHistory');
fetch(`${window.api}/history`, { priority: 'low' }).then((res) => {
const timeline = document.getElementById('history_timeline');
const table = document.getElementById('history_table');
res.json().then((data) => {
if (!data || !data.length) {
table.innerHTML = '<p>No history data available.</p>';
return;
}
// build table
let html = '<table><thead><tr><th>Time</th><th>ID</th><th>Job</th><th>Action</th><th>Duration</th><th>Outputs</th></tr></thead><tbody>';
for (const entry of data) {
const ts = new Date(1000 * entry.timestamp).toLocaleString();
const duration = entry.duration ? `${(entry.duration).toFixed(3)}` : '';
const outputs = entry.outputs.join(', ');
html += `<tr><td>${ts}</td><td>${entry.id}</td><td>${entry.job}</td><td>${entry.op}</td><td>${duration}</td><td>${outputs}</td></tr>`;
}
html += '</tbody></table>';
table.innerHTML = html;
// crop data to last processing session
let startIdx = -1;
for (let i = data.length - 1; i >= 0; --i) {
const e = data[i];
if ((e.job === 'control' || e.job === 'text' || e.job === 'control' || e.job === 'image') && (e.op === 'begin')) {
startIdx = i;
break;
}
}
if (startIdx >= 0) data = data.slice(startIdx);
// build timeline
const ts = [];
for (const entry of data) {
if (entry.op === 'begin') {
const start = entry.timestamp;
const end = data.find((e) => (e.id === entry.id && e.op === 'end')) || data[data.length - 1].timestamp;
ts.push({ start, end: end.timestamp, label: entry.job, type: '' });
}
}
timeline.innerHTML = '';
if (!ts.length) return;
new Timesheet(timeline, ts); // eslint-disable-line no-undef, no-new
});
});
}
+121
View File
@@ -0,0 +1,121 @@
.timesheet {
width: 100%;
height: auto;
margin: 0 auto;
}
.timesheet {
background-color: var(--sd-main-background-color);
position: relative;
}
.timesheet.color-scheme-default .bubble-default {
background-color: RGBA(252, 70, 74, 1);
}
.timesheet.color-scheme-default .bubble-lorem {
background-color: RGBA(154, 202, 39, 1);
}
.timesheet.color-scheme-default .bubble-ipsum {
background-color: RGBA(60, 182, 227, 1);
}
.timesheet.color-scheme-default .bubble-dolor {
background-color: RGBA(244, 207, 48, 1);
}
.timesheet.color-scheme-default .bubble-sit {
background-color: RGBA(169, 105, 202, 1);
}
.timesheet.color-scheme-alternative .bubble-default {
background-color: #f3552e;
}
.timesheet.color-scheme-alternative .bubble-lorem {
background-color: #88c33a;
}
.timesheet.color-scheme-alternative .bubble-ipsum {
background-color: #436ae0;
}
.timesheet.color-scheme-alternative .bubble-dolor {
background-color: #f4d234;
}
.timesheet.color-scheme-alternative .bubble-sit {
background-color: #707d86;
}
.timesheet .scale {
height: 100%;
position: absolute;
top: 0;
left: 0;
float: left;
}
.timesheet .scale section {
float: left;
text-align: center;
color: rgba(250, 250, 250, 0.8);
font-size: 13px;
line-height: 24px;
font-weight: lighter;
border-left: 1px dashed rgba(250, 250, 250, 0.2);
height: 100%}
.timesheet .data {
margin: 10px 0 10px 0;
padding: 0;
text-align: left;
list-style-type: none;
color: rgba(250, 250, 250, 0.8);
font-size: 13px;
overflow: hidden;
}
.timesheet .data li {
margin: 0 0 3px 0;
line-height: 22px;
height: 21px;
display: block;
clear: both;
position: relative;
white-space: nowrap;
}
.timesheet .data li:hover .bubble {
opacity: 1;
}
.timesheet .data li .date {
color: #b5b5b5;
font-size: 14px;
}
.timesheet .data li .label {
font-weight: lighter;
font-size: 14px;
padding-left: 5px;
line-height: 21px;
color: #979796;
white-space: nowrap;
}
.timesheet .data li .bubble {
width: 24px;
height: 7px;
display: block;
float: left;
position: relative;
top: 7px;
border-radius: 4px;
margin: 0 10px 0 0;
opacity: 0.7;
background: var(--sd-main-accent-color);
}
#timesheet-alternative {
background-color: RGBA(247, 247, 247, 1);
border-radius: 5px;
}
#timesheet-alternative section {
color: RGBA(63, 68, 72, 1);
border-left: 1px dashed RGBA(63, 68, 72, 0.2);
}
#timesheet-alternative section:first-child {
border-left: 1px dashed transparent;
}
#timesheet-alternative .date {
display: none;
}
#timesheet-alternative .bubble {
margin-right: 7px;
}
#timesheet-alternative .label {
padding-left: 0px;
color: RGBA(48, 48, 48, 1);
}
+53
View File
@@ -0,0 +1,53 @@
/* eslint max-classes-per-file: ["error", 2] */
class Bubble {
constructor(min, start, end, label, scale) {
this.min = min;
this.start = start;
this.end = end;
this.scale = scale;
this.offset = Math.round(this.scale * (this.start - this.min));
this.width = Math.round(this.scale * (this.end - this.start));
this.label = label;
this.duration = Math.round(1000 * (this.end - this.start)) / 1000;
this.title = `Job: ${this.label}\nDuration: ${this.duration}s\nStart: ${new Date(1000 * this.start).toLocaleString()}\nEnd: ${new Date(1000 * this.end).toLocaleString()}`;
}
getDateLabel() {
return Math.round(1000 * (this.end - this.start)) / 1000;
}
}
class Timesheet {
constructor(container, data) {
this.min = Math.floor(data[0].start);
this.max = Math.round(data[data.length - 1].end + 0.5);
this.data = data;
this.container = container;
const box = container.getBoundingClientRect();
const width = box.width - 140;
this.scale = width / (this.max - this.min);
// draw sections
let html = [];
for (let c = 0; c <= this.max - this.min; c++) html.push(`<section style="width: ${this.scale}px;"></section>`);
container.className = 'timesheet color-scheme-default';
container.innerHTML = `<div class="scale"">${html.join('')}</div>`;
// insert data
html = [];
for (let n = 0, m = this.data.length; n < m; n++) {
const cur = this.data[n];
const bubble = new Bubble(this.min, cur.start, cur.end, cur.label, this.scale);
const line = [
`<span title="${bubble.title}" style="margin-left: ${bubble.offset}px; width: ${bubble.width}px;" class="bubble" data-duration="${bubble.duration}"></span>`,
`<span class="date">${bubble.duration}</span> `,
`<span class="label">${bubble.label}</span>`,
].join('');
html.push(`<li>${line}</li>`);
}
this.container.innerHTML += `<ul class="data">${html.join('')}</ul>`;
}
}
window.Timesheet = Timesheet;