from logging import getLogger from fastapi import UploadFile from app.configs.constants import GPTModels, FieldsAndExercises, TemperatureSettings from app.dtos.reading import ReadingDTO from app.helpers import ExercisesHelper from app.services.abc import IReadingService, ILLMService from .fill_blanks import FillBlanks from .idea_match import IdeaMatch from .paragraph_match import ParagraphMatch from .true_false import TrueFalse from .import_reading import ImportReadingModule from .write_blanks import WriteBlanks class ReadingService(IReadingService): def __init__(self, llm: ILLMService): self._llm = llm self._fill_blanks = FillBlanks(llm) self._idea_match = IdeaMatch(llm) self._paragraph_match = ParagraphMatch(llm) self._true_false = TrueFalse(llm) self._write_blanks = WriteBlanks(llm) self._logger = getLogger(__name__) self._import = ImportReadingModule(llm) async def import_exam(self, exercises: UploadFile, solutions: UploadFile = None): return await self._import.import_from_file(exercises, solutions) async def generate_reading_passage(self, part: int, topic: str, word_count: int = 800): part_system_message = { "1": 'The generated text should be fairly easy to understand and have multiple paragraphs.', "2": 'The generated text should be fairly hard to understand and have multiple paragraphs.', "3": ( 'The generated text should be very hard to understand and include different points, theories, ' 'subtle differences of opinions from people, correctly sourced to the person who said it, ' 'over the specified topic and have multiple paragraphs.' ) } messages = [ { "role": "system", "content": ( 'You are a helpful assistant designed to output JSON on this format: ' '{"title": "title of the text", "text": "generated text"}') }, { "role": "user", "content": ( f'Generate an extensive text for IELTS Reading Passage {part}, of at least {word_count} words, ' f'on the topic of "{topic}". The passage should offer a substantial amount of ' 'information, analysis, or narrative relevant to the chosen subject matter. This text ' 'passage aims to serve as the primary reading section of an IELTS test, providing an ' 'in-depth and comprehensive exploration of the topic. Make sure that the generated text ' 'does not contain forbidden subjects in muslim countries.' ) }, { "role": "system", "content": part_system_message[str(part)] } ] if part == 3: messages.append({ "role": "user", "content": "Use real text excerpts on your generated passage and cite the sources." }) return await self._llm.prediction( GPTModels.GPT_4_O, messages, FieldsAndExercises.GEN_TEXT_FIELDS, TemperatureSettings.GEN_QUESTION_TEMPERATURE ) async def generate_reading_exercises(self, dto: ReadingDTO): exercises = [] start_id = 1 for req_exercise in dto.exercises: if req_exercise.type == "fillBlanks": question = await self._fill_blanks.gen_summary_fill_blanks_exercise( dto.text, req_exercise.quantity, start_id, dto.difficulty, req_exercise.num_random_words ) exercises.append(question) self._logger.info(f"Added fill blanks: {question}") elif req_exercise.type == "trueFalse": question = await self._true_false.gen_true_false_not_given_exercise( dto.text, req_exercise.quantity, start_id, dto.difficulty ) exercises.append(question) self._logger.info(f"Added trueFalse: {question}") elif req_exercise.type == "writeBlanks": question = await self._write_blanks.gen_write_blanks_exercise( dto.text, req_exercise.quantity, start_id, dto.difficulty, req_exercise.max_words ) if ExercisesHelper.answer_word_limit_ok(question): exercises.append(question) self._logger.info(f"Added write blanks: {question}") else: exercises.append({}) self._logger.info("Did not add write blanks because it did not respect word limit") elif req_exercise.type == "paragraphMatch": question = await self._paragraph_match.gen_paragraph_match_exercise( dto.text, req_exercise.quantity, start_id ) exercises.append(question) self._logger.info(f"Added paragraph match: {question}") elif req_exercise.type == "ideaMatch": question = await self._idea_match.gen_idea_match_exercise( dto.text, req_exercise.quantity, start_id ) question["variant"] = "ideaMatch" exercises.append(question) self._logger.info(f"Added idea match: {question}") start_id = start_id + req_exercise.quantity return { "exercises": exercises }