From 9bc06d83407cffef5c0853276f6056e701b4295b Mon Sep 17 00:00:00 2001 From: Cristiano Ferreira Date: Mon, 10 Jun 2024 19:30:41 +0100 Subject: [PATCH 1/4] Start on level exam for utas. --- app.py | 15 ++++++++++++++ helper/exercises.py | 50 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/app.py b/app.py index 9b22d2e..6f02d4f 100644 --- a/app.py +++ b/app.py @@ -1013,6 +1013,21 @@ def get_level_exam(): except Exception as e: return str(e) +@app.route('/level_utas', method=['GET']) +@jwt_required() +def get_level_utas(): + try: + number_of_exercises = 45 + mc_exercises = gen_multiple_choice_level(number_of_exercises) + return { + "exercises": [mc_exercises], + "isDiagnostic": False, + "minTimer": 25, + "module": "level" + } + except Exception as e: + return str(e) + @app.route('/fetch_tips', methods=['POST']) @jwt_required() diff --git a/helper/exercises.py b/helper/exercises.py index c009e2d..c379417 100644 --- a/helper/exercises.py +++ b/helper/exercises.py @@ -1036,7 +1036,7 @@ def gen_multiple_choice_level(quantity: int, start_id=1): ["questions"], GEN_QUESTION_TEMPERATURE) - if len(question["questions"]) != 25: + if len(question["questions"]) != quantity: return gen_multiple_choice_level(quantity, start_id) else: all_exams = get_all("level") @@ -1063,12 +1063,11 @@ def replace_exercise_if_exists(all_exams, current_exercise, current_exam, seen_k seen_keys.add(key) for exam in all_exams: - exam_dict = exam.to_dict() if any( exercise["prompt"] == current_exercise["prompt"] and any(exercise["options"][0]["text"] == current_option["text"] for current_option in current_exercise["options"]) - for exercise in exam_dict.get("exercises", [])[0]["questions"] + for exercise in exam.get("questions", []) ): return replace_exercise_if_exists(all_exams, generate_single_mc_level_question(), current_exam, seen_keys) return current_exercise, seen_keys @@ -1109,3 +1108,48 @@ def parse_conversation(conversation_data): readable_text.append(f"{name}: {text}") return "\n".join(readable_text) + + +def gen_multiple_choice_blank_space_utas(quantity: int, start_id: int, all_exams): + gen_multiple_choice_for_text = "Generate " + str( + quantity) + " multiple choice blank space questions of 4 options for an english level exam, some easy questions, some intermediate " \ + "questions and some advanced questions. Ensure that the questions cover a range of topics such as " \ + "verb tense, subject-verb agreement, pronoun usage, sentence structure, and punctuation. Make sure " \ + "every question only has 1 correct answer." + + messages = [ + { + "role": "system", + "content": ( + 'You are a helpful assistant designed to output JSON on this format: {"questions": [{"id": "9", "options": ' + '[{"id": "A", "text": ' + '"And"}, {"id": "B", "text": "Cat"}, {"id": "C", "text": ' + '"Happy"}, {"id": "D", "text": "Jump"}], ' + '"prompt": "Which of the following is a conjunction?", ' + '"solution": "A", "variant": "text"}]}') + }, + { + "role": "user", + "content": gen_multiple_choice_for_text + } + ] + + token_count = count_total_tokens(messages) + question = make_openai_call(GPT_4_O, messages, token_count, + ["questions"], + GEN_QUESTION_TEMPERATURE) + + if len(question["questions"]) != quantity: + return gen_multiple_choice_level(quantity, start_id) + else: + seen_keys = set() + for i in range(len(question["questions"])): + question["questions"][i], seen_keys = replace_exercise_if_exists(all_exams, question["questions"][i], + question, + seen_keys) + return { + "id": str(uuid.uuid4()), + "prompt": "Select the appropriate option.", + "questions": fix_exercise_ids(question, start_id)["questions"], + "type": "multipleChoice", + } From 7633822916fbded4690c5ed7601179a61f103429 Mon Sep 17 00:00:00 2001 From: Cristiano Ferreira Date: Wed, 12 Jun 2024 23:10:55 +0100 Subject: [PATCH 2/4] Add exercises for utas level. --- helper/exercises.py | 238 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 238 insertions(+) diff --git a/helper/exercises.py b/helper/exercises.py index c379417..59db409 100644 --- a/helper/exercises.py +++ b/helper/exercises.py @@ -1153,3 +1153,241 @@ def gen_multiple_choice_blank_space_utas(quantity: int, start_id: int, all_exams "questions": fix_exercise_ids(question, start_id)["questions"], "type": "multipleChoice", } + + +def gen_multiple_choice_underlined_utas(quantity: int, start_id: int): + json_format = { + "questions": [ + { + "id": "9", + "options": [ + { + "id": "A", + "text": "a" + }, + { + "id": "B", + "text": "b" + }, + { + "id": "C", + "text": "c" + }, + { + "id": "D", + "text": "d" + } + ], + "prompt": "prompt", + "solution": "A", + "variant": "text" + } + ] + } + + gen_multiple_choice_for_text = 'Generate ' + str(quantity) + (' multiple choice questions of 4 options for an english ' + 'level exam, some easy questions, some intermediate ' + 'questions and some advanced questions.Ensure that ' + 'the questions cover a range of topics such as verb ' + 'tense, subject-verb agreement, pronoun usage, ' + 'sentence structure, and punctuation. Make sure ' + 'every question only has 1 correct answer.') + + messages = [ + { + "role": "system", + "content": 'You are a helpful assistant designed to output JSON on this format: ' + str(json_format) + }, + { + "role": "user", + "content": gen_multiple_choice_for_text + }, + { + "role": "user", + "content": ( + 'The type of multiple choice is the prompt has wrong words or group of words and the options are to ' + 'find the wrong word or group of words that are underlined in the prompt. \nExample:\n' + 'Prompt: "I complain about my boss all the time, but my colleagues thinks the boss is nice."\n' + 'Options:\na: "complain"\nb: "all the time"\nc: "thinks"\nd: "is"') + } + ] + + token_count = count_total_tokens(messages) + question = make_openai_call(GPT_4_O, messages, token_count, + ["questions"], + GEN_QUESTION_TEMPERATURE) + + if len(question["questions"]) != quantity: + return gen_multiple_choice_level(quantity, start_id) + else: + return { + "id": str(uuid.uuid4()), + "prompt": "Select the appropriate option.", + "questions": fix_exercise_ids(question, start_id)["questions"], + "type": "multipleChoice", + } + +def gen_blank_space_text_utas(quantity: int, start_id: int, size: int, topic=random.choice(mti_topics)): + json_format = { + "question": { + "words": [ + { + "id": "1", + "text": "a" + }, + { + "id": "2", + "text": "b" + }, + { + "id": "3", + "text": "c" + }, + { + "id": "4", + "text": "d" + } + ], + "text": "text" + } + } + gen_text = 'Generate a text of at least ' + str(size) + ' words about the topic ' + topic + '.' + + messages = [ + { + "role": "system", + "content": 'You are a helpful assistant designed to output JSON on this format: ' + str(json_format) + }, + { + "role": "user", + "content": gen_text + }, + { + "role": "user", + "content": ( + 'From the generated text choose ' + str(quantity) + ' words (cannot be sequential words) to replace ' + 'once with {{id}} where id starts on ' + str(start_id) + ' and is ' + 'incremented for each word. The ids must be ordered throughout the text and the words must be ' + 'replaced only once. Put the removed words and respective ids on the words array of the json in the correct order.') + } + ] + + token_count = count_total_tokens(messages) + question = make_openai_call(GPT_4_O, messages, token_count, + ["question"], + GEN_QUESTION_TEMPERATURE) + + return { + "id": str(uuid.uuid4()), + "prompt": "Select the appropriate option.", + "questions": question["question"], + "type": "blankSpace" + } + +def gen_reading_passage_utas(start_id, sa_quantity: int, mc_quantity: int, topic=random.choice(mti_topics)): + + passage = generate_reading_passage(QuestionType.READING_PASSAGE_1, topic) + exercises = gen_reading_exercises_utas(passage["text"], start_id, sa_quantity, mc_quantity) + if contains_empty_dict(exercises): + return gen_reading_passage_utas(start_id, sa_quantity, mc_quantity, topic) + return { + "exercises": exercises, + "text": { + "content": passage["text"], + "title": passage["title"] + } + } + +def gen_reading_exercises_utas(passage: str, start_id: int, sa_quantity: int, mc_quantity: int): + exercises = [] + + sa_questions = gen_short_answer_utas(passage, start_id, sa_quantity) + exercises.append(sa_questions) + mc_questions = gen_text_multiple_choice_utas(passage, start_id+sa_quantity, mc_quantity) + exercises.append(mc_questions) + + return exercises + +def gen_short_answer_utas(text: str, start_id: int, sa_quantity: int): + json_format = {"questions": [{"id": 1, "question": "question", "possible_answers": ["answer_1", "answer_2"]}]} + + messages = [ + { + "role": "system", + "content": 'You are a helpful assistant designed to output JSON on this format: ' + str(json_format) + }, + { + "role": "user", + "content": ( + 'Generate ' + str(sa_quantity) + ' short answer questions, and the possible answers, must have ' + 'maximum 3 words per answer, about this text:\n"' + text + '"') + }, + { + "role": "user", + "content": 'The id starts at ' + str(start_id) + '.' + } + ] + + token_count = count_total_tokens(messages) + return make_openai_call(GPT_4_O, messages, token_count, + ["questions"], + GEN_QUESTION_TEMPERATURE) +def gen_text_multiple_choice_utas(text: str, start_id: int, mc_quantity: int): + json_format = { + "questions": [ + { + "id": "9", + "options": [ + { + "id": "A", + "text": "a" + }, + { + "id": "B", + "text": "b" + }, + { + "id": "C", + "text": "c" + }, + { + "id": "D", + "text": "d" + } + ], + "prompt": "prompt", + "solution": "A", + "variant": "text" + } + ] + } + + messages = [ + { + "role": "system", + "content": 'You are a helpful assistant designed to output JSON on this format: ' + str(json_format) + }, + { + "role": "user", + "content": 'Generate ' + str(mc_quantity) + ' multiple choice questions of 4 options for this text:\n' + text + }, + { + "role": "user", + "content": 'Make sure every question only has 1 correct answer.' + } + ] + + token_count = count_total_tokens(messages) + question = make_openai_call(GPT_4_O, messages, token_count, + ["questions"], + GEN_QUESTION_TEMPERATURE) + + if len(question["questions"]) != mc_quantity: + return gen_multiple_choice_level(mc_quantity, start_id) + else: + return { + "id": str(uuid.uuid4()), + "prompt": "Select the appropriate option.", + "questions": fix_exercise_ids(question, start_id)["questions"], + "type": "multipleChoice", + } \ No newline at end of file From 1d110d5fa95ad0e9f1d9b43f6c16ec7d59fd2519 Mon Sep 17 00:00:00 2001 From: Cristiano Ferreira Date: Thu, 13 Jun 2024 18:24:42 +0100 Subject: [PATCH 3/4] Add exercises for utas level. --- app.py | 94 +++++++++++++++++++++++++++++++++++++++++++-- helper/constants.py | 2 - helper/exercises.py | 50 ++++++------------------ 3 files changed, 101 insertions(+), 45 deletions(-) diff --git a/app.py b/app.py index 6f02d4f..684a422 100644 --- a/app.py +++ b/app.py @@ -1013,14 +1013,100 @@ def get_level_exam(): except Exception as e: return str(e) -@app.route('/level_utas', method=['GET']) +@app.route('/level_utas', methods=['GET']) @jwt_required() def get_level_utas(): try: - number_of_exercises = 45 - mc_exercises = gen_multiple_choice_level(number_of_exercises) + # Formats + mc = { + "id": str(uuid.uuid4()), + "prompt": "Choose the correct word or group of words that completes the sentences.", + "questions": None, + "type": "multipleChoice", + "part": 1 + } + + umc = { + "id": str(uuid.uuid4()), + "prompt": "Choose the underlined word or group of words that is not correct.", + "questions": None, + "type": "multipleChoice", + "part": 2 + } + + bs_1 = { + "id": str(uuid.uuid4()), + "prompt": "Read the text and write the correct word for each space.", + "questions": None, + "type": "blankSpaceText", + "part": 3 + } + + bs_2 = { + "id": str(uuid.uuid4()), + "prompt": "Read the text and write the correct word for each space.", + "questions": None, + "type": "blankSpaceText", + "part": 4 + } + + reading = { + "id": str(uuid.uuid4()), + "prompt": "Read the text and answer the questions below.", + "questions": None, + "type": "readingExercises", + "part": 5 + } + + all_mc_questions = [] + + # PART 1 + mc_exercises1 = gen_multiple_choice_blank_space_utas(15, 1, all_mc_questions) + print(json.dumps(mc_exercises1, indent=4)) + all_mc_questions.append(mc_exercises1) + + # PART 2 + mc_exercises2 = gen_multiple_choice_blank_space_utas(15, 16, all_mc_questions) + print(json.dumps(mc_exercises2, indent=4)) + all_mc_questions.append(mc_exercises2) + + # PART 3 + mc_exercises3 = gen_multiple_choice_blank_space_utas(15, 31, all_mc_questions) + print(json.dumps(mc_exercises3, indent=4)) + all_mc_questions.append(mc_exercises3) + + mc_exercises = mc_exercises1['questions'] + mc_exercises2['questions'] + mc_exercises3['questions'] + print(json.dumps(mc_exercises, indent=4)) + mc["questions"] = mc_exercises + + # Underlined mc + underlined_mc = gen_multiple_choice_underlined_utas(15, 46) + print(json.dumps(underlined_mc, indent=4)) + umc["questions"] = underlined_mc + + # Blank Space text 1 + blank_space_text_1 = gen_blank_space_text_utas(12, 61, 250) + print(json.dumps(blank_space_text_1, indent=4)) + bs_1["questions"] = blank_space_text_1 + + # Blank Space text 2 + blank_space_text_2 = gen_blank_space_text_utas(14, 73, 350) + print(json.dumps(blank_space_text_2, indent=4)) + bs_2["questions"] = blank_space_text_2 + + # Reading text + reading_text = gen_reading_passage_utas(87, 10, 4) + print(json.dumps(reading_text, indent=4)) + reading["questions"] = reading_text + return { - "exercises": [mc_exercises], + "exercises": { + "blankSpaceMultipleChoice": mc, + "underlinedMultipleChoice": umc, + "blankSpaceText1": bs_1, + "blankSpaceText2": bs_2, + "readingExercises": reading, + }, "isDiagnostic": False, "minTimer": 25, "module": "level" diff --git a/helper/constants.py b/helper/constants.py index 7fcc092..c5f924c 100644 --- a/helper/constants.py +++ b/helper/constants.py @@ -141,7 +141,6 @@ mti_topics = [ "Poverty Alleviation", "Cybersecurity and Privacy", "Human Rights", - "Social Justice", "Food and Agriculture", "Cyberbullying and Online Safety", "Linguistic Diversity", @@ -232,7 +231,6 @@ topics = [ "Meditation Practices", "Literary Symbolism", "Marine Conservation", - "Social Justice Movements", "Sustainable Tourism", "Ancient Philosophy", "Cold War Era", diff --git a/helper/exercises.py b/helper/exercises.py index 59db409..1365d78 100644 --- a/helper/exercises.py +++ b/helper/exercises.py @@ -1147,12 +1147,7 @@ def gen_multiple_choice_blank_space_utas(quantity: int, start_id: int, all_exams question["questions"][i], seen_keys = replace_exercise_if_exists(all_exams, question["questions"][i], question, seen_keys) - return { - "id": str(uuid.uuid4()), - "prompt": "Select the appropriate option.", - "questions": fix_exercise_ids(question, start_id)["questions"], - "type": "multipleChoice", - } + return fix_exercise_ids(question, start_id) def gen_multiple_choice_underlined_utas(quantity: int, start_id: int): @@ -1220,12 +1215,7 @@ def gen_multiple_choice_underlined_utas(quantity: int, start_id: int): if len(question["questions"]) != quantity: return gen_multiple_choice_level(quantity, start_id) else: - return { - "id": str(uuid.uuid4()), - "prompt": "Select the appropriate option.", - "questions": fix_exercise_ids(question, start_id)["questions"], - "type": "multipleChoice", - } + return fix_exercise_ids(question, start_id)["questions"] def gen_blank_space_text_utas(quantity: int, start_id: int, size: int, topic=random.choice(mti_topics)): json_format = { @@ -1277,37 +1267,24 @@ def gen_blank_space_text_utas(quantity: int, start_id: int, size: int, topic=ran ["question"], GEN_QUESTION_TEMPERATURE) - return { - "id": str(uuid.uuid4()), - "prompt": "Select the appropriate option.", - "questions": question["question"], - "type": "blankSpace" - } + return question["question"] def gen_reading_passage_utas(start_id, sa_quantity: int, mc_quantity: int, topic=random.choice(mti_topics)): passage = generate_reading_passage(QuestionType.READING_PASSAGE_1, topic) - exercises = gen_reading_exercises_utas(passage["text"], start_id, sa_quantity, mc_quantity) - if contains_empty_dict(exercises): - return gen_reading_passage_utas(start_id, sa_quantity, mc_quantity, topic) + short_answer = gen_short_answer_utas(passage["text"], start_id, sa_quantity) + mc_exercises = gen_text_multiple_choice_utas(passage["text"], start_id+sa_quantity, mc_quantity) return { - "exercises": exercises, + "exercises": { + "shortAnswer":short_answer, + "multipleChoice": mc_exercises, + }, "text": { "content": passage["text"], "title": passage["title"] } } -def gen_reading_exercises_utas(passage: str, start_id: int, sa_quantity: int, mc_quantity: int): - exercises = [] - - sa_questions = gen_short_answer_utas(passage, start_id, sa_quantity) - exercises.append(sa_questions) - mc_questions = gen_text_multiple_choice_utas(passage, start_id+sa_quantity, mc_quantity) - exercises.append(mc_questions) - - return exercises - def gen_short_answer_utas(text: str, start_id: int, sa_quantity: int): json_format = {"questions": [{"id": 1, "question": "question", "possible_answers": ["answer_1", "answer_2"]}]} @@ -1331,7 +1308,7 @@ def gen_short_answer_utas(text: str, start_id: int, sa_quantity: int): token_count = count_total_tokens(messages) return make_openai_call(GPT_4_O, messages, token_count, ["questions"], - GEN_QUESTION_TEMPERATURE) + GEN_QUESTION_TEMPERATURE)["questions"] def gen_text_multiple_choice_utas(text: str, start_id: int, mc_quantity: int): json_format = { "questions": [ @@ -1385,9 +1362,4 @@ def gen_text_multiple_choice_utas(text: str, start_id: int, mc_quantity: int): if len(question["questions"]) != mc_quantity: return gen_multiple_choice_level(mc_quantity, start_id) else: - return { - "id": str(uuid.uuid4()), - "prompt": "Select the appropriate option.", - "questions": fix_exercise_ids(question, start_id)["questions"], - "type": "multipleChoice", - } \ No newline at end of file + return fix_exercise_ids(question, start_id)["questions"] \ No newline at end of file From 20dfd5be7883c34961eb5980c07fdea056632b4d Mon Sep 17 00:00:00 2001 From: Cristiano Ferreira Date: Thu, 13 Jun 2024 18:30:58 +0100 Subject: [PATCH 4/4] Add exercises for utas level. --- helper/exercises.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/helper/exercises.py b/helper/exercises.py index 1365d78..1d05bee 100644 --- a/helper/exercises.py +++ b/helper/exercises.py @@ -1062,6 +1062,26 @@ def replace_exercise_if_exists(all_exams, current_exercise, current_exam, seen_k else: seen_keys.add(key) + for exam in all_exams: + exam_dict = exam.to_dict() + if any( + exercise["prompt"] == current_exercise["prompt"] and + any(exercise["options"][0]["text"] == current_option["text"] for current_option in + current_exercise["options"]) + for exercise in exam_dict.get("exercises", [])[0]["questions"] + ): + return replace_exercise_if_exists(all_exams, generate_single_mc_level_question(), current_exam, seen_keys) + return current_exercise, seen_keys + +def replace_exercise_if_exists_utas(all_exams, current_exercise, current_exam, seen_keys): + # Extracting relevant fields for comparison + 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_level_question(), current_exam, seen_keys) + else: + seen_keys.add(key) + for exam in all_exams: if any( exercise["prompt"] == current_exercise["prompt"] and @@ -1069,7 +1089,7 @@ def replace_exercise_if_exists(all_exams, current_exercise, current_exam, seen_k current_exercise["options"]) for exercise in exam.get("questions", []) ): - return replace_exercise_if_exists(all_exams, generate_single_mc_level_question(), current_exam, seen_keys) + return replace_exercise_if_exists_utas(all_exams, generate_single_mc_level_question(), current_exam, seen_keys) return current_exercise, seen_keys @@ -1144,7 +1164,7 @@ def gen_multiple_choice_blank_space_utas(quantity: int, start_id: int, all_exams else: seen_keys = set() for i in range(len(question["questions"])): - question["questions"][i], seen_keys = replace_exercise_if_exists(all_exams, question["questions"][i], + question["questions"][i], seen_keys = replace_exercise_if_exists_utas(all_exams, question["questions"][i], question, seen_keys) return fix_exercise_ids(question, start_id)