From 811af1625b19d5d84041f59d27cab1b4edd39fd4 Mon Sep 17 00:00:00 2001 From: grabowski Date: Wed, 12 Aug 2026 17:35:23 +0700 Subject: [PATCH] feat: dashboard shows the live model version; DB stats count openmeteo_rain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The flood-outlook subtitle now reads '... · model hgb-v3+ (trained )' from the forecast rows, so the deployed model is always visible on the page. /api/stats adds openmeteo_rain_measurements (guarded — the table may not exist on older DBs) to the whole-DB total, and the Total-datapoints tile note gains a 'forecast rain' entry. --- src/static/dashboard.html | 12 ++++++++++-- src/web_api.py | 24 ++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/static/dashboard.html b/src/static/dashboard.html index bef0101..45551c3 100644 --- a/src/static/dashboard.html +++ b/src/static/dashboard.html @@ -1283,8 +1283,13 @@ grid.appendChild(cardEl); }); const asOf = rows[0].as_of ? new Date(rows[0].as_of).toLocaleString([], { dateStyle: 'medium', timeStyle: 'short' }) : null; + const modelRow = rows.find((r) => r.source === 'model'); + const trainedAt = modelRow?.trained_at ? new Date(modelRow.trained_at).toLocaleDateString([], { day: 'numeric', month: 'short' }) : null; + const modelInfo = modelRow?.model_version + ? ` · model ${modelRow.model_version}${trainedAt ? ` (trained ${trainedAt})` : ''}` + : ''; $('forecast-status').textContent = `Probability of exceeding warning / danger level within 6, 12 and 24 h` + - (asOf ? ` · based on readings up to ${asOf}` : ''); + (asOf ? ` · based on readings up to ${asOf}` : '') + modelInfo; // Only the P.1 city outlook is shown by default; the per-station // grid stays collapsed behind the expander. const expand = $('forecast-expand'); @@ -1311,7 +1316,10 @@ $('db-total').textContent = Number(stats.total_measurements).toLocaleString(); if (stats.rid_measurements != null) { const fmtNum = (value) => Number(value || 0).toLocaleString(); - $('db-total-note').textContent = `${fmtNum(stats.rid_measurements)} RID · ${fmtNum(stats.hii_waterlevel_measurements)} HII level · ${fmtNum(stats.hii_rainfall_measurements)} rain`; + const openmeteo = stats.openmeteo_rain_measurements + ? ` · ${fmtNum(stats.openmeteo_rain_measurements)} forecast rain` + : ''; + $('db-total-note').textContent = `${fmtNum(stats.rid_measurements)} RID · ${fmtNum(stats.hii_waterlevel_measurements)} HII level · ${fmtNum(stats.hii_rainfall_measurements)} rain${openmeteo}`; $('db-stations-note').textContent = `${stats.rid_station_count} RID · ${stats.hii_station_count} ThaiWater/HII`; } $('db-range').textContent = `${fmtDate(stats.first_timestamp)} – ${fmtDate(stats.last_timestamp)}`; diff --git a/src/web_api.py b/src/web_api.py index eae5dae..2060fe3 100644 --- a/src/web_api.py +++ b/src/web_api.py @@ -1222,6 +1222,22 @@ async def get_database_stats(): except Exception as e: logger.warning(f"HII stats unavailable: {e}") + openmeteo_n = 0 + try: # table appears with the 2026-08 rain work; older DBs lack it + engine = _hii_engine() + if engine is not None: + from sqlalchemy import text as _text + + with engine.connect() as conn: + openmeteo_n = int( + conn.execute( + _text("SELECT COUNT(*) FROM openmeteo_rain") + ).scalar() + or 0 + ) + except Exception: + pass + def as_dt(value): if isinstance(value, str): return datetime.fromisoformat(value) @@ -1241,11 +1257,15 @@ async def get_database_stats(): last_ts = hi return { - # Whole-DB totals (RID + HII feeds); breakdown fields alongside - "total_measurements": stats["total_measurements"] + rain_n + wl_n, + # Whole-DB totals (RID + HII + Open-Meteo); breakdown alongside + "total_measurements": stats["total_measurements"] + + rain_n + + wl_n + + openmeteo_n, "rid_measurements": stats["total_measurements"], "hii_rainfall_measurements": rain_n, "hii_waterlevel_measurements": wl_n, + "openmeteo_rain_measurements": openmeteo_n, "station_count": stats["station_count"] + hii_stations, "rid_station_count": stats["station_count"], "hii_station_count": hii_stations,