Merged in release/async (pull request #49)

Release/async

Approved-by: Tiago Ribeiro
This commit is contained in:
carlos.mesquita
2025-01-06 09:11:52 +00:00
committed by Tiago Ribeiro
6 changed files with 91 additions and 10 deletions

13
firebase-debug.log Normal file
View File

@@ -0,0 +1,13 @@
[debug] [2025-01-05T18:01:58.255Z] ----------------------------------------------------------------------
[debug] [2025-01-05T18:01:58.257Z] Command: /usr/bin/node /usr/local/bin/firebase login --reauth
[debug] [2025-01-05T18:01:58.257Z] CLI Version: 13.28.0
[debug] [2025-01-05T18:01:58.257Z] Platform: linux
[debug] [2025-01-05T18:01:58.257Z] Node Version: v18.19.1
[debug] [2025-01-05T18:01:58.258Z] Time: Sun Jan 05 2025 18:01:58 GMT+0000 (Western European Standard Time)
[debug] [2025-01-05T18:01:58.258Z] ----------------------------------------------------------------------
[debug]
[info]
[info] Visit this URL on this device to log in:
[info] https://accounts.google.com/o/oauth2/auth?client_id=563584335869-fgrhgmd47bqnekij5i8b5pr03ho849e6.apps.googleusercontent.com&scope=email%20openid%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloudplatformprojects.readonly%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Ffirebase%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform&response_type=code&state=211115440&redirect_uri=http%3A%2F%2Flocalhost%3A9005&login_hint=carlos.mesquita%40ecrop.dev
[info]
[info] Waiting for authentication...

View File

@@ -30,7 +30,7 @@ class ConversationPayload(BaseModel):
name: str name: str
gender: str gender: str
text: str text: str
voice: str voice: Optional[str] = None
class Dialog(BaseModel): class Dialog(BaseModel):
conversation: Optional[List[ConversationPayload]] = Field(default_factory=list) conversation: Optional[List[ConversationPayload]] = Field(default_factory=list)

View File

@@ -15,7 +15,7 @@ class Entity(BaseModel):
class UserDTO(BaseModel): class UserDTO(BaseModel):
id: uuid.UUID = Field(default_factory=uuid.uuid4) id: str
email: str email: str
name: str name: str
type: str type: str

View File

@@ -1,7 +1,7 @@
import asyncio import asyncio
from logging import getLogger from logging import getLogger
import random import random
from typing import Dict, Any from typing import Dict, Any, Union
from starlette.datastructures import UploadFile from starlette.datastructures import UploadFile
@@ -111,6 +111,15 @@ class ListeningService(IListeningService):
return dialog return dialog
async def generate_mp3(self, dto: Dialog) -> bytes: async def generate_mp3(self, dto: Dialog) -> bytes:
convo = dto.conversation
voices_assigned = True
for segment in convo:
if segment.voice is None:
voices_assigned = False
if not voices_assigned:
dto = self._get_conversation_voices(dto, True)
return await self._tts.text_to_speech(dto) return await self._tts.text_to_speech(dto)
async def create_instructions(self, text: str) -> bytes: async def create_instructions(self, text: str) -> bytes:
@@ -263,7 +272,13 @@ class ListeningService(IListeningService):
) )
return {"dialog": response["monologue"]} return {"dialog": response["monologue"]}
def _get_conversation_voices(self, response: Dict, unique_voices_across_segments: bool): # TODO: This was a refactor from the previous ielts-be, don't know why there is a distinction between
# section 1 and 3, I think it would make sense to only keep only the section 1 logic, only bringing this up since
# there would need to be a refactor of the POST /api/listening/media endpoint which imo is pointless
# https://bitbucket.org/ecropdev/ielts-be/src/676f660f3e80220e3db0418dbeef0b1c0f257edb/helper/exercises.py?at=release%2Fmongodb-migration
"""
def generate_listening_1_conversation(topic: str):
...
chosen_voices = [] chosen_voices = []
name_to_voice = {} name_to_voice = {}
for segment in response['conversation']: for segment in response['conversation']:
@@ -273,18 +288,70 @@ class ListeningService(IListeningService):
voice = name_to_voice[name] voice = name_to_voice[name]
else: else:
voice = None voice = None
while voice is None:
if segment['gender'].lower() == 'male':
available_voices = MALE_NEURAL_VOICES
else:
available_voices = FEMALE_NEURAL_VOICES
chosen_voice = random.choice(available_voices)['Id']
if chosen_voice not in chosen_voices:
voice = chosen_voice
chosen_voices.append(voice)
name_to_voice[name] = voice
segment['voice'] = voice
return response
def generate_listening_3_conversation(topic: str):
...
name_to_voice = {}
for segment in response['conversation']:
if 'voice' not in segment:
name = segment['name']
if name in name_to_voice:
voice = name_to_voice[name]
else:
if segment['gender'].lower() == 'male':
voice = random.choice(MALE_NEURAL_VOICES)['Id']
else:
voice = random.choice(FEMALE_NEURAL_VOICES)['Id']
name_to_voice[name] = voice
segment['voice'] = voice
return response
"""
def _get_conversation_voices(self, response: Union[Dict, Dialog], unique_voices_across_segments: bool):
chosen_voices = []
name_to_voice = {}
is_model = isinstance(response, Dialog)
conversation = response.conversation if is_model else response['conversation']
for segment in conversation:
voice_check = (segment.voice is None) if is_model else ('voice' not in segment)
if voice_check:
name = segment.name if is_model else segment['name']
if name in name_to_voice:
voice = name_to_voice[name]
else:
voice = None
gender = segment.gender if is_model else segment['gender']
# section 1 # section 1
if unique_voices_across_segments: if unique_voices_across_segments:
while voice is None: while voice is None:
chosen_voice = self._get_random_voice(segment['gender']) chosen_voice = self._get_random_voice(gender)
if chosen_voice not in chosen_voices: if chosen_voice not in chosen_voices:
voice = chosen_voice voice = chosen_voice
chosen_voices.append(voice) chosen_voices.append(voice)
# section 3 # section 3
else: else:
voice = self._get_random_voice(segment['gender']) voice = self._get_random_voice(gender)
name_to_voice[name] = voice name_to_voice[name] = voice
segment['voice'] = voice
if is_model:
segment.voice = voice
else:
segment['voice'] = voice
return response return response
@staticmethod @staticmethod

View File

@@ -45,7 +45,6 @@ class UserService(IUserService):
error_msg = f"Couldn't upload users. Failed to run command firebase auth import -> ```cmd {result.stdout}```" error_msg = f"Couldn't upload users. Failed to run command firebase auth import -> ```cmd {result.stdout}```"
self._logger.error(error_msg) self._logger.error(error_msg)
return error_msg return error_msg
await self._init_users(batch_dto) await self._init_users(batch_dto)
FileHelper.remove_file(path) FileHelper.remove_file(path)
@@ -68,7 +67,7 @@ class UserService(IUserService):
for user in batch_dto.users: for user in batch_dto.users:
user_data = { user_data = {
'UID': str(user.id), 'UID': user.id,
'Email': user.email, 'Email': user.email,
'Email Verified': False, 'Email Verified': False,
'Password Hash': user.passwordHash, 'Password Hash': user.passwordHash,
@@ -142,7 +141,7 @@ class UserService(IUserService):
'subscriptionExpirationDate': user.expiryDate, 'subscriptionExpirationDate': user.expiryDate,
'entities': user.entities 'entities': user.entities
} }
await self._db.save_to_db("users", new_user, str(user.id)) await self._db.save_to_db("users", new_user, user.id)
async def _create_code(self, user: UserDTO, maker_id: str) -> str: async def _create_code(self, user: UserDTO, maker_id: str) -> str:
code = shortuuid.ShortUUID().random(length=6) code = shortuuid.ShortUUID().random(length=6)
@@ -174,6 +173,7 @@ class UserService(IUserService):
'name': user.groupName.strip(), 'name': user.groupName.strip(),
'participants': [user_id], 'participants': [user_id],
'disableEditing': False, 'disableEditing': False,
'entity': user.entities[0]['id']
} }
await self._db.save_to_db("groups", new_group, str(uuid.uuid4())) await self._db.save_to_db("groups", new_group, str(uuid.uuid4()))
else: else:

View File

@@ -0,0 +1 @@
8e44d32a-b921-4650-a0a5-2ad773356b61,batchentitytest1@ecrop.dev,False,5eoALGIXxSmwj8wRc3U7RGFM4tROrWs/+qJv6O9puXKoCCiniWlDQeWnCFXG8RJBrD48Hoqqoojso6rg31bTXA==,s76hIKNUacTPo8JqYx+7NA==,,,,,,,,,,,,,,,,,,,1736100072082,,
1 8e44d32a-b921-4650-a0a5-2ad773356b61 batchentitytest1@ecrop.dev False 5eoALGIXxSmwj8wRc3U7RGFM4tROrWs/+qJv6O9puXKoCCiniWlDQeWnCFXG8RJBrD48Hoqqoojso6rg31bTXA== s76hIKNUacTPo8JqYx+7NA== 1736100072082