feat: downsampled DOD trail + history endpoint for live-chart backfill
So a viewer sees recent trend on open instead of a blank chart. Viewing
only — reports still use the device's FTP .rnd data.
- NL43Reading table (auto-creates; no migration): unit_id, timestamp,
lp/leq/lmax/ln1/ln2.
- Monitor stores one downsampled reading per MONITOR_TRAIL_SAMPLE_S
(default 60s) from its keepalive poll loop, pruning rows older than
MONITOR_TRAIL_RETENTION_HOURS (default 24h). ~1440 rows/unit max.
- GET /api/nl43/{unit}/history?hours=N -> the trail for the last N hours
(clamped 0.1-48h), oldest-first.
Because keepalive runs 24/7, the trail fills continuously, so the history
is there whenever someone opens the live view.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+26
-1
@@ -11,7 +11,7 @@ import os
|
||||
import asyncio
|
||||
|
||||
from app.database import get_db
|
||||
from app.models import NL43Config, NL43Status, AlertRule, AlertEvent
|
||||
from app.models import NL43Config, NL43Status, AlertRule, AlertEvent, NL43Reading
|
||||
from app.services import NL43Client, persist_snapshot
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -330,6 +330,31 @@ async def monitor_status():
|
||||
return {"status": "ok", "monitors": monitor_manager.status()}
|
||||
|
||||
|
||||
@router.get("/{unit_id}/history")
|
||||
def get_monitor_history(unit_id: str, hours: float = 2.0, db: Session = Depends(get_db)):
|
||||
"""Recent downsampled monitor readings (the DOD trail) for the live-chart
|
||||
backfill. Viewing only — NOT the FTP report data."""
|
||||
from datetime import timedelta
|
||||
hours = max(0.1, min(hours, 48.0))
|
||||
cutoff = datetime.utcnow() - timedelta(hours=hours)
|
||||
rows = (db.query(NL43Reading)
|
||||
.filter(NL43Reading.unit_id == unit_id, NL43Reading.timestamp >= cutoff)
|
||||
.order_by(NL43Reading.timestamp.asc()).all())
|
||||
return {
|
||||
"status": "ok",
|
||||
"unit_id": unit_id,
|
||||
"hours": hours,
|
||||
"count": len(rows),
|
||||
"readings": [
|
||||
{
|
||||
"timestamp": r.timestamp.isoformat() if r.timestamp else None,
|
||||
"lp": r.lp, "leq": r.leq, "lmax": r.lmax, "ln1": r.ln1, "ln2": r.ln2,
|
||||
}
|
||||
for r in rows
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# ALERTS — threshold rules + fired events
|
||||
# ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user