worksheet_ai / src /skill_evaluator /api /analytics.py
SoumyajitSen94298's picture
Deploy: FastAPI backend with Docker
01757c2
Raw
History Blame Contribute Delete
4.95 kB
from fastapi import APIRouter, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func
from ..database import get_db
from ..models import TokenUsage, Student
router = APIRouter(prefix="/api/analytics", tags=["Analytics"])
# GPT-4o-mini pricing (per 1M tokens)
INPUT_COST_PER_M = 0.15
OUTPUT_COST_PER_M = 0.60
EVENT_LABELS = {
"syllabus_analysis": "Syllabus Analysis",
"test_generation": "Test Paper Generation",
"answer_evaluation": "Answer Evaluation",
"performance_analysis": "Performance Analysis",
"targeted_test_generation": "Targeted Test Generation",
}
def calc_cost(input_tokens: int, output_tokens: int) -> float:
return round(
(input_tokens / 1_000_000) * INPUT_COST_PER_M
+ (output_tokens / 1_000_000) * OUTPUT_COST_PER_M,
6,
)
@router.get("/overview")
async def get_overview(db: AsyncSession = Depends(get_db)):
result = await db.execute(select(TokenUsage).order_by(TokenUsage.created_at.desc()))
rows = result.scalars().all()
total_input = sum(r.input_tokens for r in rows)
total_output = sum(r.output_tokens for r in rows)
total_tokens = sum(r.total_tokens for r in rows)
by_event: dict[str, dict] = {}
for r in rows:
et = r.event_type
if et not in by_event:
by_event[et] = {"event_type": et, "label": EVENT_LABELS.get(et, et),
"count": 0, "input_tokens": 0, "output_tokens": 0, "total_tokens": 0}
by_event[et]["count"] += 1
by_event[et]["input_tokens"] += r.input_tokens
by_event[et]["output_tokens"] += r.output_tokens
by_event[et]["total_tokens"] += r.total_tokens
for et in by_event:
by_event[et]["cost_usd"] = calc_cost(by_event[et]["input_tokens"], by_event[et]["output_tokens"])
return {
"total_events": len(rows),
"total_input_tokens": total_input,
"total_output_tokens": total_output,
"total_tokens": total_tokens,
"estimated_cost_usd": calc_cost(total_input, total_output),
"by_event_type": sorted(by_event.values(), key=lambda x: x["total_tokens"], reverse=True),
"model": rows[0].model if rows else "β€”",
}
@router.get("/by-student")
async def get_by_student(db: AsyncSession = Depends(get_db)):
result = await db.execute(select(TokenUsage).where(TokenUsage.student_id.isnot(None)))
rows = result.scalars().all()
students_result = await db.execute(select(Student).order_by(Student.created_at.asc()))
students = students_result.scalars().all()
student_code_map = {s.id: f"STU-{i+1:03d}" for i, s in enumerate(students)}
by_student: dict[str, dict] = {}
for r in rows:
sid = r.student_id
if sid not in by_student:
by_student[sid] = {
"student_id": sid,
"student_name": r.student_name or "Unknown",
"student_code": student_code_map.get(sid, "β€”"),
"total_tokens": 0,
"input_tokens": 0,
"output_tokens": 0,
"event_count": 0,
"by_event": {},
}
by_student[sid]["total_tokens"] += r.total_tokens
by_student[sid]["input_tokens"] += r.input_tokens
by_student[sid]["output_tokens"] += r.output_tokens
by_student[sid]["event_count"] += 1
et = r.event_type
by_student[sid]["by_event"][et] = by_student[sid]["by_event"].get(et, 0) + r.total_tokens
for sid in by_student:
by_student[sid]["cost_usd"] = calc_cost(
by_student[sid]["input_tokens"], by_student[sid]["output_tokens"]
)
return sorted(by_student.values(), key=lambda x: x["total_tokens"], reverse=True)
@router.get("/events")
async def get_events(limit: int = 100, db: AsyncSession = Depends(get_db)):
result = await db.execute(
select(TokenUsage).order_by(TokenUsage.created_at.desc()).limit(limit)
)
rows = result.scalars().all()
students_result = await db.execute(select(Student).order_by(Student.created_at.asc()))
students = students_result.scalars().all()
student_code_map = {s.id: f"STU-{i+1:03d}" for i, s in enumerate(students)}
return [
{
"id": r.id,
"event_type": r.event_type,
"label": EVENT_LABELS.get(r.event_type, r.event_type),
"student_name": r.student_name,
"student_code": student_code_map.get(r.student_id, "β€”") if r.student_id else "β€”",
"subject": r.subject,
"grade": r.grade,
"input_tokens": r.input_tokens,
"output_tokens": r.output_tokens,
"total_tokens": r.total_tokens,
"cost_usd": calc_cost(r.input_tokens, r.output_tokens),
"model": r.model,
"extra_info": r.extra_info,
"created_at": r.created_at.isoformat(),
}
for r in rows
]