feat: station search in side panel; DB stats cover HII tables
CI/CD Pipeline - Northern Thailand Ping River Monitor / Test Suite (3.11) (push) Failing after 38s
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 16s
Documentation / Validate Documentation (push) Failing after 9s
Documentation / Generate API Documentation (push) Successful in 10s
Documentation / Build Sphinx Documentation (push) Successful in 16s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Cleanup (push) Successful in 0s
Documentation / Documentation Summary (push) Successful in 2s

A search box above the station lists filters both the RID flow list and
the basin-station list by code, name (Thai included), or river, and
persists across refreshes. /api/stats now reports whole-DB totals — RID
+ hii_rainfall + hii_waterlevel counts, combined station count, and a
date range spanning all sources — with per-source breakdown fields the
stats strip shows as tile notes. Sensor section renamed to 'Additional
basin stations · ThaiWater/HII'.
This commit is contained in:
2026-08-11 16:06:39 +07:00
parent ba4710b243
commit e27d418a1a
3 changed files with 81 additions and 9 deletions
+52 -2
View File
@@ -762,11 +762,61 @@ async def get_database_stats():
if stats is None:
raise HTTPException(status_code=503, detail="Database statistics unavailable")
def hii_totals():
engine = _hii_engine()
if engine is None:
return None
from sqlalchemy import text
with engine.connect() as conn:
return conn.execute(
text(
"""
SELECT (SELECT COUNT(*) FROM hii_rainfall) AS rain_n,
(SELECT COUNT(*) FROM hii_waterlevel) AS wl_n,
(SELECT COUNT(*) FROM hii_rain_stations) AS rain_s,
(SELECT COUNT(*) FROM hii_wl_stations) AS wl_s,
(SELECT MIN(timestamp) FROM hii_rainfall) AS rain_lo,
(SELECT MAX(timestamp) FROM hii_rainfall) AS rain_hi,
(SELECT MIN(timestamp) FROM hii_waterlevel) AS wl_lo,
(SELECT MAX(timestamp) FROM hii_waterlevel) AS wl_hi
"""
)
).one()
hii = None
try:
hii = await asyncio.to_thread(hii_totals)
except Exception as e:
logger.warning(f"HII stats unavailable: {e}")
def as_dt(value):
if isinstance(value, str):
return datetime.fromisoformat(value)
return value
first_ts = stats["first_timestamp"]
last_ts = stats["last_timestamp"]
rain_n = wl_n = hii_stations = 0
if hii is not None:
rain_n, wl_n = hii.rain_n or 0, hii.wl_n or 0
hii_stations = (hii.rain_s or 0) + (hii.wl_s or 0)
for lo in (as_dt(hii.rain_lo), as_dt(hii.wl_lo)):
if lo is not None and lo < first_ts:
first_ts = lo
for hi in (as_dt(hii.rain_hi), as_dt(hii.wl_hi)):
if hi is not None and hi > last_ts:
last_ts = hi
data = {
"total_measurements": stats["total_measurements"],
"station_count": stats["station_count"],
# Whole-DB totals (RID + HII feeds); breakdown fields alongside
"total_measurements": stats["total_measurements"] + rain_n + wl_n,
"rid_measurements": stats["total_measurements"],
"hii_rainfall_measurements": rain_n,
"hii_waterlevel_measurements": wl_n,
"station_count": stats["station_count"] + hii_stations,
"rid_station_count": stats["station_count"],
"hii_station_count": hii_stations,
"first_timestamp": first_ts.isoformat(),
"last_timestamp": last_ts.isoformat(),
"days_spanned": (last_ts.date() - first_ts.date()).days + 1,