From 37b9ba2ba89b05532c727e621d219f208a7432e3 Mon Sep 17 00:00:00 2001 From: eol Date: Fri, 24 Jul 2026 04:43:48 -0400 Subject: [PATCH] perf(dashboard): trim logs to first+last 3 and slim top-level news MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Daily JSON files were 65% larger than necessary (877KB → 305KB on the largest day) due to two sources of bloat: 1. Logs: every incident shipped its FULL log timeline (up to 11 logs with 139+ news items for long-running incidents like Venezuela). Now trimmed to first log (genesis) + last 3 logs (recent activity). Frontend shows a ⋮ separator with a tooltip indicating how many logs were hidden. Header reads '4 of 11 log(s)' instead of '11 log(s)'. 2. Top-level news array: carried full 7-field dicts (url, body, image, outlet...) per news item, but the frontend only uses headline (search haystack) and published_date (recent activity filter) when logs exist. Now slimmed to {headline, published_date} when logs exist — the full dicts in logs[].news[] are unchanged (drawer timeline reads from there). Added logs_total field to incident dict so the frontend can detect trimming. SCHEMA_VERSION bumped to 1.5. --- dashboard/app.js | 56 +++++++++++++++++++++--------- dashboard/styles.css | 1 + scripts/generate_dashboard_data.py | 31 +++++++++++------ 3 files changed, 62 insertions(+), 26 deletions(-) diff --git a/dashboard/app.js b/dashboard/app.js index 485f4ae9..71ff6c29 100644 --- a/dashboard/app.js +++ b/dashboard/app.js @@ -781,6 +781,45 @@ function dataBranchUrl(i) { return `https://github.com/nullhack/src-disaster-awareness/tree/data/incidents/${treeId}`; } +function _logEntryHtml(log) { + return ` +
  • +
    + + + + ${log.news.length} article(s) + +
    ${esc(log.summary)}
    +
    + ${log.news.length ? `
      ${log.news.map((n) => ` +
    • ${esc(n.headline)} +
      ${fmtDate(n.published_date) || ""} · ${esc(n.outlet || "")}
    • `).join("")}
    ` : `

    No linked news.

    `} +
    +
  • `; +} + +function _timelineHtml(logs, logsTotal) { + if (!logs || !logs.length) return ""; + const total = logsTotal || logs.length; + const trimmed = total > logs.length; + const reversed = logs.slice().reverse(); + let entries; + if (trimmed && reversed.length > 1) { + const recent = reversed.slice(0, -1); + const genesis = reversed[reversed.length - 1]; + entries = recent.map(_logEntryHtml).join("") + + `
  • ` + + _logEntryHtml(genesis); + } else { + entries = reversed.map(_logEntryHtml).join(""); + } + const header = trimmed + ? `Timeline · ${logs.length} of ${total} log(s)` + : `Timeline · ${logs.length} log(s)`; + return `

    ${header}

    `; +} + function openDrawer(id) { const i = STATE.digest.incidents.find((x) => x.incident_id === id); if (!i) return; $("#drawerTitle").textContent = i.canonical_name; @@ -841,22 +880,7 @@ function openDrawer(id) { const i = STATE.digest.incidents.find((x) => x.incide ${L.url ? `` : ``}`).join("")} ` : `

    No deep links available for this incident.

    `} - ${(i.logs && i.logs.length) ? `

    Timeline · ${i.logs.length} log(s)

    -
    ` : ""} + ${_timelineHtml(i.logs, i.logs_total)} ${(i.news && i.news.length && !(i.logs && i.logs.length)) ? `

    News · ${i.news_total} linked (${i.news.length} shown)