From 169678e1bc82291dceb2bf9dabe60f70f290f475 Mon Sep 17 00:00:00 2001 From: Emi Griffith Date: Sun, 19 Jul 2026 21:02:40 -0700 Subject: [PATCH] Simplify the score module and page: dedup builders, consolidate metric metadata (#209) Backend (scoring.py): - Fold the parallel WEIGHTS/METRIC_LABELS/DIRECTION_WORDS/TEMP_DIR_METRICS maps into one METRICS descriptor so a scored metric is defined in one place. - Share one _build_entry between the seasonal and annual paths. - Add _freq_for so the annual entry reads the wet-day / heat-stress-day share directly instead of re-running the full precip_divergence just for it. - Drop unconsumed payload: per-q v6/pct and the overall bias. - Drop _slice's dead 'annual' branch; derive SLICES from SEASONS. Frontend (score.js): - One scoreCell() and signedPts() helper for the four score-cell sites and the two signed-points chips; reuse .section-title / .table-wrap instead of cloning. Behavior-preserving (identical scores); bumps the score cache version. --- static/score.js | 54 +++++++++++++++++++++--------------------------- static/style.css | 7 ++----- 2 files changed, 26 insertions(+), 35 deletions(-) diff --git a/static/score.js b/static/score.js index 1a7fff6..a1f9e52 100644 --- a/static/score.js +++ b/static/score.js @@ -14,7 +14,20 @@ const SEASON_NAMES = { djf: "Winter", mam: "Spring", jja: "Summer", son: "Fall" const SEASON_ORDER = ["djf", "mam", "jja", "son"]; const tintColor = (cls) => TIER_COLORS[cls] || ""; -const tintClass = (cls) => (cls ? `t-${cls}` : "t-none"); +const tintClass = (cls) => `t-${cls}`; + +// A signed-points chip, colored up (▲) or down (▼): "+3" / "-2". +const signedPts = (d, suffix = "") => + `${d >= 0 ? "+" : ""}${d}${suffix}`; + +// One tinted score cell (an em-dash when the entry has no score). `strong` bolds +// the number for an Overall row/column; the tint + hover title come from the entry. +function scoreCell(entry, strong = false) { + if (!entry || entry.score == null) return `—`; + const title = entry.label ? `${entry.label}: ${entry.grade}` : entry.grade; + const num = strong ? `${entry.score}` : `${entry.score}`; + return `${num}`; +} let selected = null; // {lat, lon} let lastData = null; // most recent /score response, for repainting on a unit switch @@ -101,7 +114,7 @@ function render(data) { // ---- hero: the overall annual score ---- scoreHead.innerHTML = `
-

Climate-shift score · ${esc(place)}

+

Climate-shift score · ${esc(place)}

${ov.score}
@@ -122,12 +135,8 @@ function render(data) { `${shortLabel(k)}`).join("")}`; const seasonRows = SEASON_ORDER.map((sk) => { const sl = s.slices[sk]; - const oc = sl.overall; - const overallCell = oc - ? `${oc.score}` - : `—`; - const cells = METRIC_ORDER.map((k) => seasonCell(sl.metrics[k])).join(""); - return `${SEASON_NAMES[sk]}${overallCell}${cells}`; + const cells = METRIC_ORDER.map((k) => scoreCell(sl.metrics[k])).join(""); + return `${SEASON_NAMES[sk]}${scoreCell(sl.overall, true)}${cells}`; }).join(""); scoreBody.innerHTML = ` @@ -135,7 +144,7 @@ function render(data) { ${wbNote}

By season

-
+
${seasonHead}${seasonRows}