Spaces:
Sleeping
Sleeping
File size: 5,181 Bytes
71c1ad2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | # app/api/v1/alerts.py
# Alert endpoints for parents and children
from fastapi import APIRouter, HTTPException, Depends
from pydantic import BaseModel
from typing import Optional
from app.core.dependencies import get_current_user
from app.db.models.user import UserDocument, UserRole
from app.db.models.alert import AlertDocument, AlertStatus
from app.observability.logging import get_logger
from datetime import datetime
logger = get_logger(__name__)
router = APIRouter(prefix="/alerts", tags=["Alerts"])
@router.get("")
async def list_alerts(
page: int = 1,
limit: int = 20,
status: Optional[str] = None,
severity: Optional[str] = None,
user: UserDocument = Depends(get_current_user),
):
"""List alerts. Parents see all their children's alerts; children see their own."""
skip = (page - 1) * limit
if user.role == UserRole.PARENT:
query = AlertDocument.find(AlertDocument.parent_id == str(user.id))
else:
query = AlertDocument.find(AlertDocument.child_id == str(user.id))
if status:
query = query.find(AlertDocument.status == AlertStatus(status))
if severity:
from app.db.models.alert import AlertSeverity
query = query.find(AlertDocument.severity == AlertSeverity(severity))
alerts = await query.sort(-AlertDocument.created_at).skip(skip).limit(limit).to_list()
return {
"success": True,
"page": page,
"limit": limit,
"alerts": [_fmt(a) for a in alerts],
}
@router.get("/{alert_id}")
async def get_alert(
alert_id: str,
user: UserDocument = Depends(get_current_user),
):
alert = await AlertDocument.get(alert_id)
if not alert:
raise HTTPException(status_code=404, detail="Alert not found")
_check_access(alert, user)
return {"success": True, "alert": _fmt(alert)}
class AcknowledgeRequest(BaseModel):
resolution_notes: Optional[str] = None
@router.post("/{alert_id}/acknowledge")
async def acknowledge_alert(
alert_id: str,
user: UserDocument = Depends(get_current_user),
):
alert = await AlertDocument.get(alert_id)
if not alert:
raise HTTPException(status_code=404, detail="Alert not found")
_check_access(alert, user)
alert.status = AlertStatus.ACKNOWLEDGED
alert.acknowledged_at = datetime.utcnow()
alert.updated_at = datetime.utcnow()
await alert.save()
return {"success": True, "alert": _fmt(alert)}
@router.post("/{alert_id}/resolve")
async def resolve_alert(
alert_id: str,
body: AcknowledgeRequest,
user: UserDocument = Depends(get_current_user),
):
if user.role != UserRole.PARENT:
raise HTTPException(status_code=403, detail="Only parents can resolve alerts")
alert = await AlertDocument.get(alert_id)
if not alert:
raise HTTPException(status_code=404, detail="Alert not found")
if alert.parent_id != str(user.id):
raise HTTPException(status_code=403, detail="Cannot resolve this alert")
alert.status = AlertStatus.RESOLVED
alert.resolved_at = datetime.utcnow()
alert.resolved_by = str(user.id)
alert.resolution_notes = body.resolution_notes
alert.updated_at = datetime.utcnow()
await alert.save()
return {"success": True, "alert": _fmt(alert)}
@router.get("/stats/summary")
async def alert_stats(user: UserDocument = Depends(get_current_user)):
if user.role != UserRole.PARENT:
raise HTTPException(status_code=403, detail="Only parents can view stats")
alerts = await AlertDocument.find(
AlertDocument.parent_id == str(user.id)
).to_list()
by_severity: dict = {}
by_status: dict = {}
by_category: dict = {}
for a in alerts:
by_severity[a.severity.value] = by_severity.get(a.severity.value, 0) + 1
by_status[a.status.value] = by_status.get(a.status.value, 0) + 1
for c in a.categories:
by_category[c] = by_category.get(c, 0) + 1
return {
"success": True,
"total": len(alerts),
"by_severity": by_severity,
"by_status": by_status,
"by_category": by_category,
}
# ββββββββββββββββββββββββββββββββββββββββββββββ
# Helpers
# ββββββββββββββββββββββββββββββββββββββββββββββ
def _check_access(alert: AlertDocument, user: UserDocument):
if alert.parent_id != str(user.id) and alert.child_id != str(user.id):
raise HTTPException(status_code=403, detail="Cannot access this alert")
def _fmt(a: AlertDocument) -> dict:
return {
"id": str(a.id),
"child_id": a.child_id,
"parent_id": a.parent_id,
"title": a.title,
"message": a.message,
"guidance": a.guidance,
"severity": a.severity.value,
"categories": a.categories,
"status": a.status.value,
"created_at": a.created_at.isoformat(),
"acknowledged_at": a.acknowledged_at.isoformat() if a.acknowledged_at else None,
"resolved_at": a.resolved_at.isoformat() if a.resolved_at else None,
}
|