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 `
+
+
+
+
+ ${fmtDateTime(log.log_date) || ""}
+ ${log.news.length} article(s)
+
+ ${esc(log.summary)}
+
+ ${log.news.length ? `` : `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 ``;
+}
+
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)
-
${i.logs.slice().reverse().map((log, idx) => `
- -
-
-
-
- ${fmtDateTime(log.log_date) || ""}
- ${log.news.length} article(s)
-
- ${esc(log.summary)}
-
- ${log.news.length ? `` : `No linked news.
`}
-
- `).join("")}
` : ""}
+ ${_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)
${i.news.map((n) => `
- ${esc(n.headline)}
diff --git a/dashboard/styles.css b/dashboard/styles.css
index f1a9822a..6eddf834 100644
--- a/dashboard/styles.css
+++ b/dashboard/styles.css
@@ -583,6 +583,7 @@ code { font-family: var(--mono); font-size: .9em; }
.log-entry__date { color: var(--rc-ink); white-space: nowrap; }
.log-entry__count { color: var(--rc-muted); font-weight: 400; font-size: 11px; white-space: nowrap; }
.log-entry__summary { font-size: 13px; line-height: 1.55; color: var(--rc-slate); padding: 4px 0 0 18px; }
+.log-entry--gap { text-align: center; color: var(--rc-muted); font-size: 16px; letter-spacing: 2px; padding: 6px 0; list-style: none; cursor: default; border-bottom: 1px solid var(--rc-line); }
.keys { display: flex; flex-wrap: wrap; gap: 5px; }
.keys code {
font-size: 11px; background: var(--rc-bg); padding: 2px 7px; border-radius: 4px; color: var(--rc-slate);
diff --git a/scripts/generate_dashboard_data.py b/scripts/generate_dashboard_data.py
index 052188bd..028aa0c4 100644
--- a/scripts/generate_dashboard_data.py
+++ b/scripts/generate_dashboard_data.py
@@ -20,10 +20,11 @@
from disaster_report._country_names import country_name
from disaster_report.store.content import ContentStore
-SCHEMA_VERSION = "1.4"
+SCHEMA_VERSION = "1.5"
TRACKING_WINDOW_DAYS = 7
DEFAULT_TRACKING_WINDOW_DAYS = 7
MIN_SEVERITY = "LOW"
+MAX_RECENT_LOGS = 3
SEVERITY_RANK = {"LOW": 1, "MEDIUM": 2, "HIGH": 3, "CRITICAL": 4}
SEVERITY_NAMES = {v: k for k, v in SEVERITY_RANK.items()}
@@ -390,6 +391,9 @@ def build_incident_object(store: ContentStore, inc: dict, as_of_date: datetime)
news_count = len(news)
latest_summary = load_latest_log(store, incident_id)
logs = load_logs_for_incident(store, incident_id)
+ logs_total = len(logs)
+ if logs_total > MAX_RECENT_LOGS + 1:
+ logs = [logs[0]] + logs[-MAX_RECENT_LOGS:]
genesis = None
for r in reports:
@@ -577,15 +581,21 @@ def build_incident_object(store: ContentStore, inc: dict, as_of_date: datetime)
"gdacs_population": gdacs_pop if gdacs_pop else None,
}
- news_items = [
- {
- "headline": n["headline"] or "",
- "url": n["url"],
- "outlet": n["outlet"] or "",
- "published_date": n["published_date"],
- }
- for n in news
- ]
+ if logs:
+ news_items = [
+ {"headline": n["headline"] or "", "published_date": n["published_date"]}
+ for n in news
+ ]
+ else:
+ news_items = [
+ {
+ "headline": n["headline"] or "",
+ "url": n["url"],
+ "outlet": n["outlet"] or "",
+ "published_date": n["published_date"],
+ }
+ for n in news
+ ]
if lat is None and iso2 and iso2 in ISO2_CENTROIDS:
lat, lon = ISO2_CENTROIDS[iso2]
@@ -626,6 +636,7 @@ def build_incident_object(store: ContentStore, inc: dict, as_of_date: datetime)
"news": news_items,
"news_total": news_count,
"logs": logs,
+ "logs_total": logs_total,
}