klg-asutk-app/backend/app/api/routes/stats.py
Yuriy a7da43be0e apply recommendations: security, get_db, exceptions, eslint, api-client
- session: set_tenant use bound param (SQL injection fix)
- health: text('SELECT 1'), REDIS_URL from config
- deps: re-export get_db from session, use settings.ENABLE_DEV_AUTH (default False)
- routes: all get_db from app.api.deps; conftest overrides deps.get_db
- main: register exception handlers from app.api.exceptions
- next.config: enable ESLint and TypeScript checks
- .eslintrc: drop @typescript-eslint/recommended; fix no-console (logger, ws-client, regulations)
- backend/.env.example added
- frontend: export apiFetch; dashboard, profile, settings, risks use api-client
- docs/ANALYSIS_AND_RECOMMENDATIONS.md

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-14 21:48:58 +03:00

45 lines
2.0 KiB
Python

"""Dashboard stats API — tenant-aware aggregation."""
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from sqlalchemy import func
from app.api.deps import get_current_user
from app.api.helpers import is_operator, is_authority
from app.api.deps import get_db
from app.models import Aircraft, RiskAlert, Organization, Audit
router = APIRouter(tags=["stats"])
@router.get("/stats")
def get_stats(db: Session = Depends(get_db), user=Depends(get_current_user)):
org_filter = user.organization_id if is_operator(user) else None
# Aircraft
ac_q = db.query(Aircraft)
if org_filter: ac_q = ac_q.filter(Aircraft.operator_id == org_filter)
ac_total = ac_q.count()
sm = dict(ac_q.with_entities(Aircraft.current_status, func.count(Aircraft.id)).group_by(Aircraft.current_status).all())
active = sm.get("in_service", 0) + sm.get("active", 0)
# Risks (unresolved)
rq = db.query(RiskAlert).filter(RiskAlert.is_resolved == False)
if org_filter: rq = rq.join(Aircraft).filter(Aircraft.operator_id == org_filter)
risk_total = rq.count()
rm = dict(rq.with_entities(RiskAlert.severity, func.count(RiskAlert.id)).group_by(RiskAlert.severity).all())
# Audits
aq = db.query(Audit)
if org_filter: aq = aq.join(Aircraft, Audit.aircraft_id == Aircraft.id).filter(Aircraft.operator_id == org_filter)
# Orgs
oq = db.query(Organization)
if not is_authority(user) and org_filter: oq = oq.filter(Organization.id == org_filter)
return {
"aircraft": {"total": ac_total, "active": active, "maintenance": sm.get("maintenance", 0), "storage": sm.get("storage", 0)},
"risks": {"total": risk_total, "critical": rm.get("critical", 0), "high": rm.get("high", 0), "medium": rm.get("medium", 0), "low": rm.get("low", 0)},
"audits": {"current": aq.filter(Audit.status == "in_progress").count(), "upcoming": aq.filter(Audit.status == "draft").count(), "completed": aq.filter(Audit.status == "completed").count()},
"organizations": {"total": oq.count()},
}