feat: dashboard shows the live model version; DB stats count openmeteo_rain
CI/CD Pipeline - Northern Thailand Ping River Monitor / Test Suite (3.11) (push) Failing after 24s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Build Docker Image (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Integration Test with Services (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Deploy to Staging (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Deploy to Production (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Performance Test (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Code Quality (push) Successful in 15s
Documentation / Generate API Documentation (push) Successful in 9s
Documentation / Documentation Summary (push) Successful in 2s
Documentation / Validate Documentation (push) Failing after 7s
Documentation / Build Sphinx Documentation (push) Successful in 15s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Cleanup (push) Successful in 1s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Test Suite (3.11) (push) Failing after 24s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Build Docker Image (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Integration Test with Services (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Deploy to Staging (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Deploy to Production (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Performance Test (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Code Quality (push) Successful in 15s
Documentation / Generate API Documentation (push) Successful in 9s
Documentation / Documentation Summary (push) Successful in 2s
Documentation / Validate Documentation (push) Failing after 7s
Documentation / Build Sphinx Documentation (push) Successful in 15s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Cleanup (push) Successful in 1s
The flood-outlook subtitle now reads '... · model hgb-v3+<sha> (trained <date>)' 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.
This commit is contained in:
@@ -1283,8 +1283,13 @@
|
|||||||
grid.appendChild(cardEl);
|
grid.appendChild(cardEl);
|
||||||
});
|
});
|
||||||
const asOf = rows[0].as_of ? new Date(rows[0].as_of).toLocaleString([], { dateStyle: 'medium', timeStyle: 'short' }) : null;
|
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` +
|
$('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
|
// Only the P.1 city outlook is shown by default; the per-station
|
||||||
// grid stays collapsed behind the expander.
|
// grid stays collapsed behind the expander.
|
||||||
const expand = $('forecast-expand');
|
const expand = $('forecast-expand');
|
||||||
@@ -1311,7 +1316,10 @@
|
|||||||
$('db-total').textContent = Number(stats.total_measurements).toLocaleString();
|
$('db-total').textContent = Number(stats.total_measurements).toLocaleString();
|
||||||
if (stats.rid_measurements != null) {
|
if (stats.rid_measurements != null) {
|
||||||
const fmtNum = (value) => Number(value || 0).toLocaleString();
|
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-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)}`;
|
$('db-range').textContent = `${fmtDate(stats.first_timestamp)} – ${fmtDate(stats.last_timestamp)}`;
|
||||||
|
|||||||
+22
-2
@@ -1222,6 +1222,22 @@ async def get_database_stats():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"HII stats unavailable: {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):
|
def as_dt(value):
|
||||||
if isinstance(value, str):
|
if isinstance(value, str):
|
||||||
return datetime.fromisoformat(value)
|
return datetime.fromisoformat(value)
|
||||||
@@ -1241,11 +1257,15 @@ async def get_database_stats():
|
|||||||
last_ts = hi
|
last_ts = hi
|
||||||
|
|
||||||
return {
|
return {
|
||||||
# Whole-DB totals (RID + HII feeds); breakdown fields alongside
|
# Whole-DB totals (RID + HII + Open-Meteo); breakdown alongside
|
||||||
"total_measurements": stats["total_measurements"] + rain_n + wl_n,
|
"total_measurements": stats["total_measurements"]
|
||||||
|
+ rain_n
|
||||||
|
+ wl_n
|
||||||
|
+ openmeteo_n,
|
||||||
"rid_measurements": stats["total_measurements"],
|
"rid_measurements": stats["total_measurements"],
|
||||||
"hii_rainfall_measurements": rain_n,
|
"hii_rainfall_measurements": rain_n,
|
||||||
"hii_waterlevel_measurements": wl_n,
|
"hii_waterlevel_measurements": wl_n,
|
||||||
|
"openmeteo_rain_measurements": openmeteo_n,
|
||||||
"station_count": stats["station_count"] + hii_stations,
|
"station_count": stats["station_count"] + hii_stations,
|
||||||
"rid_station_count": stats["station_count"],
|
"rid_station_count": stats["station_count"],
|
||||||
"hii_station_count": hii_stations,
|
"hii_station_count": hii_stations,
|
||||||
|
|||||||
Reference in New Issue
Block a user