chatty / app /api /endpoints /user.py
GitHub Action
Deploy to Hugging Face Space
3470cf9
Raw
History Blame Contribute Delete
4.78 kB
from __future__ import annotations
from fastapi import APIRouter, Depends, HTTPException
from app.api.auth import get_current_user_id
from app.api.schemas import (
AssessmentDetailResponse,
SpeechAggregatesResponse,
SpeechAssessmentResponse,
StatsResponse,
UserResponse,
)
from app.db.session import get_session_factory
from app.services.grammar_exercise import get_exercise_count
from app.services.pronunciation import get_error_count
from app.services.speech_assessment import (
get_assessment,
get_recent_assessments,
get_score_aggregates,
)
from app.services.stats import get_stats
from app.services.user import get_or_create_user
router = APIRouter(tags=["user"])
@router.get("/user", response_model=UserResponse)
async def get_user(user_id: int = Depends(get_current_user_id)) -> UserResponse:
"""Get current user profile."""
factory = get_session_factory()
async with factory() as s:
user = await get_or_create_user(s, user_id)
return UserResponse(
telegram_id=user.telegram_id,
level=user.level,
interface_language=user.interface_language,
created_at=user.created_at,
)
@router.get("/stats", response_model=StatsResponse)
async def get_user_stats(user_id: int = Depends(get_current_user_id)) -> StatsResponse:
"""Get learning statistics."""
factory = get_session_factory()
async with factory() as s:
user = await get_or_create_user(s, user_id)
st = await get_stats(s, user_id)
pron_count = await get_error_count(s, user_id)
gram_count = await get_exercise_count(s, user_id)
assessed = user.last_assessed_at.strftime("%d %b %Y") if user.last_assessed_at else "not yet"
return StatsResponse(
level=user.level,
last_assessed=assessed,
total_messages=st.total_messages,
total_corrections=st.total_corrections,
accuracy_pct=st.accuracy_pct,
streak_days=st.streak_days,
words_saved=st.words_saved,
pronunciation_errors=pron_count,
grammar_exercises=gram_count,
)
@router.get("/assessment/latest", response_model=SpeechAssessmentResponse | None)
async def get_latest_assessment(
user_id: int = Depends(get_current_user_id),
) -> SpeechAssessmentResponse | None:
"""Get the most recent speech assessment (6 metrics + transcript), or null."""
factory = get_session_factory()
async with factory() as s:
rows = await get_recent_assessments(s, user_id, limit=1)
if not rows:
return None
a = rows[0]
return SpeechAssessmentResponse(
accuracy=a.accuracy,
fluency=a.fluency,
prosody=a.prosody,
vocabulary=a.vocabulary,
grammar=a.grammar,
topic=a.topic,
stress_accuracy=a.stress_accuracy,
transcript=a.transcript,
created_at=a.created_at,
)
@router.get("/assessment/phrase/{assessment_id}", response_model=AssessmentDetailResponse)
async def get_phrase_analysis(
assessment_id: int,
user_id: int = Depends(get_current_user_id),
) -> AssessmentDetailResponse:
"""Full per-phrase analysis: six metrics, transcript, grammar fixes, pronunciation."""
factory = get_session_factory()
async with factory() as s:
user = await get_or_create_user(s, user_id)
a = await get_assessment(s, assessment_id, user_id)
if a is None:
raise HTTPException(status_code=404, detail="assessment not found")
return AssessmentDetailResponse(
id=a.id,
dialog_turn_id=a.dialog_turn_id,
accuracy=a.accuracy,
fluency=a.fluency,
prosody=a.prosody,
vocabulary=a.vocabulary,
grammar=a.grammar,
topic=a.topic,
stress_accuracy=a.stress_accuracy,
transcript=a.transcript,
corrections=a.corrections or [],
pronunciation_notes=a.pronunciation_notes or [],
stress_notes=a.stress_notes or [],
peaks=a.peaks,
duration_sec=a.duration_sec,
level=user.level,
created_at=a.created_at,
)
@router.get("/assessment/aggregates", response_model=SpeechAggregatesResponse)
async def get_assessment_aggregates(
user_id: int = Depends(get_current_user_id),
) -> SpeechAggregatesResponse:
"""Get averaged speech metrics over the user's recent turns."""
factory = get_session_factory()
async with factory() as s:
agg = await get_score_aggregates(s, user_id)
return SpeechAggregatesResponse(
accuracy=agg["accuracy"],
fluency=agg["fluency"],
prosody=agg["prosody"],
vocabulary=agg["vocabulary"],
grammar=agg["grammar"],
topic=agg["topic"],
stress_accuracy=agg["stress_accuracy"],
count=agg["count"] or 0,
)