aee0f09db8
- Datafabrik: Dockerfile fix, agentorkestrering fungerar - Vision: Identify-modell, FAISS, OCR alla testade - API: Alla 7 integrationstester passerade - Upplösare: Entitetsupplösning verifierad
187 lines
5.1 KiB
Python
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
|
|
}
|