| 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, |
| ) |
|
|