82 lines
2.8 KiB
Python
82 lines
2.8 KiB
Python
from fastapi import APIRouter, Depends, Query, HTTPException
|
|
from sqlalchemy import text
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from core.database import get_session
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.get("")
|
|
async def list_alarms(
|
|
site_id: str = Query(...),
|
|
state: str = Query("active", description="active | resolved | acknowledged | all"),
|
|
limit: int = Query(50, ge=1, le=200),
|
|
session: AsyncSession = Depends(get_session),
|
|
):
|
|
where = "WHERE site_id = :site_id"
|
|
if state != "all":
|
|
where += " AND state = :state"
|
|
|
|
result = await session.execute(text(f"""
|
|
SELECT id, sensor_id, site_id, room_id, rack_id,
|
|
severity, message, state, triggered_at,
|
|
acknowledged_at, resolved_at
|
|
FROM alarms
|
|
{where}
|
|
ORDER BY triggered_at DESC
|
|
LIMIT :limit
|
|
"""), {"site_id": site_id, "state": state, "limit": limit})
|
|
return [dict(r) for r in result.mappings().all()]
|
|
|
|
|
|
@router.post("/{alarm_id}/acknowledge")
|
|
async def acknowledge_alarm(
|
|
alarm_id: int,
|
|
session: AsyncSession = Depends(get_session),
|
|
):
|
|
result = await session.execute(text("""
|
|
UPDATE alarms
|
|
SET state = 'acknowledged', acknowledged_at = NOW()
|
|
WHERE id = :id AND state = 'active'
|
|
RETURNING id
|
|
"""), {"id": alarm_id})
|
|
await session.commit()
|
|
if not result.fetchone():
|
|
raise HTTPException(status_code=404, detail="Alarm not found or not active")
|
|
return {"id": alarm_id, "state": "acknowledged"}
|
|
|
|
|
|
@router.post("/{alarm_id}/resolve")
|
|
async def resolve_alarm(
|
|
alarm_id: int,
|
|
session: AsyncSession = Depends(get_session),
|
|
):
|
|
result = await session.execute(text("""
|
|
UPDATE alarms
|
|
SET state = 'resolved', resolved_at = NOW()
|
|
WHERE id = :id AND state IN ('active', 'acknowledged')
|
|
RETURNING id
|
|
"""), {"id": alarm_id})
|
|
await session.commit()
|
|
if not result.fetchone():
|
|
raise HTTPException(status_code=404, detail="Alarm not found or already resolved")
|
|
return {"id": alarm_id, "state": "resolved"}
|
|
|
|
|
|
@router.get("/stats")
|
|
async def alarm_stats(
|
|
site_id: str = Query(...),
|
|
session: AsyncSession = Depends(get_session),
|
|
):
|
|
result = await session.execute(text("""
|
|
SELECT
|
|
COUNT(*) FILTER (WHERE state = 'active') AS active,
|
|
COUNT(*) FILTER (WHERE state = 'acknowledged') AS acknowledged,
|
|
COUNT(*) FILTER (WHERE state = 'resolved') AS resolved,
|
|
COUNT(*) FILTER (WHERE state = 'active' AND severity = 'critical') AS critical,
|
|
COUNT(*) FILTER (WHERE state = 'active' AND severity = 'warning') AS warning
|
|
FROM alarms
|
|
WHERE site_id = :site_id
|
|
"""), {"site_id": site_id})
|
|
row = result.mappings().one()
|
|
return {k: int(v) for k, v in row.items()}
|