117 lines
4.2 KiB
Python
117 lines
4.2 KiB
Python
import logging
|
|
from typing import Dict
|
|
|
|
from fastapi import BackgroundTasks, Response, HTTPException
|
|
from fastapi.datastructures import FormData
|
|
|
|
from ielts_be.controllers import IGradeController
|
|
from ielts_be.services import IGradeService, IEvaluationService
|
|
from ielts_be.dtos.evaluation import EvaluationType
|
|
from ielts_be.dtos.speaking import GradeSpeakingItem
|
|
from ielts_be.dtos.writing import WritingGradeTaskDTO
|
|
|
|
class GradeController(IGradeController):
|
|
|
|
def __init__(
|
|
self,
|
|
grade_service: IGradeService,
|
|
evaluation_service: IEvaluationService,
|
|
):
|
|
self._service = grade_service
|
|
self._evaluation_service = evaluation_service
|
|
self._logger = logging.getLogger(__name__)
|
|
|
|
async def grade_writing_task(
|
|
self,
|
|
task: int, dto: WritingGradeTaskDTO, background_tasks: BackgroundTasks
|
|
):
|
|
if task == 1 and dto.type == "academic" and dto.attachment is None:
|
|
raise HTTPException(status_code=400, detail="Academic writing task requires a picture!")
|
|
|
|
await self._evaluation_service.create_evaluation(
|
|
dto.userId, dto.sessionId, dto.exerciseId, EvaluationType.WRITING, task
|
|
)
|
|
|
|
await self._evaluation_service.begin_evaluation(
|
|
dto.userId, dto.sessionId, task, dto.exerciseId, EvaluationType.WRITING, dto, background_tasks
|
|
)
|
|
|
|
return Response(status_code=200)
|
|
|
|
async def grade_speaking_task(self, task: int, form: FormData, background_tasks: BackgroundTasks):
|
|
answers: Dict[int, Dict] = {}
|
|
user_id = form.get("userId")
|
|
session_id = form.get("sessionId")
|
|
exercise_id = form.get("exerciseId")
|
|
|
|
if not session_id or not exercise_id:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail="Fields sessionId and exerciseId are required!"
|
|
)
|
|
|
|
for key, value in form.items():
|
|
if '_' not in key:
|
|
continue
|
|
|
|
field_name, index = key.rsplit('_', 1)
|
|
index = int(index)
|
|
|
|
if index not in answers:
|
|
answers[index] = {}
|
|
|
|
if field_name == 'question':
|
|
answers[index]['question'] = value
|
|
elif field_name == 'audio':
|
|
answers[index]['answer'] = value
|
|
|
|
for i, answer in answers.items():
|
|
if 'question' not in answer or 'answer' not in answer:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail=f"Incomplete data for answer {i}. Both question and audio required."
|
|
)
|
|
|
|
items = [
|
|
GradeSpeakingItem(
|
|
question=answers[i]['question'],
|
|
answer=answers[i]['answer']
|
|
)
|
|
for i in sorted(answers.keys())
|
|
]
|
|
|
|
ex_type = EvaluationType.SPEAKING if task == 2 else EvaluationType.SPEAKING_INTERACTIVE
|
|
|
|
await self._evaluation_service.create_evaluation(
|
|
user_id, session_id, exercise_id, ex_type, task
|
|
)
|
|
|
|
await self._evaluation_service.begin_evaluation(
|
|
user_id, session_id, task, exercise_id, ex_type, items, background_tasks
|
|
)
|
|
|
|
return Response(status_code=200)
|
|
|
|
async def grade_short_answers(self, data: Dict):
|
|
return await self._service.grade_short_answers(data)
|
|
|
|
async def grading_summary(self, data: Dict):
|
|
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)
|
|
|
|
@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']
|
|
)
|
|
)
|
|
|