Fixed level issues
This commit is contained in:
@@ -18,6 +18,7 @@ async def generate_exercises(
|
|||||||
dto: LevelExercisesDTO,
|
dto: LevelExercisesDTO,
|
||||||
level_controller: ILevelController = Depends(Provide[controller])
|
level_controller: ILevelController = Depends(Provide[controller])
|
||||||
):
|
):
|
||||||
|
print(dto.dict())
|
||||||
return await level_controller.generate_exercises(dto)
|
return await level_controller.generate_exercises(dto)
|
||||||
|
|
||||||
@level_router.get(
|
@level_router.get(
|
||||||
@@ -47,11 +48,12 @@ async def get_level_utas(
|
|||||||
dependencies=[Depends(Authorized([IsAuthenticatedViaBearerToken]))]
|
dependencies=[Depends(Authorized([IsAuthenticatedViaBearerToken]))]
|
||||||
)
|
)
|
||||||
@inject
|
@inject
|
||||||
async def upload(
|
async def import_level(
|
||||||
file: UploadFile,
|
exercises: UploadFile,
|
||||||
|
solutions: UploadFile = None,
|
||||||
level_controller: ILevelController = Depends(Provide[controller])
|
level_controller: ILevelController = Depends(Provide[controller])
|
||||||
):
|
):
|
||||||
return await level_controller.upload_level(file)
|
return await level_controller.upload_level(exercises, solutions)
|
||||||
|
|
||||||
|
|
||||||
@level_router.post(
|
@level_router.post(
|
||||||
|
|||||||
@@ -35,11 +35,11 @@ async def generate_video(
|
|||||||
dependencies=[Depends(Authorized([IsAuthenticatedViaBearerToken]))]
|
dependencies=[Depends(Authorized([IsAuthenticatedViaBearerToken]))]
|
||||||
)
|
)
|
||||||
@inject
|
@inject
|
||||||
async def pool_video(
|
async def poll_video(
|
||||||
vid_id: str = Path(...),
|
vid_id: str = Path(...),
|
||||||
speaking_controller: ISpeakingController = Depends(Provide[controller])
|
speaking_controller: ISpeakingController = Depends(Provide[controller])
|
||||||
):
|
):
|
||||||
return await speaking_controller.pool_video(vid_id)
|
return await speaking_controller.poll_video(vid_id)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
from fastapi import UploadFile
|
from fastapi import UploadFile
|
||||||
from typing import Dict
|
from typing import Dict, Optional
|
||||||
|
|
||||||
|
|
||||||
class ILevelController(ABC):
|
class ILevelController(ABC):
|
||||||
@@ -19,7 +19,7 @@ class ILevelController(ABC):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def upload_level(self, file: UploadFile):
|
async def upload_level(self, file: UploadFile, solutions: Optional[UploadFile] = None):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
|
|||||||
@@ -19,5 +19,5 @@ class ISpeakingController(ABC):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def pool_video(self, vid_id: str):
|
async def poll_video(self, vid_id: str):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from fastapi import UploadFile
|
from fastapi import UploadFile
|
||||||
from typing import Dict
|
from typing import Dict, Optional
|
||||||
|
|
||||||
from watchfiles import awatch
|
from watchfiles import awatch
|
||||||
|
|
||||||
@@ -21,8 +21,8 @@ class LevelController(ILevelController):
|
|||||||
async def get_level_utas(self):
|
async def get_level_utas(self):
|
||||||
return await self._service.get_level_utas()
|
return await self._service.get_level_utas()
|
||||||
|
|
||||||
async def upload_level(self, file: UploadFile):
|
async def upload_level(self, exercises: UploadFile, solutions: Optional[UploadFile] = None):
|
||||||
return await self._service.upload_level(file)
|
return await self._service.upload_level(exercises, solutions)
|
||||||
|
|
||||||
async def get_custom_level(self, data: Dict):
|
async def get_custom_level(self, data: Dict):
|
||||||
return await self._service.get_custom_level(data)
|
return await self._service.get_custom_level(data)
|
||||||
|
|||||||
@@ -22,5 +22,5 @@ class SpeakingController(ISpeakingController):
|
|||||||
async def generate_video(self, text: str, avatar: str):
|
async def generate_video(self, text: str, avatar: str):
|
||||||
return await self._vid_gen.create_video(text, avatar)
|
return await self._vid_gen.create_video(text, avatar)
|
||||||
|
|
||||||
async def pool_video(self, vid_id: str):
|
async def poll_video(self, vid_id: str):
|
||||||
return await self._vid_gen.pool_status(vid_id)
|
return await self._vid_gen.poll_status(vid_id)
|
||||||
|
|||||||
@@ -8,12 +8,11 @@ from app.configs.constants import LevelExerciseType
|
|||||||
class LevelExercises(BaseModel):
|
class LevelExercises(BaseModel):
|
||||||
type: LevelExerciseType
|
type: LevelExerciseType
|
||||||
quantity: int
|
quantity: int
|
||||||
text_size: Optional[int]
|
text_size: Optional[int] = None
|
||||||
sa_qty: Optional[int]
|
sa_qty: Optional[int] = None
|
||||||
mc_qty: Optional[int]
|
mc_qty: Optional[int] = None
|
||||||
topic: Optional[str]
|
topic: Optional[str] = None
|
||||||
|
|
||||||
class LevelExercisesDTO(BaseModel):
|
class LevelExercisesDTO(BaseModel):
|
||||||
text: str
|
|
||||||
exercises: List[LevelExercises]
|
exercises: List[LevelExercises]
|
||||||
difficulty: Optional[str]
|
difficulty: Optional[str] = None
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class ILevelService(ABC):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def upload_level(self, upload: UploadFile) -> Dict:
|
async def upload_level(self, upload: UploadFile, solutions: Optional[UploadFile] = None) -> Dict:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
|
|||||||
@@ -18,5 +18,5 @@ class IVideoGeneratorService(ABC):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def pool_status(self, video_id: str):
|
async def poll_status(self, video_id: str):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
|
from asyncio import gather
|
||||||
from typing import Dict, Optional
|
from typing import Dict, Optional
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
from fastapi import UploadFile
|
from fastapi import UploadFile
|
||||||
|
|
||||||
|
import random
|
||||||
|
|
||||||
|
from app.configs.constants import EducationalContent
|
||||||
from app.dtos.level import LevelExercisesDTO
|
from app.dtos.level import LevelExercisesDTO
|
||||||
from app.repositories.abc import IDocumentStore
|
from app.repositories.abc import IDocumentStore
|
||||||
from app.services.abc import (
|
from app.services.abc import (
|
||||||
@@ -41,48 +47,61 @@ class LevelService(ILevelService):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def upload_level(self, upload: UploadFile) -> Dict:
|
async def upload_level(self, upload: UploadFile, solutions: Optional[UploadFile] = None) -> Dict:
|
||||||
return await self._upload_module.generate_level_from_file(upload)
|
return await self._upload_module.generate_level_from_file(upload, solutions)
|
||||||
|
|
||||||
|
async def _generate_exercise(self, req_exercise, start_id):
|
||||||
|
if req_exercise.type == "mcBlank":
|
||||||
|
questions = await self._mc.gen_multiple_choice("blank_space", req_exercise.quantity, start_id)
|
||||||
|
questions["variant"] = "mcBlank"
|
||||||
|
questions["type"] = "multipleChoice"
|
||||||
|
questions["prompt"] = "Choose the correct word or group of words that completes the sentences."
|
||||||
|
return questions
|
||||||
|
|
||||||
|
elif req_exercise.type == "mcUnderline":
|
||||||
|
questions = await self._mc.gen_multiple_choice("underline", req_exercise.quantity, start_id)
|
||||||
|
questions["variant"] = "mcUnderline"
|
||||||
|
questions["type"] = "multipleChoice"
|
||||||
|
questions["prompt"] = "Choose the underlined word or group of words that is not correct."
|
||||||
|
return questions
|
||||||
|
|
||||||
|
elif req_exercise.type == "passageUtas":
|
||||||
|
topic = req_exercise.topic if req_exercise.topic else random.choice(EducationalContent.TOPICS)
|
||||||
|
exercise = await self._passage_utas.gen_reading_passage_utas(
|
||||||
|
start_id,
|
||||||
|
req_exercise.quantity,
|
||||||
|
topic,
|
||||||
|
req_exercise.text_size
|
||||||
|
)
|
||||||
|
exercise["prompt"] = "Read the text and answer the questions below."
|
||||||
|
|
||||||
|
return exercise
|
||||||
|
|
||||||
|
elif req_exercise.type == "fillBlanksMC":
|
||||||
|
exercise = await self._fill_blanks.gen_fill_blanks(
|
||||||
|
start_id,
|
||||||
|
req_exercise.quantity,
|
||||||
|
req_exercise.text_size,
|
||||||
|
req_exercise.topic
|
||||||
|
)
|
||||||
|
exercise["prompt"] = "Read the text below and choose the correct word for each space."
|
||||||
|
return exercise
|
||||||
|
|
||||||
async def generate_exercises(self, dto: LevelExercisesDTO):
|
async def generate_exercises(self, dto: LevelExercisesDTO):
|
||||||
exercises = []
|
start_ids = []
|
||||||
start_id = 1
|
current_id = 1
|
||||||
|
|
||||||
for req_exercise in dto.exercises:
|
for req_exercise in dto.exercises:
|
||||||
if req_exercise.type == "multipleChoice":
|
start_ids.append(current_id)
|
||||||
questions = await self._mc.gen_multiple_choice("normal", req_exercise.quantity, start_id)
|
current_id += req_exercise.quantity
|
||||||
exercises.append(questions)
|
|
||||||
|
|
||||||
elif req_exercise.type == "mcBlank":
|
tasks = [
|
||||||
questions = await self._mc.gen_multiple_choice("blank_space", req_exercise.quantity, start_id)
|
self._generate_exercise(req_exercise, start_id)
|
||||||
questions["variant"] = "mc"
|
for req_exercise, start_id in zip(dto.exercises, start_ids)
|
||||||
exercises.append(questions)
|
]
|
||||||
|
questions = await gather(*tasks)
|
||||||
|
questions = [{'id': str(uuid4()), **exercise} for exercise in questions]
|
||||||
|
|
||||||
elif req_exercise.type == "mcUnderline":
|
return {"exercises": questions}
|
||||||
questions = await self._mc.gen_multiple_choice("underline", req_exercise.quantity, start_id)
|
|
||||||
exercises.append(questions)
|
|
||||||
|
|
||||||
elif req_exercise.type == "blankSpaceText":
|
|
||||||
questions = await self._blank_space.gen_blank_space_text_utas(
|
|
||||||
req_exercise.quantity, start_id, req_exercise.text_size, req_exercise.topic
|
|
||||||
)
|
|
||||||
exercises.append(questions)
|
|
||||||
|
|
||||||
elif req_exercise.type == "passageUtas":
|
|
||||||
questions = await self._passage_utas.gen_reading_passage_utas(
|
|
||||||
start_id, req_exercise.mc_qty, req_exercise.text_size
|
|
||||||
)
|
|
||||||
exercises.append(questions)
|
|
||||||
|
|
||||||
elif req_exercise.type == "fillBlanksMC":
|
|
||||||
questions = await self._passage_utas.gen_reading_passage_utas(
|
|
||||||
start_id, req_exercise.mc_qty, req_exercise.text_size
|
|
||||||
)
|
|
||||||
exercises.append(questions)
|
|
||||||
|
|
||||||
start_id = start_id + req_exercise.quantity
|
|
||||||
|
|
||||||
return exercises
|
|
||||||
|
|
||||||
# Just here to support other modules that I don't know if they are supposed to still be used
|
# Just here to support other modules that I don't know if they are supposed to still be used
|
||||||
async def gen_multiple_choice(self, mc_variant: str, quantity: int, start_id: int = 1):
|
async def gen_multiple_choice(self, mc_variant: str, quantity: int, start_id: int = 1):
|
||||||
|
|||||||
@@ -11,11 +11,12 @@ class FillBlanks:
|
|||||||
|
|
||||||
|
|
||||||
async def gen_fill_blanks(
|
async def gen_fill_blanks(
|
||||||
self, quantity: int, start_id: int, size: int, topic=None
|
self, start_id: int, quantity: int, size: int = 300, topic=None
|
||||||
):
|
):
|
||||||
if not topic:
|
if not topic:
|
||||||
topic = random.choice(EducationalContent.MTI_TOPICS)
|
topic = random.choice(EducationalContent.MTI_TOPICS)
|
||||||
|
print(quantity)
|
||||||
|
print(start_id)
|
||||||
messages = [
|
messages = [
|
||||||
{
|
{
|
||||||
"role": "system",
|
"role": "system",
|
||||||
@@ -28,19 +29,18 @@ class FillBlanks:
|
|||||||
{
|
{
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"content": (
|
"content": (
|
||||||
f'From the generated text choose {quantity} words (cannot be sequential words) to replace '
|
f'From the generated text choose exactly {quantity} words (cannot be sequential words) replace '
|
||||||
'once with {{id}} where id starts on ' + str(start_id) + ' and is incremented for each word. '
|
'each with {{id}} (starting from ' + str(start_id) + ' and incrementing), then generate a '
|
||||||
'The ids must be ordered throughout the text and the words must be replaced only once. '
|
'JSON object containing: the modified text, a solutions array with each word\'s correct '
|
||||||
'For each removed word you will place it in the solutions array and assign a letter from A to D,'
|
'letter (A-D), and a words array containing each id with four options where one is '
|
||||||
' then you will place that removed word and the chosen letter on the words array along with '
|
'the original word (matching the solution) and three are plausible but incorrect '
|
||||||
' other 3 other words for the remaining letter. This is a fill blanks question for an english '
|
'alternatives that maintain grammatical consistency. '
|
||||||
'exam, so don\'t choose words completely at random.'
|
'You cannot use repeated words!' #TODO: Solve this after
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
question = await self._llm.prediction(
|
question = await self._llm.prediction(
|
||||||
GPTModels.GPT_4_O, messages, ["question"], TemperatureSettings.GEN_QUESTION_TEMPERATURE
|
GPTModels.GPT_4_O, messages, [], TemperatureSettings.GEN_QUESTION_TEMPERATURE
|
||||||
)
|
)
|
||||||
return {
|
return {
|
||||||
**question,
|
**question,
|
||||||
@@ -56,7 +56,7 @@ class FillBlanks:
|
|||||||
"solutions": [
|
"solutions": [
|
||||||
{
|
{
|
||||||
"id": "",
|
"id": "",
|
||||||
"solution": ""
|
"solution": "<A,B,C or D>"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"words": [
|
"words": [
|
||||||
|
|||||||
@@ -13,15 +13,12 @@ class PassageUtas:
|
|||||||
self._mc_variants = mc_variants
|
self._mc_variants = mc_variants
|
||||||
|
|
||||||
async def gen_reading_passage_utas(
|
async def gen_reading_passage_utas(
|
||||||
self, start_id, mc_quantity: int, topic: Optional[str] # sa_quantity: int,
|
self, start_id, mc_quantity: int, topic: Optional[str], word_size: Optional[int] # sa_quantity: int,
|
||||||
):
|
):
|
||||||
|
|
||||||
passage = await self._reading_service.generate_reading_passage(1, topic)
|
passage = await self._reading_service.generate_reading_passage(1, topic, word_size)
|
||||||
mc_exercises = await self._gen_text_multiple_choice_utas(passage["text"], start_id, mc_quantity)
|
mc_exercises = await self._gen_text_multiple_choice_utas(passage["text"], start_id, mc_quantity)
|
||||||
|
mc_exercises["type"] = "multipleChoice"
|
||||||
#short_answer = await self._gen_short_answer_utas(passage["text"], start_id, sa_quantity)
|
|
||||||
# + sa_quantity, mc_quantity)
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
exercises: {
|
exercises: {
|
||||||
"shortAnswer": short_answer,
|
"shortAnswer": short_answer,
|
||||||
@@ -29,11 +26,12 @@ class PassageUtas:
|
|||||||
},
|
},
|
||||||
"""
|
"""
|
||||||
return {
|
return {
|
||||||
"exercises": mc_exercises,
|
**mc_exercises,
|
||||||
"text": {
|
"passage": {
|
||||||
"content": passage["text"],
|
"content": passage["text"],
|
||||||
"title": passage["title"]
|
"title": passage["title"]
|
||||||
}
|
},
|
||||||
|
"mcVariant": "passageUtas"
|
||||||
}
|
}
|
||||||
|
|
||||||
async def _gen_short_answer_utas(self, text: str, start_id: int, sa_quantity: int):
|
async def _gen_short_answer_utas(self, text: str, start_id: int, sa_quantity: int):
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import aiofiles
|
|||||||
import os
|
import os
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
|
|
||||||
from typing import Dict, Any, Coroutine
|
from typing import Dict, Any, Coroutine, Optional
|
||||||
|
|
||||||
import pdfplumber
|
import pdfplumber
|
||||||
from fastapi import UploadFile
|
from fastapi import UploadFile
|
||||||
@@ -21,7 +21,7 @@ class UploadLevelModule:
|
|||||||
self._logger = getLogger(__name__)
|
self._logger = getLogger(__name__)
|
||||||
self._llm = openai
|
self._llm = openai
|
||||||
|
|
||||||
async def generate_level_from_file(self, file: UploadFile) -> Dict[str, Any] | None:
|
async def generate_level_from_file(self, file: UploadFile, solutions: Optional[UploadFile]) -> Dict[str, Any] | None:
|
||||||
ext, path_id = await FileHelper.save_upload(file)
|
ext, path_id = await FileHelper.save_upload(file)
|
||||||
FileHelper.convert_file_to_pdf(
|
FileHelper.convert_file_to_pdf(
|
||||||
f'./tmp/{path_id}/upload.{ext}', f'./tmp/{path_id}/exercises.pdf'
|
f'./tmp/{path_id}/upload.{ext}', f'./tmp/{path_id}/exercises.pdf'
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ class Heygen(IVideoGeneratorService):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def pool_status(self, video_id: str) -> Task:
|
async def poll_status(self, video_id: str) -> Task:
|
||||||
response = await self._http_client.get(self._GET_VIDEO_URL, headers=self._get_header, params={
|
response = await self._http_client.get(self._GET_VIDEO_URL, headers=self._get_header, params={
|
||||||
'video_id': video_id
|
'video_id': video_id
|
||||||
})
|
})
|
||||||
@@ -65,7 +65,6 @@ class Heygen(IVideoGeneratorService):
|
|||||||
|
|
||||||
status = response_data["data"]["status"]
|
status = response_data["data"]["status"]
|
||||||
error = response_data["data"]["error"]
|
error = response_data["data"]["error"]
|
||||||
|
|
||||||
if status != "completed" and error is None:
|
if status != "completed" and error is None:
|
||||||
self._logger.info(f"Status: {status}")
|
self._logger.info(f"Status: {status}")
|
||||||
return Task(
|
return Task(
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ class OpenAI(ILLMService):
|
|||||||
return await self._prediction(
|
return await self._prediction(
|
||||||
model, messages, token_count, fields_to_check, temperature, (try_count + 1), check_blacklisted
|
model, messages, token_count, fields_to_check, temperature, (try_count + 1), check_blacklisted
|
||||||
)
|
)
|
||||||
|
print(result)
|
||||||
return json.loads(result)
|
return json.loads(result)
|
||||||
|
|
||||||
async def prediction_override(self, **kwargs):
|
async def prediction_override(self, **kwargs):
|
||||||
|
|||||||
Reference in New Issue
Block a user