File size: 2,720 Bytes
582520a | 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 | """Dashboard metrics API."""
from fastapi import APIRouter, Depends
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession
from app.db import get_db
from app.models import Conversation, Lead, UnansweredQuestion
from app.schemas import DashboardStats
router = APIRouter(prefix="/api/dashboard", tags=["dashboard"])
@router.get("/stats", response_model=DashboardStats)
async def dashboard_stats(
db: AsyncSession = Depends(get_db),
) -> DashboardStats:
"""Aggregate KPIs for the operator dashboard."""
total = await db.scalar(
select(func.count(Conversation.id))
) or 0
resolved = await db.scalar(
select(func.count(Conversation.id)).where(
Conversation.resolved_without_human.is_(True)
)
) or 0
appointments = await db.scalar(
select(func.count(Conversation.id)).where(
Conversation.appointment_booked.is_(True)
)
) or 0
handed = await db.scalar(
select(func.count(Conversation.id)).where(
Conversation.handed_off.is_(True)
)
) or 0
unanswered = await db.scalar(
select(func.count(UnansweredQuestion.id))
) or 0
leads_complete = await db.scalar(
select(func.count(Lead.id)).where(Lead.complete.is_(True))
) or 0
channel_rows = (
await db.execute(
select(
Conversation.channel,
func.count(Conversation.id),
).group_by(Conversation.channel)
)
).all()
language_rows = (
await db.execute(
select(
Conversation.language,
func.count(Conversation.id),
).group_by(Conversation.language)
)
).all()
recent = (
await db.execute(
select(UnansweredQuestion)
.order_by(UnansweredQuestion.created_at.desc())
.limit(10)
)
).scalars().all()
rate = (resolved / total * 100.0) if total else 0.0
return DashboardStats(
total_conversations=int(total),
resolved_without_human=int(resolved),
resolution_rate=round(rate, 1),
appointments_booked=int(appointments),
handed_off=int(handed),
unanswered_count=int(unanswered),
leads_complete=int(leads_complete),
by_channel={c: int(n) for c, n in channel_rows},
by_language={lang: int(n) for lang, n in language_rows},
recent_unanswered=[
{
"id": q.id,
"conversation_id": q.conversation_id,
"question": q.question,
"created_at": q.created_at.isoformat(),
}
for q in recent
],
)
|