Updated this to the latest version of develop, got rid of most of the duplication, might be missing some packages in toml, needs testing
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Dict
|
||||
from typing import Dict, List
|
||||
|
||||
|
||||
class IGradeController(ABC):
|
||||
@@ -9,18 +9,14 @@ class IGradeController(ABC):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def grade_speaking_task(self, task: int, data: Dict):
|
||||
async def grade_speaking_task(self, task: int, answers: List[Dict]) -> Dict:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def grade_short_answers(self, data: Dict):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def grading_summary(self, data: Dict):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def _grade_speaking_task_1_2(self, task: int, question: str, answer_firebase_path: str):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def _grade_speaking_task3(self, answers: Dict):
|
||||
pass
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
from fastapi import UploadFile
|
||||
from typing import Dict
|
||||
|
||||
|
||||
class ILevelController(ABC):
|
||||
|
||||
@@ -10,3 +13,11 @@ class ILevelController(ABC):
|
||||
@abstractmethod
|
||||
async def get_level_utas(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def upload_level(self, file: UploadFile):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_custom_level(self, data: Dict):
|
||||
pass
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import BackgroundTasks
|
||||
|
||||
|
||||
class ISpeakingController(ABC):
|
||||
|
||||
@abstractmethod
|
||||
async def get_speaking_task(self, task: int, topic: str, difficulty: str):
|
||||
async def get_speaking_part(self, task: int, topic: str, difficulty: str, second_topic: Optional[str] = None):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
@@ -13,9 +15,11 @@ class ISpeakingController(ABC):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def generate_speaking_video(self, data):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def generate_interactive_video(self, data):
|
||||
async def generate_video(
|
||||
self, part: int, avatar: str, topic: str, questions: list[str],
|
||||
*,
|
||||
second_topic: Optional[str] = None,
|
||||
prompts: Optional[list[str]] = None,
|
||||
suffix: Optional[str] = None,
|
||||
):
|
||||
pass
|
||||
|
||||
@@ -6,3 +6,7 @@ class ITrainingController(ABC):
|
||||
@abstractmethod
|
||||
async def fetch_tips(self, data):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_training_content(self, data):
|
||||
pass
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
import logging
|
||||
import os
|
||||
import uuid
|
||||
from typing import Dict
|
||||
|
||||
from fastapi import HTTPException
|
||||
from pydantic import ValidationError
|
||||
from typing import Dict, List
|
||||
|
||||
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.helpers import FileHelper
|
||||
from app.services.abc import ISpeakingService, IWritingService, IGradeService
|
||||
from app.utils import handle_exception
|
||||
|
||||
|
||||
class GradeController(IGradeController):
|
||||
@@ -28,47 +23,20 @@ class GradeController(IGradeController):
|
||||
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)
|
||||
return await self._writing_service.grade_writing_task(task, data.question, data.answer)
|
||||
|
||||
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())
|
||||
@handle_exception(400)
|
||||
async def grade_speaking_task(self, task: int, answers: List[Dict]) -> Dict:
|
||||
FileHelper.delete_files_older_than_one_day(FilePaths.AUDIO_FILES_PATH)
|
||||
return await self._speaking_service.grade_speaking_task(task, answers)
|
||||
|
||||
async def grade_short_answers(self, data: Dict):
|
||||
return await self._service.grade_short_answers(data)
|
||||
|
||||
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
|
||||
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):
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
from fastapi import UploadFile
|
||||
from typing import Dict
|
||||
|
||||
from app.controllers.abc import ILevelController
|
||||
from app.services.abc import ILevelService
|
||||
|
||||
@@ -8,13 +11,13 @@ class LevelController(ILevelController):
|
||||
self._service = level_service
|
||||
|
||||
async def get_level_exam(self):
|
||||
try:
|
||||
return await self._service.get_level_exam()
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
return await self._service.get_level_exam()
|
||||
|
||||
async def get_level_utas(self):
|
||||
try:
|
||||
return await self._service.get_level_utas()
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
return await self._service.get_level_utas()
|
||||
|
||||
async def upload_level(self, file: UploadFile):
|
||||
return await self._service.upload_level(file)
|
||||
|
||||
async def get_custom_level(self, data: Dict):
|
||||
return await self._service.get_custom_level(data)
|
||||
|
||||
@@ -1,97 +1,19 @@
|
||||
import random
|
||||
import logging
|
||||
from typing import List
|
||||
|
||||
from app.controllers.abc import IListeningController
|
||||
from app.dtos import SaveListeningDTO
|
||||
from app.dtos.listening import SaveListeningDTO
|
||||
from app.services.abc import IListeningService
|
||||
from app.helpers import IOHelper, ExercisesHelper
|
||||
from app.configs.constants import (
|
||||
FilePaths, EducationalContent, FieldsAndExercises
|
||||
)
|
||||
|
||||
|
||||
class ListeningController(IListeningController):
|
||||
|
||||
def __init__(self, listening_service: IListeningService):
|
||||
self._service = listening_service
|
||||
self._logger = logging.getLogger(__name__)
|
||||
self._sections = {
|
||||
"section_1": {
|
||||
"topic": EducationalContent.TWO_PEOPLE_SCENARIOS,
|
||||
"exercise_sample_size": 1,
|
||||
"total_exercises": FieldsAndExercises.TOTAL_LISTENING_SECTION_1_EXERCISES,
|
||||
"type": "conversation",
|
||||
"start_id": 1
|
||||
},
|
||||
"section_2": {
|
||||
"topic": EducationalContent.SOCIAL_MONOLOGUE_CONTEXTS,
|
||||
"exercise_sample_size": 2,
|
||||
"total_exercises": FieldsAndExercises.TOTAL_LISTENING_SECTION_2_EXERCISES,
|
||||
"type": "monologue",
|
||||
"start_id": 11
|
||||
},
|
||||
"section_3": {
|
||||
"topic": EducationalContent.FOUR_PEOPLE_SCENARIOS,
|
||||
"exercise_sample_size": 1,
|
||||
"total_exercises": FieldsAndExercises.TOTAL_LISTENING_SECTION_3_EXERCISES,
|
||||
"type": "conversation",
|
||||
"start_id": 21
|
||||
},
|
||||
"section_4": {
|
||||
"topic": EducationalContent.ACADEMIC_SUBJECTS,
|
||||
"exercise_sample_size": 2,
|
||||
"total_exercises": FieldsAndExercises.TOTAL_LISTENING_SECTION_4_EXERCISES,
|
||||
"type": "monologue",
|
||||
"start_id": 31
|
||||
}
|
||||
}
|
||||
|
||||
async def get_listening_question(self, section_id: int, topic: str, req_exercises: List[str], difficulty: str):
|
||||
try:
|
||||
IOHelper.delete_files_older_than_one_day(FilePaths.AUDIO_FILES_PATH)
|
||||
section = self._sections[f"section_{str(section_id)}"]
|
||||
if not topic:
|
||||
topic = random.choice(section["topic"])
|
||||
|
||||
if len(req_exercises) == 0:
|
||||
req_exercises = random.sample(FieldsAndExercises.LISTENING_EXERCISE_TYPES, section["exercise_sample_size"])
|
||||
|
||||
number_of_exercises_q = ExercisesHelper.divide_number_into_parts(section["total_exercises"], len(req_exercises))
|
||||
|
||||
dialog = await self._service.generate_listening_question(section_id, topic)
|
||||
|
||||
if section_id in {1, 3}:
|
||||
dialog = self.parse_conversation(dialog)
|
||||
|
||||
self._logger.info(f'Generated {section["type"]}: {str(dialog)}')
|
||||
|
||||
exercises = await self._service.generate_listening_exercises(
|
||||
section_id, str(dialog), req_exercises, number_of_exercises_q, section["start_id"], difficulty
|
||||
)
|
||||
|
||||
return {
|
||||
"exercises": exercises,
|
||||
"text": dialog,
|
||||
"difficulty": difficulty
|
||||
}
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
async def get_listening_question(
|
||||
self, section_id: int, topic: str, req_exercises: List[str], difficulty: str
|
||||
):
|
||||
return await self._service.get_listening_question(section_id, topic, req_exercises, difficulty)
|
||||
|
||||
async def save_listening(self, data: SaveListeningDTO):
|
||||
try:
|
||||
return await self._service.save_listening(data.parts, data.minTimer, data.difficulty)
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
@staticmethod
|
||||
def parse_conversation(conversation_data):
|
||||
conversation_list = conversation_data.get('conversation', [])
|
||||
readable_text = []
|
||||
|
||||
for message in conversation_list:
|
||||
name = message.get('name', 'Unknown')
|
||||
text = message.get('text', '')
|
||||
readable_text.append(f"{name}: {text}")
|
||||
|
||||
return "\n".join(readable_text)
|
||||
return await self._service.save_listening(data.parts, data.minTimer, data.difficulty, data.id)
|
||||
|
||||
@@ -15,29 +15,29 @@ class ReadingController(IReadingController):
|
||||
self._logger = logging.getLogger(__name__)
|
||||
self._passages = {
|
||||
"passage_1": {
|
||||
"start_id": 1,
|
||||
"total_exercises": FieldsAndExercises.TOTAL_READING_PASSAGE_1_EXERCISES
|
||||
},
|
||||
"passage_2": {
|
||||
"start_id": 14,
|
||||
"total_exercises": FieldsAndExercises.TOTAL_READING_PASSAGE_2_EXERCISES
|
||||
},
|
||||
"passage_3": {
|
||||
"start_id": 27,
|
||||
"total_exercises": FieldsAndExercises.TOTAL_READING_PASSAGE_3_EXERCISES
|
||||
}
|
||||
}
|
||||
|
||||
async def get_reading_passage(self, passage_id: int, topic: str, req_exercises: List[str], difficulty: str):
|
||||
try:
|
||||
passage = self._passages[f'passage_{str(passage_id)}']
|
||||
passage = self._passages[f'passage_{str(passage_id)}']
|
||||
|
||||
if len(req_exercises) == 0:
|
||||
req_exercises = random.sample(FieldsAndExercises.READING_EXERCISE_TYPES, 2)
|
||||
if len(req_exercises) == 0:
|
||||
req_exercises = random.sample(FieldsAndExercises.READING_EXERCISE_TYPES, 2)
|
||||
|
||||
number_of_exercises_q = ExercisesHelper.divide_number_into_parts(
|
||||
passage["total_exercises"], len(req_exercises)
|
||||
)
|
||||
number_of_exercises_q = ExercisesHelper.divide_number_into_parts(
|
||||
passage["total_exercises"], len(req_exercises)
|
||||
)
|
||||
|
||||
return await self._service.gen_reading_passage(
|
||||
passage_id, topic, req_exercises, number_of_exercises_q, difficulty
|
||||
)
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
return await self._service.gen_reading_passage(
|
||||
passage_id, topic, req_exercises, number_of_exercises_q, difficulty, passage["start_id"]
|
||||
)
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import logging
|
||||
import uuid
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import BackgroundTasks
|
||||
|
||||
from app.controllers.abc import ISpeakingController
|
||||
from app.dtos import (
|
||||
SaveSpeakingDTO, SpeakingGenerateVideoDTO,
|
||||
SpeakingGenerateInteractiveVideoDTO
|
||||
)
|
||||
from app.dtos.speaking import SaveSpeakingDTO
|
||||
|
||||
from app.services.abc import ISpeakingService
|
||||
from app.configs.constants import ExamVariant, MinTimers
|
||||
from app.configs.question_templates import getSpeakingTemplate
|
||||
@@ -19,45 +18,30 @@ class SpeakingController(ISpeakingController):
|
||||
self._service = speaking_service
|
||||
self._logger = logging.getLogger(__name__)
|
||||
|
||||
async def get_speaking_task(self, task: int, topic: str, difficulty: str):
|
||||
try:
|
||||
return await self._service.get_speaking_task(task, topic, difficulty)
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
async def get_speaking_part(self, task: int, topic: str, difficulty: str, second_topic: Optional[str] = None):
|
||||
return await self._service.get_speaking_part(task, topic, difficulty, second_topic)
|
||||
|
||||
async def save_speaking(self, data: SaveSpeakingDTO, background_tasks: BackgroundTasks):
|
||||
try:
|
||||
exercises = data.exercises
|
||||
min_timer = data.minTimer
|
||||
exercises = data.exercises
|
||||
min_timer = data.minTimer
|
||||
|
||||
template = getSpeakingTemplate()
|
||||
template["minTimer"] = min_timer
|
||||
template = getSpeakingTemplate()
|
||||
template["minTimer"] = min_timer
|
||||
|
||||
if min_timer < MinTimers.SPEAKING_MIN_TIMER_DEFAULT:
|
||||
template["variant"] = ExamVariant.PARTIAL.value
|
||||
else:
|
||||
template["variant"] = ExamVariant.FULL.value
|
||||
if min_timer < MinTimers.SPEAKING_MIN_TIMER_DEFAULT:
|
||||
template["variant"] = ExamVariant.PARTIAL.value
|
||||
else:
|
||||
template["variant"] = ExamVariant.FULL.value
|
||||
|
||||
req_id = str(uuid.uuid4())
|
||||
self._logger.info(f'Received request to save speaking with id: {req_id}')
|
||||
req_id = str(uuid.uuid4())
|
||||
self._logger.info(f'Received request to save speaking with id: {req_id}')
|
||||
|
||||
background_tasks.add_task(self._service.create_videos_and_save_to_db, exercises, template, req_id)
|
||||
background_tasks.add_task(self._service.create_videos_and_save_to_db, exercises, template, req_id)
|
||||
|
||||
self._logger.info('Started background task to save speaking.')
|
||||
self._logger.info('Started background task to save speaking.')
|
||||
|
||||
# Return response without waiting for create_videos_and_save_to_db to finish
|
||||
return {**template, "id": req_id}
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
# Return response without waiting for create_videos_and_save_to_db to finish
|
||||
return {**template, "id": req_id}
|
||||
|
||||
async def generate_speaking_video(self, data: SpeakingGenerateVideoDTO):
|
||||
try:
|
||||
return await self._service.generate_speaking_video(data.question, data.topic, data.avatar, data.prompts)
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
async def generate_interactive_video(self, data: SpeakingGenerateInteractiveVideoDTO):
|
||||
try:
|
||||
return await self._service.generate_interactive_video(data.questions, data.topic, data.avatar)
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
async def generate_video(self, *args, **kwargs):
|
||||
return await self._service.generate_video(*args, **kwargs)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from typing import Dict
|
||||
|
||||
from app.controllers.abc import ITrainingController
|
||||
from app.dtos import TipsDTO
|
||||
from app.dtos.training import FetchTipsDTO
|
||||
from app.services.abc import ITrainingService
|
||||
|
||||
|
||||
@@ -8,8 +10,8 @@ class TrainingController(ITrainingController):
|
||||
def __init__(self, training_service: ITrainingService):
|
||||
self._service = training_service
|
||||
|
||||
async def fetch_tips(self, data: TipsDTO):
|
||||
try:
|
||||
return await self._service.fetch_tips(data.context, data.question, data.answer, data.correct_answer)
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
async def fetch_tips(self, data: FetchTipsDTO):
|
||||
return await self._service.fetch_tips(data.context, data.question, data.answer, data.correct_answer)
|
||||
|
||||
async def get_training_content(self, data: Dict):
|
||||
return await self._service.get_training_content(data)
|
||||
|
||||
@@ -8,7 +8,4 @@ class WritingController(IWritingController):
|
||||
self._service = writing_service
|
||||
|
||||
async def get_writing_task_general_question(self, task: int, topic: str, difficulty: str):
|
||||
try:
|
||||
return await self._service.get_writing_task_general_question(task, topic, difficulty)
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
return await self._service.get_writing_task_general_question(task, topic, difficulty)
|
||||
|
||||
Reference in New Issue
Block a user