diff --git a/app.py b/app.py index 4486e20..f78fee5 100644 --- a/app.py +++ b/app.py @@ -3,6 +3,8 @@ import threading from flask import Flask, request from flask_jwt_extended import JWTManager, jwt_required from functools import reduce + +from helper.ExamVariant import ExamVariant from helper.api_messages import * from helper.exercises import * from helper.file_helper import delete_files_older_than_one_day @@ -161,6 +163,7 @@ def save_listening(): try: data = request.get_json() parts = data.get('parts') + minTimer = data.get('minTimer', LISTENING_MIN_TIMER_DEFAULT) template = getListeningTemplate() id = str(uuid.uuid4()) for i, part in enumerate(parts, start=0): @@ -175,6 +178,12 @@ def save_listening(): template["parts"][i]["audio"]["source"] = file_url template["parts"][i]["exercises"] = part["exercises"] + if minTimer != LISTENING_MIN_TIMER_DEFAULT: + template["minTimer"] = minTimer + template["variant"] = ExamVariant.PARTIAL.value + else: + template["variant"] = ExamVariant.FULL.value + (result, id) = save_to_db_with_id("listening", template, id) if result: return {**template, "id": id} @@ -293,25 +302,25 @@ def get_writing_task_2_general_question(): except Exception as e: return str(e) - -@app.route('/writing', methods=['POST']) -@jwt_required() -def save_writing_task(): - try: - 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_with_id("writing", template, id) - if result: - return {**template, "id": id} - else: - raise Exception("Failed to save writing: " + template) - except Exception as e: - return str(e) +# THE SAVING OF WRITING IS DONE WITHOUT THE API ON THE FRONTEND +# @app.route('/writing', methods=['POST']) +# @jwt_required() +# def save_writing_task(): +# try: +# 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_with_id("writing", template, id) +# if result: +# return {**template, "id": id} +# else: +# raise Exception("Failed to save writing: " + template) +# except Exception as e: +# return str(e) @app.route('/speaking_task_1', methods=['POST']) @@ -377,6 +386,7 @@ def get_speaking_task_1_question(): token_count = count_tokens(gen_sp1_question)["n_tokens"] response = make_openai_instruct_call(GPT_3_5_TURBO_INSTRUCT, gen_sp1_question, token_count, GEN_FIELDS, GEN_QUESTION_TEMPERATURE) + response["type"] = 1 return response except Exception as e: return str(e) @@ -446,6 +456,7 @@ def get_speaking_task_2_question(): token_count = count_tokens(gen_sp2_question)["n_tokens"] response = make_openai_instruct_call(GPT_3_5_TURBO_INSTRUCT, gen_sp2_question, token_count, GEN_FIELDS, GEN_QUESTION_TEMPERATURE) + response["type"] = 2 return response except Exception as e: return str(e) @@ -466,6 +477,7 @@ def get_speaking_task_3_question(): # Remove the numbers from the questions only if the string starts with a number response["questions"] = [re.sub(r"^\d+\.\s*", "", question) if re.match(r"^\d+\.", question) else question for question in response["questions"]] + response["type"] = 3 return response except Exception as e: return str(e) @@ -544,7 +556,15 @@ def save_speaking(): try: data = request.get_json() exercises = data.get('exercises') + minTimer = data.get('minTimer', SPEAKING_MIN_TIMER_DEFAULT) template = getSpeakingTemplate() + + if minTimer != SPEAKING_MIN_TIMER_DEFAULT: + template["minTimer"] = minTimer + template["variant"] = ExamVariant.PARTIAL.value + else: + template["variant"] = ExamVariant.FULL.value + id = str(uuid.uuid4()) app.logger.info('Received request to save speaking with id: ' + id) thread_event.set() @@ -646,22 +666,23 @@ def get_reading_passage_3_question(): return str(e) -@app.route('/reading', methods=['POST']) -@jwt_required() -def save_reading_passage(): - try: - data = request.get_json() - parts = data.get('parts') - template = getReadingTemplate() - template["parts"] = parts - id = str(uuid.uuid4()) - (result, id) = save_to_db_with_id("reading", template, id) - if result: - return {**template, "id": id} - else: - raise Exception("Failed to save reading: " + template) - except Exception as e: - return str(e) +# THE SAVING OF READING IS DONE WITHOUT THE API ON THE FRONTEND +# @app.route('/reading', methods=['POST']) +# @jwt_required() +# def save_reading_passage(): +# try: +# data = request.get_json() +# parts = data.get('parts') +# template = getReadingTemplate() +# template["parts"] = parts +# id = str(uuid.uuid4()) +# (result, id) = save_to_db_with_id("reading", template, id) +# if result: +# return {**template, "id": id} +# else: +# raise Exception("Failed to save reading: " + template) +# except Exception as e: +# return str(e) @app.route('/level', methods=['GET']) diff --git a/helper/ExamVariant.py b/helper/ExamVariant.py new file mode 100644 index 0000000..42f1699 --- /dev/null +++ b/helper/ExamVariant.py @@ -0,0 +1,6 @@ +from enum import Enum + + +class ExamVariant(Enum): + FULL = "full" + PARTIAL = "partial" \ No newline at end of file diff --git a/helper/constants.py b/helper/constants.py index b5f5ca5..5ccd495 100644 --- a/helper/constants.py +++ b/helper/constants.py @@ -27,6 +27,10 @@ TOTAL_LISTENING_SECTION_2_EXERCISES = 10 TOTAL_LISTENING_SECTION_3_EXERCISES = 10 TOTAL_LISTENING_SECTION_4_EXERCISES = 10 +LISTENING_MIN_TIMER_DEFAULT = 30 +WRITING_MIN_TIMER_DEFAULT = 60 +SPEAKING_MIN_TIMER_DEFAULT = 14 + EN_US_VOICES = [ {'Gender': 'Female', 'Id': 'Salli', 'LanguageCode': 'en-US', 'LanguageName': 'US English', 'Name': 'Salli', 'SupportedEngines': ['neural', 'standard']}, diff --git a/helper/heygen_api.py b/helper/heygen_api.py index 6e47518..b96478d 100644 --- a/helper/heygen_api.py +++ b/helper/heygen_api.py @@ -38,58 +38,80 @@ TYLER_CHRISTOPHER = "03c796f8ed274bb38f19e893bcbc6121" def create_videos_and_save_to_db(exercises, template, id): # Speaking 1 - app.app.logger.info('Creating video for speaking part 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: - app.app.logger.error("Failed to create video for part 1 question: " + exercises[0]["question"]) + # Using list comprehension to find the element with the desired value in the 'type' field + found_exercises_1 = [element for element in exercises if element.get('type') == 1] + # Check if any elements were found + if found_exercises_1: + exercise_1 = found_exercises_1[0] + app.app.logger.info('Creating video for speaking part 1') + sp1_result = create_video(exercise_1["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"] = exercise_1["question"] + template["exercises"][0]["title"] = exercise_1["topic"] + template["exercises"][0]["video_url"] = sp1_video_url + template["exercises"][0]["video_path"] = sp1_video_path + else: + app.app.logger.error("Failed to create video for part 1 question: " + exercise_1["question"]) # Speaking 2 - app.app.logger.info('Creating video for speaking part 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: - app.app.logger.error("Failed to create video for part 2 question: " + exercises[1]["question"]) + # Using list comprehension to find the element with the desired value in the 'type' field + found_exercises_2 = [element for element in exercises if element.get('type') == 2] + # Check if any elements were found + if found_exercises_2: + exercise_2 = found_exercises_2[0] + app.app.logger.info('Creating video for speaking part 2') + sp2_result = create_video(exercise_2["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"] = exercise_2["prompts"] + template["exercises"][1]["text"] = exercise_2["question"] + template["exercises"][1]["title"] = exercise_2["topic"] + template["exercises"][1]["video_url"] = sp2_video_url + template["exercises"][1]["video_path"] = sp2_video_path + else: + app.app.logger.error("Failed to create video for part 2 question: " + exercise_2["question"]) # Speaking 3 - sp3_questions = [] - avatar = random.choice(list(AvatarEnum)) - app.app.logger.info('Creating videos for speaking part 3') - 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: - app.app.logger.error("Failed to create video for part 3 question: " + question) - template["exercises"][2]["prompts"] = sp3_questions - template["exercises"][2]["title"] = exercises[2]["topic"] + # Using list comprehension to find the element with the desired value in the 'type' field + found_exercises_3 = [element for element in exercises if element.get('type') == 3] + # Check if any elements were found + if found_exercises_3: + exercise_3 = found_exercises_3[0] + sp3_questions = [] + avatar = random.choice(list(AvatarEnum)) + app.app.logger.info('Creating videos for speaking part 3') + for question in exercise_3["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: + app.app.logger.error("Failed to create video for part 3 question: " + question) + template["exercises"][2]["prompts"] = sp3_questions + template["exercises"][2]["title"] = exercise_3["topic"] + + if not found_exercises_3: + template["exercises"].pop(2) + if not found_exercises_2: + template["exercises"].pop(1) + if not found_exercises_1: + template["exercises"].pop(0) save_to_db_with_id("speaking", template, id) app.app.logger.info('Saved speaking to DB with id ' + id + " : " + str(template)) diff --git a/templates/question_templates.py b/templates/question_templates.py index 719c37f..a36b466 100644 --- a/templates/question_templates.py +++ b/templates/question_templates.py @@ -1,5 +1,7 @@ import uuid +from helper.constants import * + def getListeningTemplate(): return { @@ -34,7 +36,7 @@ def getListeningTemplate(): }, ], "isDiagnostic": False, - "minTimer": 30, + "minTimer": LISTENING_MIN_TIMER_DEFAULT, "module": "listening" } @@ -1180,7 +1182,7 @@ def getSpeakingTemplate(): } ], "isDiagnostic": False, - "minTimer": 14, + "minTimer": SPEAKING_MIN_TIMER_DEFAULT, "module": "speaking" } @@ -1242,7 +1244,7 @@ def getWritingTemplate(): } ], "isDiagnostic": False, - "minTimer": 60, + "minTimer": WRITING_MIN_TIMER_DEFAULT, "module": "writing", "type": "general" }