Spaces:
Sleeping
Sleeping
| from fastapi import APIRouter, HTTPException, status | |
| from fastapi.responses import JSONResponse | |
| from src.utils.logger import logger | |
| from pydantic import BaseModel | |
| from typing import List, Dict, Any, Optional | |
| import json | |
| import os | |
| router = APIRouter(prefix="/pronunciation", tags=["Pronunciation"]) | |
| class Level(BaseModel): | |
| id: str | |
| name: str | |
| description: str | |
| color: str | |
| class Vocabulary(BaseModel): | |
| word: str | |
| ipa: str | |
| vietnamese: str | |
| audioUrl: str | |
| class Sentence(BaseModel): | |
| text: str | |
| ipa: str | |
| vietnamese: str | |
| audioUrl: str | |
| class PronunciationLesson(BaseModel): | |
| id: str | |
| title: str | |
| description: str | |
| level: str | |
| vocabulary: List[Vocabulary] | |
| sentence: Sentence | |
| class LevelsResponse(BaseModel): | |
| levels: List[Level] | |
| total: int | |
| class LessonsResponse(BaseModel): | |
| lessons: List[PronunciationLesson] | |
| total: int | |
| level: str | |
| class LessonDetailResponse(BaseModel): | |
| lesson: PronunciationLesson | |
| def load_pronunciation_data() -> Dict[str, Any]: | |
| """Load pronunciation lessons data from JSON file""" | |
| try: | |
| data_file_path = os.path.join( | |
| os.path.dirname(__file__), "..", "..", "data", "pronunciation_lessons.json" | |
| ) | |
| if not os.path.exists(data_file_path): | |
| logger.warning(f"Pronunciation lessons file not found at {data_file_path}") | |
| return {"levels": [], "lessons": {}} | |
| with open(data_file_path, "r", encoding="utf-8") as file: | |
| data = json.load(file) | |
| return data | |
| except Exception as e: | |
| logger.error(f"Error loading pronunciation lessons data: {str(e)}") | |
| return {"levels": [], "lessons": {}} | |
| async def get_levels(): | |
| """ | |
| Get all available levels for pronunciation practice | |
| Returns: | |
| LevelsResponse: Contains list of all levels and total count | |
| """ | |
| try: | |
| data = load_pronunciation_data() | |
| levels_data = data.get("levels", []) | |
| levels = [Level(**level_data) for level_data in levels_data] | |
| return LevelsResponse(levels=levels, total=len(levels)) | |
| except Exception as e: | |
| logger.error(f"Error retrieving levels: {str(e)}") | |
| raise HTTPException( | |
| status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, | |
| detail="Failed to retrieve levels", | |
| ) | |
| async def get_lessons_by_level(level_id: str): | |
| """ | |
| Get all lessons for a specific level | |
| Args: | |
| level_id (str): The level ID (beginner, elementary, etc.) | |
| Returns: | |
| LessonsResponse: Contains list of lessons for the specified level | |
| """ | |
| try: | |
| data = load_pronunciation_data() | |
| lessons_data = data.get("lessons", {}) | |
| if level_id not in lessons_data: | |
| raise HTTPException( | |
| status_code=status.HTTP_404_NOT_FOUND, | |
| detail=f"Level '{level_id}' not found", | |
| ) | |
| level_lessons = lessons_data[level_id] | |
| lessons = [PronunciationLesson(**lesson_data) for lesson_data in level_lessons] | |
| return LessonsResponse(lessons=lessons, total=len(lessons), level=level_id) | |
| except HTTPException: | |
| raise | |
| except Exception as e: | |
| logger.error(f"Error retrieving lessons for level {level_id}: {str(e)}") | |
| raise HTTPException( | |
| status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, | |
| detail="Failed to retrieve lessons", | |
| ) | |
| async def get_lesson_detail(lesson_id: str): | |
| """ | |
| Get detailed information about a specific lesson | |
| Args: | |
| lesson_id (str): The unique identifier of the lesson | |
| Returns: | |
| LessonDetailResponse: Contains the lesson details | |
| """ | |
| try: | |
| data = load_pronunciation_data() | |
| lessons_data = data.get("lessons", {}) | |
| # Search for the lesson across all levels | |
| found_lesson = None | |
| for level_id, level_lessons in lessons_data.items(): | |
| for lesson_data in level_lessons: | |
| if lesson_data.get("id") == lesson_id: | |
| found_lesson = lesson_data | |
| break | |
| if found_lesson: | |
| break | |
| if not found_lesson: | |
| raise HTTPException( | |
| status_code=status.HTTP_404_NOT_FOUND, | |
| detail=f"Lesson with ID '{lesson_id}' not found", | |
| ) | |
| lesson = PronunciationLesson(**found_lesson) | |
| return LessonDetailResponse(lesson=lesson) | |
| except HTTPException: | |
| raise | |
| except Exception as e: | |
| logger.error(f"Error retrieving lesson {lesson_id}: {str(e)}") | |
| raise HTTPException( | |
| status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, | |
| detail="Failed to retrieve lesson", | |
| ) | |
| async def search_lessons_by_title(level_id: str, title: str): | |
| """ | |
| Search lessons by title within a specific level | |
| Args: | |
| level_id (str): The level ID to search within | |
| title (str): Part of the lesson title to search for | |
| Returns: | |
| LessonsResponse: Contains list of matching lessons | |
| """ | |
| try: | |
| data = load_pronunciation_data() | |
| lessons_data = data.get("lessons", {}) | |
| if level_id not in lessons_data: | |
| raise HTTPException( | |
| status_code=status.HTTP_404_NOT_FOUND, | |
| detail=f"Level '{level_id}' not found", | |
| ) | |
| level_lessons = lessons_data[level_id] | |
| matching_lessons = [ | |
| lesson_data for lesson_data in level_lessons | |
| if title.lower() in lesson_data.get("title", "").lower() | |
| ] | |
| lessons = [PronunciationLesson(**lesson_data) for lesson_data in matching_lessons] | |
| return LessonsResponse(lessons=lessons, total=len(lessons), level=level_id) | |
| except HTTPException: | |
| raise | |
| except Exception as e: | |
| logger.error(f"Error searching lessons by title '{title}' in level {level_id}: {str(e)}") | |
| raise HTTPException( | |
| status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, | |
| detail="Failed to search lessons", | |
| ) | |