95 lines
3.3 KiB
Python
95 lines
3.3 KiB
Python
|
|
from fastapi import APIRouter, Depends, HTTPException
|
||
|
|
|
||
|
|
from app.middleware.auth import get_current_user_id
|
||
|
|
from app.models import AnalyticsSummary
|
||
|
|
from app.services.db import get_pool
|
||
|
|
from app.services.hex_service import run_notebook
|
||
|
|
|
||
|
|
router = APIRouter(prefix="/analytics", tags=["analytics"])
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/distractions")
|
||
|
|
async def distraction_analytics(user_id: str = Depends(get_current_user_id)):
|
||
|
|
try:
|
||
|
|
return await run_notebook("distraction_patterns", user_id)
|
||
|
|
except Exception as e:
|
||
|
|
raise HTTPException(status_code=502, detail=f"Hex error: {e}")
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/focus-trends")
|
||
|
|
async def focus_trends(user_id: str = Depends(get_current_user_id)):
|
||
|
|
try:
|
||
|
|
return await run_notebook("focus_trends", user_id)
|
||
|
|
except Exception as e:
|
||
|
|
raise HTTPException(status_code=502, detail=f"Hex error: {e}")
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/weekly-report")
|
||
|
|
async def weekly_report(user_id: str = Depends(get_current_user_id)):
|
||
|
|
try:
|
||
|
|
return await run_notebook("weekly_report", user_id)
|
||
|
|
except Exception as e:
|
||
|
|
raise HTTPException(status_code=502, detail=f"Hex error: {e}")
|
||
|
|
|
||
|
|
|
||
|
|
@router.post("/refresh")
|
||
|
|
async def refresh_analytics(user_id: str = Depends(get_current_user_id)):
|
||
|
|
results = {}
|
||
|
|
for key in ("distraction_patterns", "focus_trends", "weekly_report"):
|
||
|
|
try:
|
||
|
|
results[key] = await run_notebook(key, user_id)
|
||
|
|
except Exception as e:
|
||
|
|
results[key] = {"error": str(e)}
|
||
|
|
return results
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/summary", response_model=AnalyticsSummary)
|
||
|
|
async def analytics_summary(user_id: str = Depends(get_current_user_id)):
|
||
|
|
pool = await get_pool()
|
||
|
|
|
||
|
|
# Direct Postgres queries — no Hex needed
|
||
|
|
focus_minutes = await pool.fetchval(
|
||
|
|
"""SELECT COALESCE(SUM(EXTRACT(EPOCH FROM (ended_at - started_at)) / 60), 0)
|
||
|
|
FROM sessions WHERE user_id = $1::uuid AND ended_at IS NOT NULL
|
||
|
|
AND started_at > now() - interval '7 days'""",
|
||
|
|
user_id,
|
||
|
|
)
|
||
|
|
|
||
|
|
sessions_completed = await pool.fetchval(
|
||
|
|
"""SELECT COUNT(*) FROM sessions
|
||
|
|
WHERE user_id = $1::uuid AND status = 'completed'
|
||
|
|
AND started_at > now() - interval '7 days'""",
|
||
|
|
user_id,
|
||
|
|
)
|
||
|
|
|
||
|
|
tasks_completed = await pool.fetchval(
|
||
|
|
"""SELECT COUNT(*) FROM tasks
|
||
|
|
WHERE user_id = $1::uuid AND status = 'done'
|
||
|
|
AND updated_at > now() - interval '7 days'""",
|
||
|
|
user_id,
|
||
|
|
)
|
||
|
|
|
||
|
|
top_distractors = await pool.fetch(
|
||
|
|
"""SELECT app_name, COUNT(*) as count
|
||
|
|
FROM distractions
|
||
|
|
WHERE user_id = $1::uuid AND detected_at > now() - interval '7 days'
|
||
|
|
GROUP BY app_name ORDER BY count DESC LIMIT 5""",
|
||
|
|
user_id,
|
||
|
|
)
|
||
|
|
|
||
|
|
avg_attention = await pool.fetchval(
|
||
|
|
"""SELECT AVG((checkpoint->>'attention_score')::float)
|
||
|
|
FROM sessions
|
||
|
|
WHERE user_id = $1::uuid AND checkpoint->>'attention_score' IS NOT NULL
|
||
|
|
AND started_at > now() - interval '7 days'""",
|
||
|
|
user_id,
|
||
|
|
)
|
||
|
|
|
||
|
|
return AnalyticsSummary(
|
||
|
|
total_focus_minutes=float(focus_minutes or 0),
|
||
|
|
sessions_completed=sessions_completed or 0,
|
||
|
|
tasks_completed=tasks_completed or 0,
|
||
|
|
top_distractors=[{"app_name": r["app_name"], "count": r["count"]} for r in top_distractors],
|
||
|
|
avg_attention_score=float(avg_attention) if avg_attention else None,
|
||
|
|
)
|