feat: rainfall + HII water-level layers on the dashboard
Documentation / Build Sphinx Documentation (push) Successful in 1m3s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Test Suite (3.11) (push) Failing after 1m4s
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
Documentation / Generate API Documentation (push) Successful in 23s
Documentation / Validate Documentation (push) Failing after 18s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Cleanup (push) Successful in 1s
Documentation / Documentation Summary (push) Successful in 3s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Code Quality (push) Failing after 11m16s
Documentation / Build Sphinx Documentation (push) Successful in 1m3s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Test Suite (3.11) (push) Failing after 1m4s
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
Documentation / Generate API Documentation (push) Successful in 23s
Documentation / Validate Documentation (push) Failing after 18s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Cleanup (push) Successful in 1s
Documentation / Documentation Summary (push) Successful in 3s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Code Quality (push) Failing after 11m16s
New endpoints GET /api/hii/rainfall/latest and /api/hii/waterlevel/latest serve the latest per-station rows from the hii_* tables. The map gains a TWA-style rain layer: circle markers binned by TMD 24-h classes (blues for light/moderate/heavy, site amber/red for very-heavy/extreme), with a legend block and show/hide toggle. The ThaiWater sensor panel now prefers the DB-backed HII feed (no API key required, 97+ stations, ThaiWater storage-percent situation colors, gauge-datum conversion via offset_msl) and falls back to the live /sensors/thaiwater passthrough.
This commit is contained in:
@@ -9,6 +9,7 @@ import secrets
|
||||
import time
|
||||
from contextlib import asynccontextmanager
|
||||
from datetime import datetime, timedelta
|
||||
from decimal import Decimal
|
||||
from threading import Lock
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
@@ -536,6 +537,73 @@ async def get_thaiwater_sensors():
|
||||
raise HTTPException(status_code=502, detail="ThaiWater API unavailable")
|
||||
|
||||
|
||||
def _hii_engine():
|
||||
"""Engine of the HII store, or None when collection is disabled."""
|
||||
collector = app_state.get("hii_collector")
|
||||
if not collector:
|
||||
return None
|
||||
store = collector.store
|
||||
if not store.engine and not store.connect():
|
||||
return None
|
||||
return store.engine
|
||||
|
||||
|
||||
def _hii_rows(sql: str, params: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
"""Run a read query against the hii_* tables, JSON-normalizing numerics."""
|
||||
engine = _hii_engine()
|
||||
if engine is None:
|
||||
return []
|
||||
from sqlalchemy import text
|
||||
|
||||
with engine.connect() as conn:
|
||||
rows = [dict(row._mapping) for row in conn.execute(text(sql), params)]
|
||||
for row in rows:
|
||||
for key, value in row.items():
|
||||
if isinstance(value, Decimal):
|
||||
row[key] = float(value)
|
||||
return rows
|
||||
|
||||
|
||||
@app.get("/api/hii/rainfall/latest")
|
||||
async def get_hii_rainfall_latest(hours: int = Query(26, ge=1, le=168)):
|
||||
"""Latest rainfall reading per HII station (Ping basin, collected hourly)."""
|
||||
increment_counter("api_requests", labels={"endpoint": "hii_rainfall"})
|
||||
sql = """
|
||||
SELECT s.id AS station_id, s.oldcode, s.name_en, s.name_th,
|
||||
s.latitude, s.longitude, s.agency,
|
||||
m.timestamp, m.rain_1h, m.rain_24h
|
||||
FROM hii_rainfall m
|
||||
JOIN hii_rain_stations s ON s.id = m.station_id
|
||||
JOIN (SELECT station_id, MAX(timestamp) AS latest_ts
|
||||
FROM hii_rainfall WHERE timestamp >= :cutoff
|
||||
GROUP BY station_id) latest
|
||||
ON latest.station_id = m.station_id AND latest.latest_ts = m.timestamp
|
||||
"""
|
||||
cutoff = datetime.now() - timedelta(hours=hours)
|
||||
return await asyncio.to_thread(_hii_rows, sql, {"cutoff": cutoff})
|
||||
|
||||
|
||||
@app.get("/api/hii/waterlevel/latest")
|
||||
async def get_hii_waterlevel_latest(hours: int = Query(26, ge=1, le=168)):
|
||||
"""Latest water-level reading per HII station (Ping basin, m MSL)."""
|
||||
increment_counter("api_requests", labels={"endpoint": "hii_waterlevel"})
|
||||
sql = """
|
||||
SELECT s.id AS station_id, s.oldcode, s.rid_code, s.name_en, s.name_th,
|
||||
s.latitude, s.longitude, s.agency, s.river_name,
|
||||
s.offset_msl, s.min_bank_msl, s.is_key_station,
|
||||
m.timestamp, m.wl_msl, m.discharge, m.storage_percent,
|
||||
m.situation_level, m.diff_wl_bank
|
||||
FROM hii_waterlevel m
|
||||
JOIN hii_wl_stations s ON s.id = m.station_id
|
||||
JOIN (SELECT station_id, MAX(timestamp) AS latest_ts
|
||||
FROM hii_waterlevel WHERE timestamp >= :cutoff
|
||||
GROUP BY station_id) latest
|
||||
ON latest.station_id = m.station_id AND latest.latest_ts = m.timestamp
|
||||
"""
|
||||
cutoff = datetime.now() - timedelta(hours=hours)
|
||||
return await asyncio.to_thread(_hii_rows, sql, {"cutoff": cutoff})
|
||||
|
||||
|
||||
@app.get("/measurements/history/{station_code}")
|
||||
async def get_postgres_history(
|
||||
station_code: str,
|
||||
|
||||
Reference in New Issue
Block a user