[verified] feat: add live river dashboard
Add mapped river and ThaiWater sensor layers, PostgreSQL history charts, API endpoints, and dashboard tests.
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
"""Read historical station measurements from PostgreSQL."""
|
||||
|
||||
import datetime
|
||||
import os
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from sqlalchemy import create_engine, text
|
||||
|
||||
|
||||
class PostgresHistory:
|
||||
def __init__(self, connection_string: Optional[str] = None, engine=None):
|
||||
connection_string = connection_string or os.getenv("POSTGRES_CONNECTION_STRING")
|
||||
if engine is None and not connection_string:
|
||||
raise RuntimeError("POSTGRES_CONNECTION_STRING is not configured")
|
||||
self.engine = engine or create_engine(connection_string, pool_pre_ping=True)
|
||||
|
||||
def station_history(
|
||||
self,
|
||||
station_code: str,
|
||||
start: datetime.datetime,
|
||||
end: datetime.datetime,
|
||||
limit: int = 2000,
|
||||
) -> List[Dict]:
|
||||
if not 1 <= limit <= 5000:
|
||||
raise ValueError("limit must be between 1 and 5000")
|
||||
if start >= end:
|
||||
raise ValueError("start must be before end")
|
||||
|
||||
query = text(
|
||||
"""
|
||||
SELECT m.timestamp, s.station_code, m.water_level,
|
||||
m.discharge, m.discharge_percent
|
||||
FROM water_measurements m
|
||||
JOIN stations s ON m.station_id = s.id
|
||||
WHERE s.station_code = :station_code
|
||||
AND m.timestamp >= :start_time
|
||||
AND m.timestamp <= :end_time
|
||||
ORDER BY m.timestamp ASC
|
||||
LIMIT :limit
|
||||
"""
|
||||
)
|
||||
with self.engine.connect() as connection:
|
||||
rows = connection.execute(
|
||||
query,
|
||||
{
|
||||
"station_code": station_code,
|
||||
"start_time": start,
|
||||
"end_time": end,
|
||||
"limit": limit,
|
||||
},
|
||||
)
|
||||
result = []
|
||||
for row in rows:
|
||||
timestamp = row[0]
|
||||
if isinstance(timestamp, str):
|
||||
timestamp = datetime.datetime.fromisoformat(timestamp)
|
||||
result.append(
|
||||
{
|
||||
"timestamp": timestamp,
|
||||
"station_code": row[1],
|
||||
"water_level": float(row[2]) if row[2] is not None else None,
|
||||
"discharge": float(row[3]) if row[3] is not None else None,
|
||||
"discharge_percent": float(row[4]) if row[4] is not None else None,
|
||||
}
|
||||
)
|
||||
return result
|
||||
Reference in New Issue
Block a user