system-prompts-and-models-o.../salesflow-saas/backend/app/api/v1/executive_room.py
Claude f5c5aafbb0
feat(dealix): wire all Tier-1 APIs to real database — Sprints A-G
Sprint A — Executive Room real data:
  Rewrote executive_roi_service.py (20→158 lines) to aggregate from 7 live
  services: deals (revenue/pipeline/win_rate), approval SLA (pending/warning/
  breach from _dealix_sla), connector health (IntegrationSyncState), compliance
  posture (saudi_compliance_matrix), contradictions (contradiction_engine),
  strategic deals, evidence packs.

Sprint B — Approval Center live:
  Wired approval_center.py to query real ApprovalRequest table with SLA data
  from payload["_dealix_sla"]. Approve/reject endpoints update real DB records
  with reviewed_at timestamp.

Sprint C — Saudi Compliance live:
  Wired saudi_compliance.py to call saudi_compliance_matrix service methods
  (get_matrix, get_posture, get_risk_heatmap) with real AsyncSession + tenant_id.

Sprint D — Contradiction + Evidence Pack DB:
  Wired contradiction.py and evidence_packs.py to real database via
  contradiction_engine and evidence_pack_service. All CRUD operations
  now persist to PostgreSQL with proper tenant isolation.

Sprint F — Operating Plane:
  Created CODEOWNERS file mapping sensitive paths to @VoXc2.
  Added architecture_brief.py step to CI pipeline (runs before pytest).

Sprint G — OWASP LLM:
  Added OWASP LLM Top 10 review + architecture brief validation to
  release-prep.md (steps 10-11).

https://claude.ai/code/session_01W1rJthWDkasijTdXCfxVHs
2026-04-16 13:44:35 +00:00

76 lines
3.4 KiB
Python

"""Executive Room API — unified executive decision surface with real data."""
from fastapi import APIRouter, Depends
from typing import Any, Dict
from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db
from app.services.executive_roi_service import executive_room_service
router = APIRouter(prefix="/executive-room", tags=["Executive Room"])
@router.get("/snapshot")
async def executive_snapshot(
tenant_id: str = "00000000-0000-0000-0000-000000000000",
db: AsyncSession = Depends(get_db),
) -> Dict[str, Any]:
"""Full executive room snapshot aggregated from 7 live services."""
return await executive_room_service.build_snapshot(db, tenant_id)
@router.get("/risks")
async def executive_risks(
tenant_id: str = "00000000-0000-0000-0000-000000000000",
db: AsyncSession = Depends(get_db),
) -> Dict[str, Any]:
"""Risk summary for executives."""
snapshot = await executive_room_service.build_snapshot(db, tenant_id)
risks = []
if snapshot["approvals"]["breach"] > 0:
risks.append({"type": "sla_breach", "severity": "high", "count": snapshot["approvals"]["breach"], "description_ar": "خرق SLA في الموافقات"})
if snapshot["contradictions"]["critical"] > 0:
risks.append({"type": "contradiction", "severity": "critical", "count": snapshot["contradictions"]["critical"], "description_ar": "تناقضات حرجة نشطة"})
if snapshot["compliance"]["non_compliant"] > 0:
risks.append({"type": "compliance", "severity": "high", "count": snapshot["compliance"]["non_compliant"], "description_ar": "ضوابط غير ممتثلة"})
if snapshot["connectors"]["error"] > 0:
risks.append({"type": "connector_error", "severity": "medium", "count": snapshot["connectors"]["error"], "description_ar": "موصلات معطلة"})
return {"risks": risks, "total": len(risks)}
@router.get("/decisions-pending")
async def pending_decisions(
tenant_id: str = "00000000-0000-0000-0000-000000000000",
db: AsyncSession = Depends(get_db),
) -> Dict[str, Any]:
"""Decisions requiring executive attention — high-priority approvals + critical contradictions."""
snapshot = await executive_room_service.build_snapshot(db, tenant_id)
decisions = []
if snapshot["approvals"]["pending"] > 0:
decisions.append({"type": "approval", "count": snapshot["approvals"]["pending"], "description_ar": "موافقات معلقة"})
if snapshot["contradictions"]["active"] > 0:
decisions.append({"type": "contradiction", "count": snapshot["contradictions"]["active"], "description_ar": "تناقضات تحتاج مراجعة"})
return {"decisions": decisions, "total": len(decisions)}
@router.get("/forecast-vs-actual")
async def forecast_vs_actual(
tenant_id: str = "00000000-0000-0000-0000-000000000000",
db: AsyncSession = Depends(get_db),
) -> Dict[str, Any]:
"""Forecast vs actual comparison from live data."""
snapshot = await executive_room_service.build_snapshot(db, tenant_id)
rev = snapshot["revenue"]
return {
"tracks": {
"revenue": {
"actual": rev["actual"],
"forecast": rev["forecast"],
"variance_percent": rev["variance_percent"],
},
"strategic_deals": snapshot["strategic_deals"],
},
"overall_health": "on_track" if rev["variance_percent"] >= -10 else "at_risk",
}