import logging import os import uuid from typing import Dict from fastapi import HTTPException from pydantic import ValidationError from app.configs.constants import FilePaths from app.controllers.abc import IGradeController from app.dtos.speaking import SpeakingGradeTask1And2DTO, SpeakingGradeTask3DTO from app.dtos.writing import WritingGradeTaskDTO from app.helpers import IOHelper from app.services.abc import ISpeakingService, IWritingService, IGradeService class GradeController(IGradeController): def __init__( self, grade_service: IGradeService, speaking_service: ISpeakingService, writing_service: IWritingService ): self._service = grade_service self._speaking_service = speaking_service self._writing_service = writing_service self._logger = logging.getLogger(__name__) async def grade_writing_task(self, task: int, data: WritingGradeTaskDTO): try: return await self._writing_service.grade_writing_task(task, data.question, data.answer) except Exception as e: return str(e) async def grade_speaking_task(self, task: int, data: Dict): try: if task in {1, 2}: body = SpeakingGradeTask1And2DTO(**data) return await self._grade_speaking_task_1_2(task, body.question, body.answer) else: body = SpeakingGradeTask3DTO(**data) return await self._grade_speaking_task3(body.answers) except ValidationError as e: raise HTTPException(status_code=422, detail=e.errors()) async def grading_summary(self, data: Dict): try: section_keys = ['reading', 'listening', 'writing', 'speaking', 'level'] extracted_sections = self._extract_existing_sections_from_body(data, section_keys) return await self._service.calculate_grading_summary(extracted_sections) except Exception as e: return str(e) async def _grade_speaking_task_1_2(self, task: int, question: str, answer_firebase_path: str): sound_file_name = FilePaths.AUDIO_FILES_PATH + str(uuid.uuid4()) try: IOHelper.delete_files_older_than_one_day(FilePaths.AUDIO_FILES_PATH) return await self._speaking_service.grade_speaking_task_1_and_2( task, question, answer_firebase_path, sound_file_name ) except Exception as e: os.remove(sound_file_name) return str(e), 400 async def _grade_speaking_task3(self, answers: Dict): try: IOHelper.delete_files_older_than_one_day(FilePaths.AUDIO_FILES_PATH) return await self._speaking_service.grade_speaking_task_3(answers) except Exception as e: return str(e), 400 @staticmethod def _extract_existing_sections_from_body(my_dict, keys_to_extract): if 'sections' in my_dict and isinstance(my_dict['sections'], list) and len(my_dict['sections']) > 0: return list( filter( lambda item: 'code' in item and item['code'] in keys_to_extract and 'grade' in item and 'name' in item, my_dict['sections'] ) )