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:
Carlos Mesquita
2024-08-30 02:35:11 +01:00
parent 3cf9fa5cba
commit f92a803d96
73 changed files with 3642 additions and 2703 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -6,3 +6,7 @@ class ITrainingController(ABC):
@abstractmethod
async def fetch_tips(self, data):
pass
@abstractmethod
async def get_training_content(self, data):
pass

View File

@@ -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):

View File

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

View File

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

View File

@@ -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"]
)

View File

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

View File

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

View File

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