// Run history: runs grouped into days (local time), as shown in the dashboard's job detail and in reports. import { parseDate } from './format.js'; import { STATUS, normalizeStatus } from './status.js'; // Colour of a day cell: the worst thing that happened that day export const DAY_SEVERITY = ['failed', 'warning', 'unknown', 'running', 'success']; const pad2 = (n) => String(n).padStart(2, '0'); export const localDayKey = (d) => `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`; export const dayLabelFmt = new Intl.DateTimeFormat(undefined, { weekday: 'short', month: 'short', day: 'numeric' }); export const dayTickFmt = new Intl.DateTimeFormat(undefined, { month: 'short', day: 'numeric' }); export function normalizeRun(r) { return { id: String(r.id), status: normalizeStatus(r.status), startedAt: parseDate(r.started_at), finishedAt: parseDate(r.finished_at), duration: r.duration_seconds, size: r.size_bytes, }; } export function buildDays(runs, count) { const today = new Date(); today.setHours(0, 0, 0, 0); const days = []; for (let i = count - 1; i >= 0; i--) { const date = new Date(today); date.setDate(date.getDate() - i); days.push({ key: localDayKey(date), date, counts: {}, total: 0 }); } const byKey = new Map(days.map((d) => [d.key, d])); for (const run of runs) { const day = byKey.get(localDayKey(run.startedAt)); if (!day) continue; day.counts[run.status] = (day.counts[run.status] ?? 0) + 1; day.total++; } for (const day of days) day.status = day.total ? DAY_SEVERITY.find((s) => day.counts[s]) : 'none'; return days; } export function describeCounts(counts) { return DAY_SEVERITY.filter((s) => counts[s]) .map((s) => `${counts[s]} ${STATUS[s].label.toLowerCase()}`) .join(', '); }