From 9f4aed52ae274fcbe2c2c87d4476cb0ce7f97bea Mon Sep 17 00:00:00 2001 From: Cristiano Ferreira Date: Sun, 10 Dec 2023 15:38:26 +0000 Subject: [PATCH] Save speaking asynchronously --- app.py | 82 ++++++++++----------------------------- helper/firebase_helper.py | 14 +++++++ helper/heygen_api.py | 58 ++++++++++++++++++++++++++- 3 files changed, 91 insertions(+), 63 deletions(-) diff --git a/app.py b/app.py index 17545bc..7b621ed 100644 --- a/app.py +++ b/app.py @@ -1,14 +1,13 @@ -import random +import threading from flask import Flask, request from flask_jwt_extended import JWTManager, jwt_required from functools import reduce from helper.api_messages import * -from helper.constants import * from helper.exercises import * from helper.file_helper import delete_files_older_than_one_day from helper.firebase_helper import * -from helper.heygen_api import create_video +from helper.heygen_api import create_videos_and_save_to_db from helper.speech_to_text_helper import * from helper.token_counter import count_tokens from helper.openai_interface import make_openai_call, make_openai_instruct_call @@ -31,6 +30,8 @@ jwt = JWTManager(app) cred = credentials.Certificate(os.getenv("GOOGLE_APPLICATION_CREDENTIALS")) firebase_admin.initialize_app(cred) +thread_event = threading.Event() + @app.route('/healthcheck', methods=['GET']) def healthcheck(): @@ -158,6 +159,7 @@ def save_listening(): data = request.get_json() parts = data.get('parts') template = getListeningTemplate() + id = str(uuid.uuid4()) for i, part in enumerate(parts, start=0): file_name = str(uuid.uuid4()) + ".mp3" sound_file_path = AUDIO_FILES_PATH + file_name @@ -170,7 +172,7 @@ def save_listening(): template["parts"][i]["audio"]["source"] = file_url template["parts"][i]["exercises"] = part["exercises"] - (result, id) = save_to_db("listening", template) + (result, id) = save_to_db_with_id("listening", template, id) if result: return {**template, "id": id} else: @@ -292,10 +294,11 @@ def save_writing_task(): data = request.get_json() exercises = data.get('exercises') template = getWritingTemplate() + id = str(uuid.uuid4()) for i, exercise in enumerate(exercises, start=0): template["exercises"][i]["prompt"] = exercise - (result, id) = save_to_db("writing", template) + (result, id) = save_to_db_with_id("writing", template, id) if result: return {**template, "id": id} else: @@ -524,63 +527,18 @@ def save_speaking(): data = request.get_json() exercises = data.get('exercises') template = getSpeakingTemplate() + id = str(uuid.uuid4()) - # Speaking 1 - sp1_result = create_video(exercises[0]["question"], random.choice(list(AvatarEnum))) - if sp1_result is not None: - sound_file_path = VIDEO_FILES_PATH + sp1_result - firebase_file_path = FIREBASE_SPEAKING_VIDEO_FILES_PATH + sp1_result - url = upload_file_firebase_get_url(FIREBASE_BUCKET, firebase_file_path, sound_file_path) - sp1_video_path = firebase_file_path - sp1_video_url = url - template["exercises"][0]["text"] = exercises[0]["question"] - template["exercises"][0]["title"] = exercises[0]["topic"] - template["exercises"][0]["video_url"] = sp1_video_url - template["exercises"][0]["video_path"] = sp1_video_path - else: - print("Failed to create video for part 1 question: " + exercises[0]["question"]) + thread_event.set() + thread = threading.Thread( + target=create_videos_and_save_to_db, + args=(exercises, template, id), + name=("thread-save-speaking-" + id) + ) + thread.start() - # Speaking 2 - sp2_result = create_video(exercises[1]["question"], random.choice(list(AvatarEnum))) - if sp2_result is not None: - sound_file_path = VIDEO_FILES_PATH + sp2_result - firebase_file_path = FIREBASE_SPEAKING_VIDEO_FILES_PATH + sp2_result - url = upload_file_firebase_get_url(FIREBASE_BUCKET, firebase_file_path, sound_file_path) - sp2_video_path = firebase_file_path - sp2_video_url = url - template["exercises"][1]["prompts"] = exercises[1]["prompts"] - template["exercises"][1]["text"] = exercises[1]["question"] - template["exercises"][1]["title"] = exercises[1]["topic"] - template["exercises"][1]["video_url"] = sp2_video_url - template["exercises"][1]["video_path"] = sp2_video_path - else: - print("Failed to create video for part 2 question: " + exercises[1]["question"]) - - # Speaking 3 - sp3_questions = [] - avatar = random.choice(list(AvatarEnum)) - for question in exercises[2]["questions"]: - result = create_video(question, avatar) - if result is not None: - sound_file_path = VIDEO_FILES_PATH + result - firebase_file_path = FIREBASE_SPEAKING_VIDEO_FILES_PATH + result - url = upload_file_firebase_get_url(FIREBASE_BUCKET, firebase_file_path, sound_file_path) - video = { - "text": question, - "video_path": firebase_file_path, - "video_url": url - } - sp3_questions.append(video) - else: - print("Failed to create video for part 3 question: " + question) - template["exercises"][2]["prompts"] = sp3_questions - template["exercises"][2]["title"] = exercises[2]["topic"] - - (result, id) = save_to_db("speaking", template) - if result: - return {**template, "id": id} - else: - raise Exception("Failed to save speaking: " + template) + # Return response without waiting for create_videos_and_save_to_db to finish + return {**template, "id": id} except Exception as e: return str(e) @@ -677,8 +635,8 @@ def save_reading_passage(): parts = data.get('parts') template = getReadingTemplate() template["parts"] = parts - - (result, id) = save_to_db("reading", template) + id = str(uuid.uuid4()) + (result, id) = save_to_db_with_id("reading", template, id) if result: return {**template, "id": id} else: diff --git a/helper/firebase_helper.py b/helper/firebase_helper.py index d169167..b5d40dc 100644 --- a/helper/firebase_helper.py +++ b/helper/firebase_helper.py @@ -59,4 +59,18 @@ def save_to_db(collection: str, item): return (False, None) +def save_to_db_with_id(collection: str, item, id: str): + db = firestore.client() + collection_ref = db.collection(collection) + # Reference to the specific document with the desired ID + document_ref = collection_ref.document(id) + # Set the data to the document + document_ref.set(item) + if document_ref: + print(f"Document added with ID: {document_ref.id}") + return (True, document_ref.id) + else: + return (False, None) + + diff --git a/helper/heygen_api.py b/helper/heygen_api.py index 5455cab..99e1654 100644 --- a/helper/heygen_api.py +++ b/helper/heygen_api.py @@ -1,9 +1,12 @@ import os +import random + import requests import time from dotenv import load_dotenv - +from helper.constants import * +from helper.firebase_helper import upload_file_firebase_get_url, save_to_db_with_id from heygen.AvatarEnum import AvatarEnum load_dotenv() @@ -30,6 +33,59 @@ KAYLA_ABBI = "d688099f8db9472cb4890b0561e81793" JEROME_RYAN = "ad41feb2a5c4483085525e3d8907f512" TYLER_CHRISTOPHER = "03c796f8ed274bb38f19e893bcbc6121" +def create_videos_and_save_to_db(exercises, template, id): + # Speaking 1 + sp1_result = create_video(exercises[0]["question"], random.choice(list(AvatarEnum))) + if sp1_result is not None: + sound_file_path = VIDEO_FILES_PATH + sp1_result + firebase_file_path = FIREBASE_SPEAKING_VIDEO_FILES_PATH + sp1_result + url = upload_file_firebase_get_url(FIREBASE_BUCKET, firebase_file_path, sound_file_path) + sp1_video_path = firebase_file_path + sp1_video_url = url + template["exercises"][0]["text"] = exercises[0]["question"] + template["exercises"][0]["title"] = exercises[0]["topic"] + template["exercises"][0]["video_url"] = sp1_video_url + template["exercises"][0]["video_path"] = sp1_video_path + else: + print("Failed to create video for part 1 question: " + exercises[0]["question"]) + + # Speaking 2 + sp2_result = create_video(exercises[1]["question"], random.choice(list(AvatarEnum))) + if sp2_result is not None: + sound_file_path = VIDEO_FILES_PATH + sp2_result + firebase_file_path = FIREBASE_SPEAKING_VIDEO_FILES_PATH + sp2_result + url = upload_file_firebase_get_url(FIREBASE_BUCKET, firebase_file_path, sound_file_path) + sp2_video_path = firebase_file_path + sp2_video_url = url + template["exercises"][1]["prompts"] = exercises[1]["prompts"] + template["exercises"][1]["text"] = exercises[1]["question"] + template["exercises"][1]["title"] = exercises[1]["topic"] + template["exercises"][1]["video_url"] = sp2_video_url + template["exercises"][1]["video_path"] = sp2_video_path + else: + print("Failed to create video for part 2 question: " + exercises[1]["question"]) + + # Speaking 3 + sp3_questions = [] + avatar = random.choice(list(AvatarEnum)) + for question in exercises[2]["questions"]: + result = create_video(question, avatar) + if result is not None: + sound_file_path = VIDEO_FILES_PATH + result + firebase_file_path = FIREBASE_SPEAKING_VIDEO_FILES_PATH + result + url = upload_file_firebase_get_url(FIREBASE_BUCKET, firebase_file_path, sound_file_path) + video = { + "text": question, + "video_path": firebase_file_path, + "video_url": url + } + sp3_questions.append(video) + else: + print("Failed to create video for part 3 question: " + question) + template["exercises"][2]["prompts"] = sp3_questions + template["exercises"][2]["title"] = exercises[2]["topic"] + + save_to_db_with_id("speaking", template, id) def create_video(text, avatar: AvatarEnum): # POST TO CREATE VIDEO