ENCOA-94: Added user to training content docs, added support for shuffles, tweaked training prompt

This commit is contained in:
Carlos Mesquita
2024-08-26 18:14:57 +01:00
6 changed files with 751 additions and 332 deletions

View File

@@ -5,3 +5,4 @@ README.md
*.pyd
__pycache__
.pytest_cache
/scripts

1
.gitignore vendored
View File

@@ -3,3 +3,4 @@ __pycache__
.env
.DS_Store
/firebase-configs/test_firebase.json
/scripts

506
app.py
View File

@@ -70,25 +70,7 @@ def get_listening_section_1_question():
req_exercises = request.args.getlist('exercises')
difficulty = request.args.get("difficulty", default=random.choice(difficulties))
if (len(req_exercises) == 0):
req_exercises = random.sample(LISTENING_1_EXERCISE_TYPES, 1)
number_of_exercises_q = divide_number_into_parts(TOTAL_LISTENING_SECTION_1_EXERCISES, len(req_exercises))
processed_conversation = generate_listening_1_conversation(topic)
app.logger.info("Generated conversation: " + str(processed_conversation))
start_id = 1
exercises = generate_listening_conversation_exercises(parse_conversation(processed_conversation),
req_exercises,
number_of_exercises_q,
start_id, difficulty)
return {
"exercises": exercises,
"text": processed_conversation,
"difficulty": difficulty
}
return gen_listening_section_1(topic, difficulty, req_exercises)
except Exception as e:
return str(e)
@@ -103,22 +85,7 @@ def get_listening_section_2_question():
req_exercises = request.args.getlist('exercises')
difficulty = request.args.get("difficulty", default=random.choice(difficulties))
if (len(req_exercises) == 0):
req_exercises = random.sample(LISTENING_2_EXERCISE_TYPES, 2)
number_of_exercises_q = divide_number_into_parts(TOTAL_LISTENING_SECTION_2_EXERCISES, len(req_exercises))
monologue = generate_listening_2_monologue(topic)
app.logger.info("Generated monologue: " + str(monologue))
start_id = 11
exercises = generate_listening_monologue_exercises(str(monologue), req_exercises, number_of_exercises_q,
start_id, difficulty)
return {
"exercises": exercises,
"text": monologue,
"difficulty": difficulty
}
return gen_listening_section_2(topic, difficulty, req_exercises)
except Exception as e:
return str(e)
@@ -133,24 +100,7 @@ def get_listening_section_3_question():
req_exercises = request.args.getlist('exercises')
difficulty = request.args.get("difficulty", default=random.choice(difficulties))
if (len(req_exercises) == 0):
req_exercises = random.sample(LISTENING_3_EXERCISE_TYPES, 1)
number_of_exercises_q = divide_number_into_parts(TOTAL_LISTENING_SECTION_3_EXERCISES, len(req_exercises))
processed_conversation = generate_listening_3_conversation(topic)
app.logger.info("Generated conversation: " + str(processed_conversation))
start_id = 21
exercises = generate_listening_conversation_exercises(parse_conversation(processed_conversation), req_exercises,
number_of_exercises_q,
start_id, difficulty)
return {
"exercises": exercises,
"text": processed_conversation,
"difficulty": difficulty
}
return gen_listening_section_3(topic, difficulty, req_exercises)
except Exception as e:
return str(e)
@@ -165,22 +115,7 @@ def get_listening_section_4_question():
req_exercises = request.args.getlist('exercises')
difficulty = request.args.get("difficulty", default=random.choice(difficulties))
if (len(req_exercises) == 0):
req_exercises = random.sample(LISTENING_EXERCISE_TYPES, 2)
number_of_exercises_q = divide_number_into_parts(TOTAL_LISTENING_SECTION_4_EXERCISES, len(req_exercises))
monologue = generate_listening_4_monologue(topic)
app.logger.info("Generated monologue: " + str(monologue))
start_id = 31
exercises = generate_listening_monologue_exercises(str(monologue), req_exercises, number_of_exercises_q,
start_id, difficulty)
return {
"exercises": exercises,
"text": monologue,
"difficulty": difficulty
}
return gen_listening_section_4(topic, difficulty, req_exercises)
except Exception as e:
return str(e)
@@ -347,37 +282,7 @@ def get_writing_task_1_general_question():
difficulty = request.args.get("difficulty", default=random.choice(difficulties))
topic = request.args.get("topic", default=random.choice(mti_topics))
try:
messages = [
{
"role": "system",
"content": ('You are a helpful assistant designed to output JSON on this format: '
'{"prompt": "prompt content"}')
},
{
"role": "user",
"content": ('Craft a prompt for an IELTS Writing Task 1 General Training exercise that instructs the '
'student to compose a letter. The prompt should present a specific scenario or situation, '
'based on the topic of "' + topic + '", requiring the student to provide information, '
'advice, or instructions within the letter. '
'Make sure that the generated prompt is '
'of ' + difficulty + 'difficulty and does not contain '
'forbidden subjects in muslim '
'countries.')
},
{
"role": "user",
"content": 'The prompt should end with "In the letter you should" followed by 3 bullet points of what '
'the answer should include.'
}
]
token_count = count_total_tokens(messages)
response = make_openai_call(GPT_3_5_TURBO, messages, token_count, "prompt",
GEN_QUESTION_TEMPERATURE)
return {
"question": add_newline_before_hyphen(response["prompt"].strip()),
"difficulty": difficulty,
"topic": topic
}
return gen_writing_task_1(topic, difficulty)
except Exception as e:
return str(e)
@@ -512,32 +417,7 @@ def get_writing_task_2_general_question():
difficulty = request.args.get("difficulty", default=random.choice(difficulties))
topic = request.args.get("topic", default=random.choice(mti_topics))
try:
messages = [
{
"role": "system",
"content": ('You are a helpful assistant designed to output JSON on this format: '
'{"prompt": "prompt content"}')
},
{
"role": "user",
"content": (
'Craft a comprehensive question of ' + difficulty + 'difficulty like the ones for IELTS Writing Task 2 General Training that directs the candidate '
'to delve into an in-depth analysis of contrasting perspectives on the topic of "' + topic + '". '
'The candidate should be asked to discuss the strengths and weaknesses of both viewpoints.')
},
{
"role": "user",
"content": 'The question should lead to an answer with either "theories", "complicated information" or '
'be "very descriptive" on the topic.'
}
]
token_count = count_total_tokens(messages)
response = make_openai_call(GPT_4_O, messages, token_count, "prompt", GEN_QUESTION_TEMPERATURE)
return {
"question": response["prompt"].strip(),
"difficulty": difficulty,
"topic": topic
}
return gen_writing_task_2(topic, difficulty)
except Exception as e:
return str(e)
@@ -719,56 +599,8 @@ def get_speaking_task_1_question():
first_topic = request.args.get("first_topic", default=random.choice(mti_topics))
second_topic = request.args.get("second_topic", default=random.choice(mti_topics))
json_format = {
"first_topic": "topic 1",
"second_topic": "topic 2",
"questions": [
"Introductory question about the first topic, starting the topic with 'Let's talk about x' and then the "
"question.",
"Follow up question about the first topic",
"Follow up question about the first topic",
"Question about second topic",
"Follow up question about the second topic",
]
}
try:
messages = [
{
"role": "system",
"content": (
'You are a helpful assistant designed to output JSON on this format: ' + str(json_format))
},
{
"role": "user",
"content": (
'Craft 5 simple and single questions of easy difficulty for IELTS Speaking Part 1 '
'that encourages candidates to delve deeply into '
'personal experiences, preferences, or insights on the topic '
'of "' + first_topic + '" and the topic of "' + second_topic + '". '
'Make sure that the generated '
'question'
'does not contain forbidden '
'subjects in'
'muslim countries.')
},
{
"role": "user",
"content": 'The questions should lead to the usage of 4 verb tenses (present perfect, present, '
'past and future).'
},
{
"role": "user",
"content": 'They must be 1 single question each and not be double-barreled questions.'
}
]
token_count = count_total_tokens(messages)
response = make_openai_call(GPT_4_O, messages, token_count, ["first_topic"],
GEN_QUESTION_TEMPERATURE)
response["type"] = 1
response["difficulty"] = difficulty
return response
return gen_speaking_part_1(first_topic, second_topic, difficulty)
except Exception as e:
return str(e)
@@ -918,50 +750,8 @@ def get_speaking_task_2_question():
difficulty = request.args.get("difficulty", default=random.choice(difficulties))
topic = request.args.get("topic", default=random.choice(mti_topics))
json_format = {
"topic": "topic",
"question": "question",
"prompts": [
"prompt_1",
"prompt_2",
"prompt_3"
],
"suffix": "And explain why..."
}
try:
messages = [
{
"role": "system",
"content": 'You are a helpful assistant designed to output JSON on this format: ' + str(json_format)
},
{
"role": "user",
"content": (
'Create a question of medium difficulty for IELTS Speaking Part 2 '
'that encourages candidates to narrate a '
'personal experience or story related to the topic '
'of "' + topic + '". Include 3 prompts that '
'guide the candidate to describe '
'specific aspects of the experience, '
'such as details about the situation, '
'their actions, and the reasons it left a '
'lasting impression. Make sure that the '
'generated question does not contain '
'forbidden subjects in muslim countries.')
},
{
"role": "user",
"content": 'The prompts must not be questions. Also include a suffix like the ones in the IELTS exams '
'that start with "And explain why".'
}
]
token_count = count_total_tokens(messages)
response = make_openai_call(GPT_4_O, messages, token_count, GEN_FIELDS, GEN_QUESTION_TEMPERATURE)
response["type"] = 2
response["difficulty"] = difficulty
response["topic"] = topic
return response
return gen_speaking_part_2(topic, difficulty)
except Exception as e:
return str(e)
@@ -972,47 +762,8 @@ def get_speaking_task_3_question():
difficulty = request.args.get("difficulty", default=random.choice(difficulties))
topic = request.args.get("topic", default=random.choice(mti_topics))
json_format = {
"topic": "topic",
"questions": [
"Introductory question about the topic.",
"Follow up question about the topic",
"Follow up question about the topic",
"Follow up question about the topic",
"Follow up question about the topic"
]
}
try:
messages = [
{
"role": "system",
"content": (
'You are a helpful assistant designed to output JSON on this format: ' + str(json_format))
},
{
"role": "user",
"content": (
'Formulate a set of 5 single questions of hard difficulty for IELTS Speaking Part 3 that encourage candidates to engage in a '
'meaningful discussion on the topic of "' + topic + '". Provide inquiries, ensuring '
'they explore various aspects, perspectives, and implications related to the topic.'
'Make sure that the generated question does not contain forbidden subjects in muslim countries.')
},
{
"role": "user",
"content": 'They must be 1 single question each and not be double-barreled questions.'
}
]
token_count = count_total_tokens(messages)
response = make_openai_call(GPT_4_O, messages, token_count, GEN_FIELDS, GEN_QUESTION_TEMPERATURE)
# 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
response["difficulty"] = difficulty
response["topic"] = topic
return response
return gen_speaking_part_3(topic, difficulty)
except Exception as e:
return str(e)
@@ -1407,7 +1158,7 @@ def get_reading_passage_1_question():
topic = request.args.get('topic', default=random.choice(topics))
req_exercises = request.args.getlist('exercises')
difficulty = request.args.get("difficulty", default=random.choice(difficulties))
return gen_reading_passage_1(topic, req_exercises, difficulty)
return gen_reading_passage_1(topic, difficulty, req_exercises)
except Exception as e:
return str(e)
@@ -1420,7 +1171,7 @@ def get_reading_passage_2_question():
topic = request.args.get('topic', default=random.choice(topics))
req_exercises = request.args.getlist('exercises')
difficulty = request.args.get("difficulty", default=random.choice(difficulties))
return gen_reading_passage_2(topic, req_exercises, difficulty)
return gen_reading_passage_2(topic, difficulty, req_exercises)
except Exception as e:
return str(e)
@@ -1433,7 +1184,7 @@ def get_reading_passage_3_question():
topic = request.args.get('topic', default=random.choice(topics))
req_exercises = request.args.getlist('exercises')
difficulty = request.args.get("difficulty", default=random.choice(difficulties))
return gen_reading_passage_3(topic, req_exercises, difficulty)
return gen_reading_passage_3(topic, difficulty, req_exercises)
except Exception as e:
return str(e)
@@ -1565,6 +1316,18 @@ class CustomLevelExerciseTypes(Enum):
MULTIPLE_CHOICE_UNDERLINED = "multiple_choice_underlined"
BLANK_SPACE_TEXT = "blank_space_text"
READING_PASSAGE_UTAS = "reading_passage_utas"
WRITING_LETTER = "writing_letter"
WRITING_2 = "writing_2"
SPEAKING_1 = "speaking_1"
SPEAKING_2 = "speaking_2"
SPEAKING_3 = "speaking_3"
READING_1 = "reading_1"
READING_2 = "reading_2"
READING_3 = "reading_3"
LISTENING_1 = "listening_1"
LISTENING_2 = "listening_2"
LISTENING_3 = "listening_3"
LISTENING_4 = "listening_4"
@app.route('/custom_level', methods=['GET'])
@@ -1579,11 +1342,24 @@ def get_custom_level():
}
for i in range(1, nr_exercises + 1, 1):
exercise_type = request.args.get('exercise_' + str(i) + '_type')
exercise_difficulty = request.args.get('exercise_' + str(i) + '_difficulty',
random.choice(['easy', 'medium', 'hard']))
exercise_qty = int(request.args.get('exercise_' + str(i) + '_qty', -1))
exercise_topic = request.args.get('exercise_' + str(i) + '_topic', random.choice(topics))
exercise_topic_2 = request.args.get('exercise_' + str(i) + '_topic_2', random.choice(topics))
exercise_text_size = int(request.args.get('exercise_' + str(i) + '_text_size', 700))
exercise_sa_qty = int(request.args.get('exercise_' + str(i) + '_sa_qty', -1))
exercise_mc_qty = int(request.args.get('exercise_' + str(i) + '_mc_qty', -1))
exercise_mc3_qty = int(request.args.get('exercise_' + str(i) + '_mc3_qty', -1))
exercise_fillblanks_qty = int(request.args.get('exercise_' + str(i) + '_fillblanks_qty', -1))
exercise_writeblanks_qty = int(request.args.get('exercise_' + str(i) + '_writeblanks_qty', -1))
exercise_writeblanksquestions_qty = int(
request.args.get('exercise_' + str(i) + '_writeblanksquestions_qty', -1))
exercise_writeblanksfill_qty = int(request.args.get('exercise_' + str(i) + '_writeblanksfill_qty', -1))
exercise_writeblanksform_qty = int(request.args.get('exercise_' + str(i) + '_writeblanksform_qty', -1))
exercise_truefalse_qty = int(request.args.get('exercise_' + str(i) + '_truefalse_qty', -1))
exercise_paragraphmatch_qty = int(request.args.get('exercise_' + str(i) + '_paragraphmatch_qty', -1))
exercise_ideamatch_qty = int(request.args.get('exercise_' + str(i) + '_ideamatch_qty', -1))
if exercise_type == CustomLevelExerciseTypes.MULTIPLE_CHOICE_4.value:
response["exercises"]["exercise_" + str(i)] = {}
@@ -1597,7 +1373,7 @@ def get_custom_level():
response["exercises"]["exercise_" + str(i)]["questions"].extend(
generate_level_mc(exercise_id, qty,
response["exercises"]["exercise_" + str(i)]["questions"])["questions"])
response["exercises"]["exercise_" + str(i)]["questions"])["questions"])
exercise_id = exercise_id + qty
exercise_qty = exercise_qty - qty
@@ -1613,7 +1389,8 @@ def get_custom_level():
response["exercises"]["exercise_" + str(i)]["questions"].extend(
gen_multiple_choice_blank_space_utas(qty, exercise_id,
response["exercises"]["exercise_" + str(i)]["questions"])["questions"])
response["exercises"]["exercise_" + str(i)]["questions"])[
"questions"])
exercise_id = exercise_id + qty
exercise_qty = exercise_qty - qty
@@ -1629,7 +1406,8 @@ def get_custom_level():
response["exercises"]["exercise_" + str(i)]["questions"].extend(
gen_multiple_choice_underlined_utas(qty, exercise_id,
response["exercises"]["exercise_" + str(i)]["questions"])["questions"])
response["exercises"]["exercise_" + str(i)]["questions"])[
"questions"])
exercise_id = exercise_id + qty
exercise_qty = exercise_qty - qty
@@ -1643,9 +1421,205 @@ def get_custom_level():
exercise_mc_qty, exercise_topic)
response["exercises"]["exercise_" + str(i)]["type"] = "readingExercises"
exercise_id = exercise_id + exercise_qty
elif exercise_type == CustomLevelExerciseTypes.WRITING_LETTER.value:
response["exercises"]["exercise_" + str(i)] = gen_writing_task_1(exercise_topic, exercise_difficulty)
response["exercises"]["exercise_" + str(i)]["type"] = "writing"
exercise_id = exercise_id + 1
elif exercise_type == CustomLevelExerciseTypes.WRITING_2.value:
response["exercises"]["exercise_" + str(i)] = gen_writing_task_2(exercise_topic, exercise_difficulty)
response["exercises"]["exercise_" + str(i)]["type"] = "writing"
exercise_id = exercise_id + 1
elif exercise_type == CustomLevelExerciseTypes.SPEAKING_1.value:
response["exercises"]["exercise_" + str(i)] = (
gen_speaking_part_1(exercise_topic, exercise_topic_2, exercise_difficulty))
response["exercises"]["exercise_" + str(i)]["type"] = "interactiveSpeaking"
exercise_id = exercise_id + 1
elif exercise_type == CustomLevelExerciseTypes.SPEAKING_2.value:
response["exercises"]["exercise_" + str(i)] = gen_speaking_part_2(exercise_topic, exercise_difficulty)
response["exercises"]["exercise_" + str(i)]["type"] = "speaking"
exercise_id = exercise_id + 1
elif exercise_type == CustomLevelExerciseTypes.SPEAKING_3.value:
response["exercises"]["exercise_" + str(i)] = gen_speaking_part_3(exercise_topic, exercise_difficulty)
response["exercises"]["exercise_" + str(i)]["type"] = "interactiveSpeaking"
exercise_id = exercise_id + 1
elif exercise_type == CustomLevelExerciseTypes.READING_1.value:
exercises = []
exercise_qty_q = queue.Queue()
total_qty = 0
if exercise_fillblanks_qty != -1:
exercises.append('fillBlanks')
exercise_qty_q.put(exercise_fillblanks_qty)
total_qty = total_qty + exercise_fillblanks_qty
if exercise_writeblanks_qty != -1:
exercises.append('writeBlanks')
exercise_qty_q.put(exercise_writeblanks_qty)
total_qty = total_qty + exercise_writeblanks_qty
if exercise_truefalse_qty != -1:
exercises.append('trueFalse')
exercise_qty_q.put(exercise_truefalse_qty)
total_qty = total_qty + exercise_truefalse_qty
if exercise_paragraphmatch_qty != -1:
exercises.append('paragraphMatch')
exercise_qty_q.put(exercise_paragraphmatch_qty)
total_qty = total_qty + exercise_paragraphmatch_qty
response["exercises"]["exercise_" + str(i)] = gen_reading_passage_1(exercise_topic, exercise_difficulty,
exercises, exercise_qty_q, exercise_id)
response["exercises"]["exercise_" + str(i)]["type"] = "reading"
exercise_id = exercise_id + total_qty
elif exercise_type == CustomLevelExerciseTypes.READING_2.value:
exercises = []
exercise_qty_q = queue.Queue()
total_qty = 0
if exercise_fillblanks_qty != -1:
exercises.append('fillBlanks')
exercise_qty_q.put(exercise_fillblanks_qty)
total_qty = total_qty + exercise_fillblanks_qty
if exercise_writeblanks_qty != -1:
exercises.append('writeBlanks')
exercise_qty_q.put(exercise_writeblanks_qty)
total_qty = total_qty + exercise_writeblanks_qty
if exercise_truefalse_qty != -1:
exercises.append('trueFalse')
exercise_qty_q.put(exercise_truefalse_qty)
total_qty = total_qty + exercise_truefalse_qty
if exercise_paragraphmatch_qty != -1:
exercises.append('paragraphMatch')
exercise_qty_q.put(exercise_paragraphmatch_qty)
total_qty = total_qty + exercise_paragraphmatch_qty
response["exercises"]["exercise_" + str(i)] = gen_reading_passage_2(exercise_topic, exercise_difficulty,
exercises, exercise_qty_q, exercise_id)
response["exercises"]["exercise_" + str(i)]["type"] = "reading"
exercise_id = exercise_id + total_qty
elif exercise_type == CustomLevelExerciseTypes.READING_3.value:
exercises = []
exercise_qty_q = queue.Queue()
total_qty = 0
if exercise_fillblanks_qty != -1:
exercises.append('fillBlanks')
exercise_qty_q.put(exercise_fillblanks_qty)
total_qty = total_qty + exercise_fillblanks_qty
if exercise_writeblanks_qty != -1:
exercises.append('writeBlanks')
exercise_qty_q.put(exercise_writeblanks_qty)
total_qty = total_qty + exercise_writeblanks_qty
if exercise_truefalse_qty != -1:
exercises.append('trueFalse')
exercise_qty_q.put(exercise_truefalse_qty)
total_qty = total_qty + exercise_truefalse_qty
if exercise_paragraphmatch_qty != -1:
exercises.append('paragraphMatch')
exercise_qty_q.put(exercise_paragraphmatch_qty)
total_qty = total_qty + exercise_paragraphmatch_qty
if exercise_ideamatch_qty != -1:
exercises.append('ideaMatch')
exercise_qty_q.put(exercise_ideamatch_qty)
total_qty = total_qty + exercise_ideamatch_qty
response["exercises"]["exercise_" + str(i)] = gen_reading_passage_3(exercise_topic, exercise_difficulty,
exercises, exercise_qty_q, exercise_id)
response["exercises"]["exercise_" + str(i)]["type"] = "reading"
exercise_id = exercise_id + total_qty
elif exercise_type == CustomLevelExerciseTypes.LISTENING_1.value:
exercises = []
exercise_qty_q = queue.Queue()
total_qty = 0
if exercise_mc_qty != -1:
exercises.append('multipleChoice')
exercise_qty_q.put(exercise_mc_qty)
total_qty = total_qty + exercise_mc_qty
if exercise_writeblanksquestions_qty != -1:
exercises.append('writeBlanksQuestions')
exercise_qty_q.put(exercise_writeblanksquestions_qty)
total_qty = total_qty + exercise_writeblanksquestions_qty
if exercise_writeblanksfill_qty != -1:
exercises.append('writeBlanksFill')
exercise_qty_q.put(exercise_writeblanksfill_qty)
total_qty = total_qty + exercise_writeblanksfill_qty
if exercise_writeblanksform_qty != -1:
exercises.append('writeBlanksForm')
exercise_qty_q.put(exercise_writeblanksform_qty)
total_qty = total_qty + exercise_writeblanksform_qty
response["exercises"]["exercise_" + str(i)] = gen_listening_section_1(exercise_topic, exercise_difficulty,
exercises, exercise_qty_q,
exercise_id)
response["exercises"]["exercise_" + str(i)]["type"] = "listening"
exercise_id = exercise_id + total_qty
elif exercise_type == CustomLevelExerciseTypes.LISTENING_2.value:
exercises = []
exercise_qty_q = queue.Queue()
total_qty = 0
if exercise_mc_qty != -1:
exercises.append('multipleChoice')
exercise_qty_q.put(exercise_mc_qty)
total_qty = total_qty + exercise_mc_qty
if exercise_writeblanksquestions_qty != -1:
exercises.append('writeBlanksQuestions')
exercise_qty_q.put(exercise_writeblanksquestions_qty)
total_qty = total_qty + exercise_writeblanksquestions_qty
response["exercises"]["exercise_" + str(i)] = gen_listening_section_2(exercise_topic, exercise_difficulty,
exercises, exercise_qty_q,
exercise_id)
response["exercises"]["exercise_" + str(i)]["type"] = "listening"
exercise_id = exercise_id + total_qty
elif exercise_type == CustomLevelExerciseTypes.LISTENING_3.value:
exercises = []
exercise_qty_q = queue.Queue()
total_qty = 0
if exercise_mc3_qty != -1:
exercises.append('multipleChoice3Options')
exercise_qty_q.put(exercise_mc3_qty)
total_qty = total_qty + exercise_mc3_qty
if exercise_writeblanksquestions_qty != -1:
exercises.append('writeBlanksQuestions')
exercise_qty_q.put(exercise_writeblanksquestions_qty)
total_qty = total_qty + exercise_writeblanksquestions_qty
response["exercises"]["exercise_" + str(i)] = gen_listening_section_3(exercise_topic, exercise_difficulty,
exercises, exercise_qty_q,
exercise_id)
response["exercises"]["exercise_" + str(i)]["type"] = "listening"
exercise_id = exercise_id + total_qty
elif exercise_type == CustomLevelExerciseTypes.LISTENING_4.value:
exercises = []
exercise_qty_q = queue.Queue()
total_qty = 0
if exercise_mc_qty != -1:
exercises.append('multipleChoice')
exercise_qty_q.put(exercise_mc_qty)
total_qty = total_qty + exercise_mc_qty
if exercise_writeblanksquestions_qty != -1:
exercises.append('writeBlanksQuestions')
exercise_qty_q.put(exercise_writeblanksquestions_qty)
total_qty = total_qty + exercise_writeblanksquestions_qty
if exercise_writeblanksfill_qty != -1:
exercises.append('writeBlanksFill')
exercise_qty_q.put(exercise_writeblanksfill_qty)
total_qty = total_qty + exercise_writeblanksfill_qty
if exercise_writeblanksform_qty != -1:
exercises.append('writeBlanksForm')
exercise_qty_q.put(exercise_writeblanksform_qty)
total_qty = total_qty + exercise_writeblanksform_qty
response["exercises"]["exercise_" + str(i)] = gen_listening_section_4(exercise_topic, exercise_difficulty,
exercises, exercise_qty_q,
exercise_id)
response["exercises"]["exercise_" + str(i)]["type"] = "listening"
exercise_id = exercise_id + total_qty
return response
@app.route('/grade_short_answers', methods=['POST'])
@jwt_required()
def grade_short_answers():
@@ -1670,7 +1644,8 @@ def grade_short_answers():
},
{
"role": "user",
"content": 'Grade these answers according to the text content and write a correct answer if they are wrong. Text, questions and answers:\n ' + str(data)
"content": 'Grade these answers according to the text content and write a correct answer if they are '
'wrong. Text, questions and answers:\n ' + str(data)
}
]
@@ -1680,6 +1655,7 @@ def grade_short_answers():
except Exception as e:
return str(e)
@app.route('/fetch_tips', methods=['POST'])
@jwt_required()
def fetch_answer_tips():

View File

@@ -15,19 +15,19 @@ from helper.speech_to_text_helper import has_x_words
nltk.download('words')
def gen_reading_passage_1(topic, req_exercises, difficulty):
def gen_reading_passage_1(topic, difficulty, req_exercises, number_of_exercises_q=queue.Queue(), start_id=1):
if (len(req_exercises) == 0):
req_exercises = random.sample(READING_EXERCISE_TYPES, 2)
number_of_exercises_q = divide_number_into_parts(TOTAL_READING_PASSAGE_1_EXERCISES, len(req_exercises))
if (number_of_exercises_q.empty()):
number_of_exercises_q = divide_number_into_parts(TOTAL_READING_PASSAGE_1_EXERCISES, len(req_exercises))
passage = generate_reading_passage_1_text(topic)
if passage == "":
return gen_reading_passage_1(topic, req_exercises, difficulty)
start_id = 1
return gen_reading_passage_1(topic, difficulty, req_exercises, number_of_exercises_q, start_id)
exercises = generate_reading_exercises(passage["text"], req_exercises, number_of_exercises_q, start_id, difficulty)
if contains_empty_dict(exercises):
return gen_reading_passage_1(topic, req_exercises, difficulty)
return gen_reading_passage_1(topic, difficulty, req_exercises, number_of_exercises_q, start_id)
return {
"exercises": exercises,
"text": {
@@ -38,19 +38,19 @@ def gen_reading_passage_1(topic, req_exercises, difficulty):
}
def gen_reading_passage_2(topic, req_exercises, difficulty):
def gen_reading_passage_2(topic, difficulty, req_exercises, number_of_exercises_q=queue.Queue(), start_id=14):
if (len(req_exercises) == 0):
req_exercises = random.sample(READING_EXERCISE_TYPES, 2)
number_of_exercises_q = divide_number_into_parts(TOTAL_READING_PASSAGE_2_EXERCISES, len(req_exercises))
if (number_of_exercises_q.empty()):
number_of_exercises_q = divide_number_into_parts(TOTAL_READING_PASSAGE_2_EXERCISES, len(req_exercises))
passage = generate_reading_passage_2_text(topic)
if passage == "":
return gen_reading_passage_2(topic, req_exercises, difficulty)
start_id = 14
return gen_reading_passage_2(topic, difficulty, req_exercises, number_of_exercises_q, start_id)
exercises = generate_reading_exercises(passage["text"], req_exercises, number_of_exercises_q, start_id, difficulty)
if contains_empty_dict(exercises):
return gen_reading_passage_2(topic, req_exercises, difficulty)
return gen_reading_passage_2(topic, difficulty, req_exercises, number_of_exercises_q, start_id)
return {
"exercises": exercises,
"text": {
@@ -61,19 +61,19 @@ def gen_reading_passage_2(topic, req_exercises, difficulty):
}
def gen_reading_passage_3(topic, req_exercises, difficulty):
def gen_reading_passage_3(topic, difficulty, req_exercises, number_of_exercises_q=queue.Queue(), start_id=27):
if (len(req_exercises) == 0):
req_exercises = random.sample(READING_EXERCISE_TYPES, 2)
number_of_exercises_q = divide_number_into_parts(TOTAL_READING_PASSAGE_3_EXERCISES, len(req_exercises))
if (number_of_exercises_q.empty()):
number_of_exercises_q = divide_number_into_parts(TOTAL_READING_PASSAGE_3_EXERCISES, len(req_exercises))
passage = generate_reading_passage_3_text(topic)
if passage == "":
return gen_reading_passage_3(topic, req_exercises, difficulty)
start_id = 27
return gen_reading_passage_3(topic, difficulty, req_exercises, number_of_exercises_q, start_id)
exercises = generate_reading_exercises(passage["text"], req_exercises, number_of_exercises_q, start_id, difficulty)
if contains_empty_dict(exercises):
return gen_reading_passage_3(topic, req_exercises, difficulty)
return gen_reading_passage_3(topic, difficulty, req_exercises, number_of_exercises_q, start_id)
return {
"exercises": exercises,
"text": {
@@ -865,7 +865,8 @@ def gen_idea_match_exercise(text: str, quantity: int, start_id):
{
"role": "user",
"content": (
'From the text extract ' + str(quantity) + ' ideas, theories, opinions and who they are from. The text: ' + str(text))
'From the text extract ' + str(
quantity) + ' ideas, theories, opinions and who they are from. The text: ' + str(text))
}
]
@@ -882,6 +883,7 @@ def gen_idea_match_exercise(text: str, quantity: int, start_id):
"type": "matchSentences"
}
def build_options(ideas):
options = []
letters = iter(string.ascii_uppercase)
@@ -892,6 +894,7 @@ def build_options(ideas):
})
return options
def build_sentences(ideas, start_id):
sentences = []
letters = iter(string.ascii_uppercase)
@@ -906,6 +909,7 @@ def build_sentences(ideas, start_id):
sentence["id"] = i
return sentences
def assign_letters_to_paragraphs(paragraphs):
result = []
letters = iter(string.ascii_uppercase)
@@ -1272,7 +1276,8 @@ def replace_exercise_if_exists(all_exams, current_exercise, current_exam, seen_k
current_exercise["options"])
for exercise in exercise_dict.get("exercises", [])[0]["questions"]
):
return replace_exercise_if_exists(all_exams, generate_single_mc_level_question(), current_exam, seen_keys)
return replace_exercise_if_exists(all_exams, generate_single_mc_level_question(), current_exam,
seen_keys)
return current_exercise, seen_keys
@@ -1302,7 +1307,8 @@ def replace_blank_space_exercise_if_exists_utas(all_exams, current_exercise, cur
key = (current_exercise['prompt'], tuple(sorted(option['text'] for option in current_exercise['options'])))
# Check if the key is in the set
if key in seen_keys:
return replace_exercise_if_exists_utas(all_exams, generate_single_mc_blank_space_level_question(), current_exam, seen_keys)
return replace_exercise_if_exists_utas(all_exams, generate_single_mc_blank_space_level_question(), current_exam,
seen_keys)
else:
seen_keys.add(key)
@@ -1313,7 +1319,8 @@ def replace_blank_space_exercise_if_exists_utas(all_exams, current_exercise, cur
current_exercise["options"])
for exercise in exam.get("questions", [])
):
return replace_exercise_if_exists_utas(all_exams, generate_single_mc_blank_space_level_question(), current_exam,
return replace_exercise_if_exists_utas(all_exams, generate_single_mc_blank_space_level_question(),
current_exam,
seen_keys)
return current_exercise, seen_keys
@@ -1323,7 +1330,8 @@ def replace_underlined_exercise_if_exists_utas(all_exams, current_exercise, curr
key = (current_exercise['prompt'], tuple(sorted(option['text'] for option in current_exercise['options'])))
# Check if the key is in the set
if key in seen_keys:
return replace_exercise_if_exists_utas(all_exams, generate_single_mc_underlined_level_question(), current_exam, seen_keys)
return replace_exercise_if_exists_utas(all_exams, generate_single_mc_underlined_level_question(), current_exam,
seen_keys)
else:
seen_keys.add(key)
@@ -1334,7 +1342,8 @@ def replace_underlined_exercise_if_exists_utas(all_exams, current_exercise, curr
current_exercise["options"])
for exercise in exam.get("questions", [])
):
return replace_exercise_if_exists_utas(all_exams, generate_single_mc_underlined_level_question(), current_exam,
return replace_exercise_if_exists_utas(all_exams, generate_single_mc_underlined_level_question(),
current_exam,
seen_keys)
return current_exercise, seen_keys
@@ -1376,8 +1385,8 @@ def generate_single_mc_blank_space_level_question():
},
{
"role": "user",
"content": ('Generate 1 multiple choice blank space question of 4 options for an english level exam, it can be easy, '
'intermediate or advanced.')
"content": ('Generate 1 multiple choice blank space question of 4 options for an english level exam, '
'it can be easy, intermediate or advanced.')
}
]
@@ -1401,8 +1410,8 @@ def generate_single_mc_underlined_level_question():
},
{
"role": "user",
"content": ('Generate 1 multiple choice blank space question of 4 options for an english level exam, it can be easy, '
'intermediate or advanced.')
"content": ('Generate 1 multiple choice blank space question of 4 options for an english level exam, '
'it can be easy, intermediate or advanced.')
},
{
@@ -1469,9 +1478,9 @@ def gen_multiple_choice_blank_space_utas(quantity: int, start_id: int, all_exams
if all_exams is not None:
seen_keys = set()
for i in range(len(question["questions"])):
question["questions"][i], seen_keys = replace_blank_space_exercise_if_exists_utas(all_exams, question["questions"][i],
question,
seen_keys)
question["questions"][i], seen_keys = (
replace_blank_space_exercise_if_exists_utas(all_exams, question["questions"][i], question,
seen_keys))
response = fix_exercise_ids(question, start_id)
response["questions"] = randomize_mc_options_order(response["questions"])
return response
@@ -1546,11 +1555,9 @@ def gen_multiple_choice_underlined_utas(quantity: int, start_id: int, all_exams=
if all_exams is not None:
seen_keys = set()
for i in range(len(question["questions"])):
question["questions"][i], seen_keys = replace_underlined_exercise_if_exists_utas(all_exams,
question["questions"][
i],
question,
seen_keys)
question["questions"][i], seen_keys = (
replace_underlined_exercise_if_exists_utas(all_exams, question["questions"][i], question,
seen_keys))
response = fix_exercise_ids(question, start_id)
response["questions"] = randomize_mc_options_order(response["questions"])
return response
@@ -1765,7 +1772,8 @@ def generate_level_mc(start_id: int, quantity: int, all_questions=None):
if all_questions is not None:
seen_keys = set()
for i in range(len(question["questions"])):
question["questions"][i], seen_keys = replace_exercise_if_exists_utas(all_questions, question["questions"][i],
question["questions"][i], seen_keys = replace_exercise_if_exists_utas(all_questions,
question["questions"][i],
question,
seen_keys)
response = fix_exercise_ids(question, start_id)
@@ -1791,3 +1799,293 @@ def randomize_mc_options_order(questions):
question['solution'] = option['id']
return questions
def gen_writing_task_1(topic, difficulty):
messages = [
{
"role": "system",
"content": ('You are a helpful assistant designed to output JSON on this format: '
'{"prompt": "prompt content"}')
},
{
"role": "user",
"content": ('Craft a prompt for an IELTS Writing Task 1 General Training exercise that instructs the '
'student to compose a letter. The prompt should present a specific scenario or situation, '
'based on the topic of "' + topic + '", requiring the student to provide information, '
'advice, or instructions within the letter. '
'Make sure that the generated prompt is '
'of ' + difficulty + 'difficulty and does not contain '
'forbidden subjects in muslim '
'countries.')
},
{
"role": "user",
"content": 'The prompt should end with "In the letter you should" followed by 3 bullet points of what '
'the answer should include.'
}
]
token_count = count_total_tokens(messages)
response = make_openai_call(GPT_3_5_TURBO, messages, token_count, "prompt",
GEN_QUESTION_TEMPERATURE)
return {
"question": add_newline_before_hyphen(response["prompt"].strip()),
"difficulty": difficulty,
"topic": topic
}
def add_newline_before_hyphen(s):
return s.replace(" -", "\n-")
def gen_writing_task_2(topic, difficulty):
messages = [
{
"role": "system",
"content": ('You are a helpful assistant designed to output JSON on this format: '
'{"prompt": "prompt content"}')
},
{
"role": "user",
"content": (
'Craft a comprehensive question of ' + difficulty + 'difficulty like the ones for IELTS Writing '
'Task 2 General Training that directs the '
'candidate'
'to delve into an in-depth analysis of '
'contrasting perspectives on the topic '
'of "' + topic + '". The candidate should be '
'asked to discuss the '
'strengths and weaknesses of '
'both viewpoints.')
},
{
"role": "user",
"content": 'The question should lead to an answer with either "theories", "complicated information" or '
'be "very descriptive" on the topic.'
}
]
token_count = count_total_tokens(messages)
response = make_openai_call(GPT_4_O, messages, token_count, "prompt", GEN_QUESTION_TEMPERATURE)
return {
"question": response["prompt"].strip(),
"difficulty": difficulty,
"topic": topic
}
def gen_speaking_part_1(first_topic: str, second_topic: str, difficulty):
json_format = {
"first_topic": "topic 1",
"second_topic": "topic 2",
"questions": [
"Introductory question about the first topic, starting the topic with 'Let's talk about x' and then the "
"question.",
"Follow up question about the first topic",
"Follow up question about the first topic",
"Question about second topic",
"Follow up question about the second topic",
]
}
messages = [
{
"role": "system",
"content": (
'You are a helpful assistant designed to output JSON on this format: ' + str(json_format))
},
{
"role": "user",
"content": (
'Craft 5 simple and single questions of easy difficulty for IELTS Speaking Part 1 '
'that encourages candidates to delve deeply into '
'personal experiences, preferences, or insights on the topic '
'of "' + first_topic + '" and the topic of "' + second_topic + '". '
'Make sure that the generated '
'question'
'does not contain forbidden '
'subjects in'
'muslim countries.')
},
{
"role": "user",
"content": 'The questions should lead to the usage of 4 verb tenses (present perfect, present, '
'past and future).'
},
{
"role": "user",
"content": 'They must be 1 single question each and not be double-barreled questions.'
}
]
token_count = count_total_tokens(messages)
response = make_openai_call(GPT_4_O, messages, token_count, ["first_topic"],
GEN_QUESTION_TEMPERATURE)
response["type"] = 1
response["difficulty"] = difficulty
return response
def gen_speaking_part_2(topic: str, difficulty):
json_format = {
"topic": "topic",
"question": "question",
"prompts": [
"prompt_1",
"prompt_2",
"prompt_3"
],
"suffix": "And explain why..."
}
messages = [
{
"role": "system",
"content": 'You are a helpful assistant designed to output JSON on this format: ' + str(json_format)
},
{
"role": "user",
"content": (
'Create a question of medium difficulty for IELTS Speaking Part 2 '
'that encourages candidates to narrate a '
'personal experience or story related to the topic '
'of "' + topic + '". Include 3 prompts that '
'guide the candidate to describe '
'specific aspects of the experience, '
'such as details about the situation, '
'their actions, and the reasons it left a '
'lasting impression. Make sure that the '
'generated question does not contain '
'forbidden subjects in muslim countries.')
},
{
"role": "user",
"content": 'The prompts must not be questions. Also include a suffix like the ones in the IELTS exams '
'that start with "And explain why".'
}
]
token_count = count_total_tokens(messages)
response = make_openai_call(GPT_4_O, messages, token_count, GEN_FIELDS, GEN_QUESTION_TEMPERATURE)
response["type"] = 2
response["difficulty"] = difficulty
response["topic"] = topic
return response
def gen_speaking_part_3(topic: str, difficulty):
json_format = {
"topic": "topic",
"questions": [
"Introductory question about the topic.",
"Follow up question about the topic",
"Follow up question about the topic",
"Follow up question about the topic",
"Follow up question about the topic"
]
}
messages = [
{
"role": "system",
"content": (
'You are a helpful assistant designed to output JSON on this format: ' + str(json_format))
},
{
"role": "user",
"content": (
'Formulate a set of 5 single questions of hard difficulty for IELTS Speaking Part 3 that encourage candidates to engage in a '
'meaningful discussion on the topic of "' + topic + '". Provide inquiries, ensuring '
'they explore various aspects, perspectives, and implications related to the topic.'
'Make sure that the generated question does not contain forbidden subjects in muslim countries.')
},
{
"role": "user",
"content": 'They must be 1 single question each and not be double-barreled questions.'
}
]
token_count = count_total_tokens(messages)
response = make_openai_call(GPT_4_O, messages, token_count, GEN_FIELDS, GEN_QUESTION_TEMPERATURE)
# 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
response["difficulty"] = difficulty
response["topic"] = topic
return response
def gen_listening_section_1(topic, difficulty, req_exercises, number_of_exercises_q=queue.Queue(), start_id=1):
if (len(req_exercises) == 0):
req_exercises = random.sample(LISTENING_1_EXERCISE_TYPES, 1)
if (number_of_exercises_q.empty()):
number_of_exercises_q = divide_number_into_parts(TOTAL_LISTENING_SECTION_1_EXERCISES, len(req_exercises))
processed_conversation = generate_listening_1_conversation(topic)
exercises = generate_listening_conversation_exercises(parse_conversation(processed_conversation),
req_exercises,
number_of_exercises_q,
start_id, difficulty)
return {
"exercises": exercises,
"text": processed_conversation,
"difficulty": difficulty
}
def gen_listening_section_2(topic, difficulty, req_exercises, number_of_exercises_q=queue.Queue(), start_id=11):
if (len(req_exercises) == 0):
req_exercises = random.sample(LISTENING_2_EXERCISE_TYPES, 2)
if (number_of_exercises_q.empty()):
number_of_exercises_q = divide_number_into_parts(TOTAL_LISTENING_SECTION_2_EXERCISES, len(req_exercises))
monologue = generate_listening_2_monologue(topic)
exercises = generate_listening_monologue_exercises(str(monologue), req_exercises, number_of_exercises_q,
start_id, difficulty)
return {
"exercises": exercises,
"text": monologue,
"difficulty": difficulty
}
def gen_listening_section_3(topic, difficulty, req_exercises, number_of_exercises_q=queue.Queue(), start_id=21):
if (len(req_exercises) == 0):
req_exercises = random.sample(LISTENING_3_EXERCISE_TYPES, 1)
if (number_of_exercises_q.empty()):
number_of_exercises_q = divide_number_into_parts(TOTAL_LISTENING_SECTION_3_EXERCISES, len(req_exercises))
processed_conversation = generate_listening_3_conversation(topic)
exercises = generate_listening_conversation_exercises(parse_conversation(processed_conversation), req_exercises,
number_of_exercises_q,
start_id, difficulty)
return {
"exercises": exercises,
"text": processed_conversation,
"difficulty": difficulty
}
def gen_listening_section_4(topic, difficulty, req_exercises, number_of_exercises_q=queue.Queue(), start_id=31):
if (len(req_exercises) == 0):
req_exercises = random.sample(LISTENING_EXERCISE_TYPES, 2)
if (number_of_exercises_q.empty()):
number_of_exercises_q = divide_number_into_parts(TOTAL_LISTENING_SECTION_4_EXERCISES, len(req_exercises))
monologue = generate_listening_4_monologue(topic)
exercises = generate_listening_monologue_exercises(str(monologue), req_exercises, number_of_exercises_q,
start_id, difficulty)
return {
"exercises": exercises,
"text": monologue,
"difficulty": difficulty
}

View File

@@ -1,3 +1,4 @@
import json
from datetime import datetime
from logging import getLogger
@@ -24,7 +25,8 @@ class TrainingContentService:
self._logger = getLogger(__name__)
self._llm = openai
def get_tips(self, stats):
def get_tips(self, training_content):
user, stats = training_content["userID"], training_content["stats"]
exam_data, exam_map = self._sort_out_solutions(stats)
training_content = self._get_exam_details_and_tips(exam_data)
tips = self._query_kb(training_content.queries)
@@ -39,7 +41,8 @@ class TrainingContentService:
'created_at': int(datetime.now().timestamp() * 1000),
**exam_map,
**usefull_tips.dict(),
**weak_areas
**weak_areas,
"user": user
}
doc_ref = self._db.collection('training').add(training_doc)
return {
@@ -70,7 +73,6 @@ class TrainingContentService:
tips = {"tips": []}
for query in queries:
print(f"{query.category} {query.text}")
if query.category == "words":
tips["tips"].extend(
self._training_content_module.query_knowledge_base(query.text, "word_link")
@@ -104,7 +106,16 @@ class TrainingContentService:
' with sentence structure and punctuation.", the "queries" field is where you will write queries '
'for tips that will be displayed to the student, the category attribute is a collection of '
'embeddings and the text will be the text used to query the knowledge base. The categories are '
f'the following [{", ".join(self.TOOLS)}].'
f'the following [{", ".join(self.TOOLS)}]. The exam data will be a json where the key of the field '
'"exams" is the exam id, an exam can be composed of multiple modules or single modules. The student'
' will see your response so refrain from using phrasing like "The student" did x, y and z. If the '
'field "answer" in a question is an empty array "[]", then the student didn\'t answer any question '
'and you must address that in your response. Also questions aren\'t modules, the only modules are: '
'level, speaking, writing, reading and listening. The details array needs to be tailored to the '
'exam attempt, even if you receive the same exam you must treat as different exams by their id.'
'Don\'t make references to an exam by it\'s id, the GUI will handle that so the student knows '
'which is the exam your comments and summary are referencing too. Even if the student hasn\'t '
'submitted no answers for an exam, you must still fill the details structure addressing that fact.'
)
},
{
@@ -150,42 +161,68 @@ class TrainingContentService:
def _sort_out_solutions(self, stats):
grouped_stats = {}
for stat in stats:
exam_id = stat["exam"]
session_key = f'{str(stat["date"])}-{stat["user"]}'
module = stat["module"]
if module not in grouped_stats:
grouped_stats[module] = {}
if exam_id not in grouped_stats[module]:
grouped_stats[module][exam_id] = []
grouped_stats[module][exam_id].append(stat)
exam_id = stat["exam"]
if session_key not in grouped_stats:
grouped_stats[session_key] = {}
if module not in grouped_stats[session_key]:
grouped_stats[session_key][module] = {
"stats": [],
"exam_id": exam_id
}
grouped_stats[session_key][module]["stats"].append(stat)
exercises = {}
exam_map = {}
for module, exams in grouped_stats.items():
exercises[module] = {}
for exam_id, stat_group in exams.items():
exam = self._get_doc_by_id(module, exam_id)
exercises[module][exam_id] = {"date": None, "exercises": [], "score": None}
for session_key, modules in grouped_stats.items():
exercises[session_key] = {}
for module, module_stats in modules.items():
exercises[session_key][module] = {}
exam_id = module_stats["exam_id"]
if exam_id not in exercises[session_key][module]:
exercises[session_key][module][exam_id] = {"date": None, "exercises": []}
exam_total_questions = 0
exam_total_correct = 0
for stat in stat_group:
for stat in module_stats["stats"]:
exam_total_questions += stat["score"]["total"]
exam_total_correct += stat["score"]["correct"]
exercises[module][exam_id]["date"] = stat["date"]
exercises[session_key][module][exam_id]["date"] = stat["date"]
if exam_id not in exam_map:
exam_map[exam_id] = {"stat_ids": [], "score": 0}
exam_map[exam_id]["stat_ids"].append(stat["id"])
if session_key not in exam_map:
exam_map[session_key] = {"stat_ids": [], "score": 0}
exam_map[session_key]["stat_ids"].append(stat["id"])
exam = self._get_doc_by_id(module, exam_id)
if module == "listening":
exercises[module][exam_id]["exercises"].extend(self._get_listening_solutions(stat, exam))
if module == "reading":
exercises[module][exam_id]["exercises"].extend(self._get_reading_solutions(stat, exam))
if module == "writing":
exercises[module][exam_id]["exercises"].extend(self._get_writing_prompts_and_answers(stat, exam))
exercises[session_key][module][exam_id]["exercises"].extend(
self._get_listening_solutions(stat, exam))
elif module == "reading":
exercises[session_key][module][exam_id]["exercises"].extend(
self._get_reading_solutions(stat, exam))
elif module == "writing":
exercises[session_key][module][exam_id]["exercises"].extend(
self._get_writing_prompts_and_answers(stat, exam)
)
elif module == "speaking":
exercises[session_key][module][exam_id]["exercises"].extend(
self._get_speaking_solutions(stat, exam)
)
elif module == "level":
exercises[session_key][module][exam_id]["exercises"].extend(
self._get_level_solutions(stat, exam)
)
exam_map[exam_id]["score"] = round((exam_total_correct / exam_total_questions) * 100)
exam_map[exam_id]["module"] = module
return exercises, exam_map
exam_map[session_key]["score"] = round((exam_total_correct / exam_total_questions) * 100)
exam_map[session_key]["module"] = module
with open('exam_result.json', 'w') as file:
json.dump({"exams": exercises}, file, indent=4)
return {"exams": exercises}, exam_map
def _get_writing_prompts_and_answers(self, stat, exam):
result = []
@@ -211,6 +248,54 @@ class TrainingContentService:
return result
@staticmethod
def _get_mc_question(exercise, stat):
shuffle_maps = stat.get("shuffleMaps", [])
answer = stat["solutions"] if len(shuffle_maps) == 0 else []
if len(shuffle_maps) != 0:
for solution in stat["solutions"]:
shuffle_map = [
item["map"] for item in shuffle_maps
if item["questionID"] == solution["question"]
]
answer.append({
"question": solution["question"],
"option": shuffle_map[solution["option"]]
})
return {
"question": exercise["prompt"],
"exercise": exercise["questions"],
"answer": stat["solutions"]
}
@staticmethod
def _swap_key_name(d, original_key, new_key):
d[new_key] = d.pop(original_key)
return d
def _get_level_solutions(self, stat, exam):
result = []
try:
for part in exam["parts"]:
for exercise in part["exercises"]:
if exercise["id"] == stat["exercise"]:
if stat["type"] == "fillBlanks":
result.append({
"prompt": exercise["prompt"],
"template": exercise["text"],
"words": exercise["words"],
"solutions": exercise["solutions"],
"answer": [
self._swap_key_name(item, 'solution', 'option')
for item in stat["solutions"]
]
})
elif stat["type"] == "multipleChoice":
result.append(self._get_mc_question(exercise, stat))
except KeyError as e:
self._logger.warning(f"Malformed stat object: {str(e)}")
return result
def _get_listening_solutions(self, stat, exam):
result = []
try:
@@ -224,16 +309,54 @@ class TrainingContentService:
"solution": exercise["solutions"],
"answer": stat["solutions"]
})
if stat["type"] == "multipleChoice":
elif stat["type"] == "fillBlanks":
result.append({
"question": exercise["prompt"],
"exercise": exercise["questions"],
"template": exercise["text"],
"words": exercise["words"],
"solutions": exercise["solutions"],
"answer": stat["solutions"]
})
elif stat["type"] == "multipleChoice":
result.append(self._get_mc_question(exercise, stat))
except KeyError as e:
self._logger.warning(f"Malformed stat object: {str(e)}")
return result
@staticmethod
def _find_shuffle_map(shuffle_maps, question_id):
return next((item["map"] for item in shuffle_maps if item["questionID"] == question_id), None)
def _get_speaking_solutions(self, stat, exam):
result = {}
try:
result = {
"comments": {
key: value['comment'] for key, value in stat['solutions'][0]['evaluation']['task_response'].items()}
,
"exercises": {}
}
for exercise in exam["exercises"]:
if exercise["id"] == stat["exercise"]:
if stat["type"] == "interactiveSpeaking":
for i in range(len(exercise["prompts"])):
result["exercises"][f"exercise_{i+1}"] = {
"question": exercise["prompts"][i]["text"]
}
for i in range(len(exercise["prompts"])):
answer = stat['solutions'][0]["evaluation"].get(f'transcript_{i+1}', '')
result["exercises"][f"exercise_{i+1}"]["answer"] = answer
elif stat["type"] == "speaking":
result["exercises"]["exercise_1"] = {
"question": exercise["text"],
"answer": stat['solutions'][0]["evaluation"].get(f'transcript', '')
}
except KeyError as e:
self._logger.warning(f"Malformed stat object: {str(e)}")
return [result]
def _get_reading_solutions(self, stat, exam):
result = []
try:
@@ -258,8 +381,13 @@ class TrainingContentService:
"solutions": exercise["solutions"],
"answer": stat["solutions"]
})
else:
# match_sentences
elif stat["type"] == "trueFalse":
result.append({
"text": text,
"questions": exercise["questions"],
"answer": stat["solutions"]
})
elif stat["type"] == "matchSentences":
result.append({
"text": text,
"question": exercise["prompt"],

View File

@@ -36,7 +36,7 @@ class UploadLevelService:
FileHelper.remove_directory(f'./tmp/{path_id}')
if response:
return response.dict(exclude_none=True)
return self.fix_ids(response.dict(exclude_none=True))
return None
@staticmethod
@@ -378,3 +378,18 @@ class UploadLevelService:
)
}
@staticmethod
def fix_ids(response):
counter = 1
for part in response["parts"]:
for exercise in part["exercises"]:
if exercise["type"] == "multipleChoice":
for question in exercise["questions"]:
question["id"] = counter
counter += 1
if exercise["type"] == "fillBlanks":
for i in range(len(exercise["words"])):
exercise["words"][i]["id"] = counter
exercise["solutions"][i]["id"] = counter
counter += 1
return response