Files
boc/landvex-admin-backend/app/routers/live_data.py
T
Bernt aee0f09db8 landvex: Fixar och tester klara för alla komponenter
- Datafabrik: Dockerfile fix, agentorkestrering fungerar
- Vision: Identify-modell, FAISS, OCR alla testade
- API: Alla 7 integrationstester passerade
- Upplösare: Entitetsupplösning verifierad
2026-07-05 06:41:32 +00:00

187 lines
5.1 KiB
Python

"""
LIFE Live Data API
Exponerar realtidsdata från multi-source pipeline
"""
from fastapi import APIRouter
import sqlite3
from datetime import datetime, timedelta
router = APIRouter(prefix="/live", tags=["live_data"])
DB_PATH = "/home/bernt/.openclaw/workspace/rivp-pilot-1/rivp.db"
def get_db():
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row
return conn
@router.get("/public/status")
async def get_live_status():
"""Get current system status"""
conn = get_db()
c = conn.cursor()
# Total counts
c.execute("SELECT COUNT(*) FROM roads")
total_roads = c.fetchone()[0]
c.execute("SELECT COUNT(*) FROM observations")
total_obs = c.fetchone()[0]
# Today's observations
c.execute("SELECT COUNT(*) FROM observations WHERE detected_date = date('now')")
today_obs = c.fetchone()[0]
# By source
c.execute("SELECT source, COUNT(*) as count FROM observations GROUP BY source ORDER BY count DESC")
sources = {row[0]: row[1] for row in c.fetchall()}
# Recent activity (last hour)
c.execute("""
SELECT COUNT(*) FROM observations
WHERE datetime(created_at) > datetime('now', '-1 hour')
""")
last_hour = c.fetchone()[0]
# Reality Latency calculation
c.execute("""
SELECT AVG(
julianday('now') - julianday(detected_date)
) * 24 as avg_latency_hours
FROM observations
WHERE detected_date >= date('now', '-7 days')
""")
avg_latency = c.fetchone()[0] or 0
conn.close()
return {
"timestamp": datetime.now().isoformat(),
"system_status": "operational",
"data_freshness": {
"total_roads": total_roads,
"total_observations": total_obs,
"observations_today": today_obs,
"observations_last_hour": last_hour,
"reality_latency_hours": round(avg_latency, 1)
},
"sources": sources,
"pipeline_status": {
"trafikverket": "active",
"smhi": "active",
"quixzoom": "active",
"satellite": "active"
}
}
@router.get("/public/observations/stream")
async def get_observation_stream(limit: int = 50):
"""Get recent observations as a stream"""
conn = get_db()
c = conn.cursor()
c.execute("""
SELECT o.*, r.name as road_name, r.county, r.road_number
FROM observations o
JOIN roads r ON o.road_id = r.id
ORDER BY o.created_at DESC
LIMIT ?
""", (limit,))
observations = [dict(row) for row in c.fetchall()]
conn.close()
return {
"count": len(observations),
"observations": observations
}
@router.get("/public/alerts")
async def get_active_alerts():
"""Get active alerts (critical/high severity)"""
conn = get_db()
c = conn.cursor()
c.execute("""
SELECT o.*, r.name as road_name, r.county
FROM observations o
JOIN roads r ON o.road_id = r.id
WHERE o.severity IN ('critical', 'high')
AND o.detected_date >= date('now', '-7 days')
ORDER BY
CASE o.severity
WHEN 'critical' THEN 1
WHEN 'high' THEN 2
ELSE 3
END,
o.detected_date DESC
""")
alerts = [dict(row) for row in c.fetchall()]
conn.close()
return {
"alert_count": len(alerts),
"critical_count": sum(1 for a in alerts if a.get('severity') == 'critical'),
"high_count": sum(1 for a in alerts if a.get('severity') == 'high'),
"alerts": alerts
}
@router.get("/public/coverage")
async def get_coverage_map():
"""Get coverage data for map visualization"""
conn = get_db()
c = conn.cursor()
# Get all observations with coordinates
c.execute("""
SELECT o.latitude, o.longitude, o.observation_type, o.severity, o.confidence,
r.name as road_name, r.county
FROM observations o
JOIN roads r ON o.road_id = r.id
WHERE o.latitude != 0 AND o.longitude != 0
ORDER BY o.detected_date DESC
LIMIT 500
""")
points = []
for row in c.fetchall():
points.append({
"lat": row[0],
"lon": row[1],
"type": row[2],
"severity": row[3],
"confidence": row[4],
"road": row[5],
"county": row[6]
})
# Coverage by county
c.execute("""
SELECT r.county,
COUNT(DISTINCT r.id) as roads,
COUNT(o.id) as observations,
AVG(o.confidence) as avg_confidence
FROM roads r
LEFT JOIN observations o ON r.id = o.road_id
GROUP BY r.county
ORDER BY observations DESC
""")
coverage = []
for row in c.fetchall():
coverage.append({
"county": row[0],
"roads": row[1],
"observations": row[2],
"avg_confidence": round(row[3] or 0, 2)
})
conn.close()
return {
"total_points": len(points),
"points": points,
"coverage_by_county": coverage
}