From 12bec14c92fa8e943c74d9f2906b1612c18ac5de Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Mon, 16 Oct 2023 19:52:36 +0530 Subject: [PATCH] feat: quiz validations and marks --- .../doctype/lms_question/lms_question.json | 26 ++++++++-------- lms/lms/doctype/lms_quiz/lms_quiz.json | 25 ++++++++++++++- lms/lms/doctype/lms_quiz/lms_quiz.py | 21 ++++++++++--- .../lms_quiz_question/lms_quiz_question.json | 16 ++++++++-- .../lms_quiz_result/lms_quiz_result.json | 23 ++++++++++++-- lms/patches.txt | 3 +- lms/patches/v1_0/add_default_marks.py | 16 ++++++++++ lms/patches/v1_0/create_quiz_questions.py | 31 +++++++------------ lms/plugins.py | 23 +++++++++++++- lms/templates/quiz/quiz.html | 3 +- 10 files changed, 140 insertions(+), 47 deletions(-) create mode 100644 lms/patches/v1_0/add_default_marks.py diff --git a/lms/lms/doctype/lms_question/lms_question.json b/lms/lms/doctype/lms_question/lms_question.json index c91f4937..a7852390 100644 --- a/lms/lms/doctype/lms_question/lms_question.json +++ b/lms/lms/doctype/lms_question/lms_question.json @@ -32,11 +32,11 @@ "column_break_lknb", "explanation_4", "section_break_hkfe", - "possible_answer_1", - "possible_answer_3", + "possibility_1", + "possibility_3", "column_break_wpjr", - "possible_answer_2", - "possible_answer_4" + "possibility_2", + "possibility_4" ], "fields": [ { @@ -167,34 +167,34 @@ "fieldtype": "Section Break" }, { - "fieldname": "possible_answer_1", + "fieldname": "column_break_wpjr", + "fieldtype": "Column Break" + }, + { + "fieldname": "possibility_1", "fieldtype": "Small Text", "label": "Possible Answer 1", "mandatory_depends_on": "eval: doc.type == 'User Input'" }, { - "fieldname": "possible_answer_3", + "fieldname": "possibility_3", "fieldtype": "Small Text", "label": "Possible Answer 3" }, { - "fieldname": "column_break_wpjr", - "fieldtype": "Column Break" - }, - { - "fieldname": "possible_answer_2", + "fieldname": "possibility_2", "fieldtype": "Small Text", "label": "Possible Answer 2" }, { - "fieldname": "possible_answer_4", + "fieldname": "possibility_4", "fieldtype": "Small Text", "label": "Possible Answer 4" } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2023-10-10 16:03:38.776125", + "modified": "2023-10-16 11:39:39.757008", "modified_by": "Administrator", "module": "LMS", "name": "LMS Question", diff --git a/lms/lms/doctype/lms_quiz/lms_quiz.json b/lms/lms/doctype/lms_quiz/lms_quiz.json index dbbea28b..c5d97764 100644 --- a/lms/lms/doctype/lms_quiz/lms_quiz.json +++ b/lms/lms/doctype/lms_quiz/lms_quiz.json @@ -12,6 +12,10 @@ "column_break_gaac", "max_attempts", "show_submission_history", + "section_break_hsiv", + "passing_percentage", + "column_break_rocd", + "total_marks", "section_break_sbjx", "questions", "section_break_3", @@ -90,11 +94,30 @@ "fieldname": "show_submission_history", "fieldtype": "Check", "label": "Show Submission History" + }, + { + "fieldname": "section_break_hsiv", + "fieldtype": "Section Break" + }, + { + "fieldname": "passing_percentage", + "fieldtype": "Int", + "label": "Passing Percentage" + }, + { + "fieldname": "column_break_rocd", + "fieldtype": "Column Break" + }, + { + "fieldname": "total_marks", + "fieldtype": "Int", + "label": "Total Marks", + "read_only": 1 } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2023-07-04 15:26:24.457745", + "modified": "2023-10-16 17:21:33.932981", "modified_by": "Administrator", "module": "LMS", "name": "LMS Quiz", diff --git a/lms/lms/doctype/lms_quiz/lms_quiz.py b/lms/lms/doctype/lms_quiz/lms_quiz.py index d91490f8..0f2ef9db 100644 --- a/lms/lms/doctype/lms_quiz/lms_quiz.py +++ b/lms/lms/doctype/lms_quiz/lms_quiz.py @@ -5,7 +5,7 @@ import json import frappe from frappe import _ from frappe.model.document import Document -from frappe.utils import cstr +from frappe.utils import cstr, comma_and from lms.lms.doctype.lms_question.lms_question import validate_correct_answers from lms.lms.utils import ( generate_slug, @@ -15,6 +15,17 @@ from lms.lms.utils import ( class LMSQuiz(Document): + def validate(self): + self.validate_duplicate_questions() + + def validate_duplicate_questions(self): + questions = [row.question for row in self.questions] + rows = [i + 1 for i, x in enumerate(questions) if questions.count(x) > 1] + if len(rows): + frappe.throw( + _("Rows {0} have the duplicate questions.").format(frappe.bold(comma_and(rows))) + ) + def autoname(self): if not self.name: self.name = generate_slug(self.title, "LMS Quiz") @@ -44,11 +55,13 @@ def quiz_summary(quiz, results): for result in results: correct = result["is_correct"][0] - result["question"] = frappe.db.get_value( + question_name = frappe.db.get_value( "LMS Quiz Question", {"parent": quiz, "idx": result["question_index"] + 1}, ["question"], ) + result["question_name"] = question_name + result["question"] = frappe.db.get_value("LMS Question", question_name, "question") for point in result["is_correct"]: correct = correct and point @@ -184,9 +197,7 @@ def check_choice_answers(question, answers): fields.append(f"option_{cstr(num)}") fields.append(f"is_correct_{cstr(num)}") - question_details = frappe.db.get_value( - "LMS Quiz Question", question, fields, as_dict=1 - ) + question_details = frappe.db.get_value("LMS Question", question, fields, as_dict=1) for num in range(1, 5): if question_details[f"option_{num}"] in answers: diff --git a/lms/lms/doctype/lms_quiz_question/lms_quiz_question.json b/lms/lms/doctype/lms_quiz_question/lms_quiz_question.json index 52cd5664..4be1f88e 100644 --- a/lms/lms/doctype/lms_quiz_question/lms_quiz_question.json +++ b/lms/lms/doctype/lms_quiz_question/lms_quiz_question.json @@ -5,22 +5,34 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "question" + "question", + "marks" ], "fields": [ { "fieldname": "question", "fieldtype": "Link", "in_list_view": 1, + "in_preview": 1, "label": "Question", "options": "LMS Question", "reqd": 1 + }, + { + "default": "1", + "fieldname": "marks", + "fieldtype": "Int", + "in_list_view": 1, + "in_preview": 1, + "label": "Marks", + "non_negative": 1, + "reqd": 1 } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-10-10 15:42:51.791902", + "modified": "2023-10-16 19:51:03.893143", "modified_by": "Administrator", "module": "LMS", "name": "LMS Quiz Question", diff --git a/lms/lms/doctype/lms_quiz_result/lms_quiz_result.json b/lms/lms/doctype/lms_quiz_result/lms_quiz_result.json index 93487a38..72aeaef6 100644 --- a/lms/lms/doctype/lms_quiz_result/lms_quiz_result.json +++ b/lms/lms/doctype/lms_quiz_result/lms_quiz_result.json @@ -6,8 +6,11 @@ "engine": "InnoDB", "field_order": [ "question", - "answer", - "is_correct" + "section_break_fztv", + "question_name", + "is_correct", + "column_break_flus", + "answer" ], "fields": [ { @@ -31,12 +34,26 @@ "in_list_view": 1, "label": "Is Correct", "read_only": 1 + }, + { + "fieldname": "section_break_fztv", + "fieldtype": "Section Break" + }, + { + "fieldname": "question_name", + "fieldtype": "Link", + "label": "Question Name", + "options": "LMS Question" + }, + { + "fieldname": "column_break_flus", + "fieldtype": "Column Break" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-11-24 11:15:45.931119", + "modified": "2023-10-16 15:25:03.380843", "modified_by": "Administrator", "module": "LMS", "name": "LMS Quiz Result", diff --git a/lms/patches.txt b/lms/patches.txt index 60acee23..5a28133a 100644 --- a/lms/patches.txt +++ b/lms/patches.txt @@ -73,4 +73,5 @@ lms.patches.v1_0.change_naming_for_batch_course #14-09-2023 execute:frappe.permissions.reset_perms("LMS Enrollment") lms.patches.v1_0.create_student_role lms.patches.v1_0.mark_confirmation_for_batch_students -lms.patches.v1_0.create_quiz_questions \ No newline at end of file +lms.patches.v1_0.create_quiz_questions +lms.patches.v1_0.add_default_marks #16-10-2023 \ No newline at end of file diff --git a/lms/patches/v1_0/add_default_marks.py b/lms/patches/v1_0/add_default_marks.py new file mode 100644 index 00000000..fb35a0f6 --- /dev/null +++ b/lms/patches/v1_0/add_default_marks.py @@ -0,0 +1,16 @@ +import frappe + + +def execute(): + questions = frappe.get_all("LMS Quiz Question", pluck="name") + + for question in questions: + frappe.db.set_value("LMS Quiz Question", question, "marks", 1) + + quizzes = frappe.get_all("LMS Quiz", pluck="name") + + for quiz in quizzes: + questions_count = frappe.db.count("LMS Quiz Question", {"parent": quiz}) + frappe.db.set_value( + "LMS Quiz", quiz, {"total_marks": questions_count, "passing_percentage": 100} + ) diff --git a/lms/patches/v1_0/create_quiz_questions.py b/lms/patches/v1_0/create_quiz_questions.py index c53a7c00..12779cbd 100644 --- a/lms/patches/v1_0/create_quiz_questions.py +++ b/lms/patches/v1_0/create_quiz_questions.py @@ -3,31 +3,21 @@ import frappe def execute(): frappe.reload_doc("lms", "doctype", "lms_question") - frappe.reload_doc("lms", "doctype", "lms_quiz_question") + + fields = ["name", "question", "type", "multiple"] + for num in range(1, 5): + fields.append(f"option_{num}") + fields.append(f"is_correct_{num}") + fields.append(f"explanation_{num}") + fields.append(f"possibility_{num}") questions = frappe.get_all( "LMS Quiz Question", - fields=[ - "name", - "question", - "type", - "multiple", - "option_1", - "is_correct_1", - "explanation_1", - "option_2", - "is_correct_2", - "explanation_2", - "option_3", - "is_correct_3", - "explanation_3", - "option_4", - "is_correct_4", - "explanation_4", - ], + fields=fields, ) for question in questions: + print(question.name) doc = frappe.new_doc("LMS Question") doc.update( { @@ -44,9 +34,10 @@ def execute(): f"option_{num}": question[f"option_{num}"], f"is_correct_{num}": question[f"is_correct_{num}"], f"explanation_{num}": question[f"explanation_{num}"], + f"possibility_{num}": question[f"possibility_{num}"], } ) doc.save() - + print(doc.name) frappe.db.set_value("LMS Quiz Question", question.name, "question", doc.name) diff --git a/lms/plugins.py b/lms/plugins.py index b0f40cd2..00569484 100644 --- a/lms/plugins.py +++ b/lms/plugins.py @@ -109,7 +109,28 @@ def quiz_renderer(quiz_name): ) +"" - quiz = frappe.get_doc("LMS Quiz", quiz_name) + quiz = frappe.db.get_value( + "LMS Quiz", + quiz_name, + ["name", "title", "max_attempts", "show_answers", "show_submission_history"], + as_dict=True, + ) + quiz.questions = [] + fields = ["name", "question", "type", "multiple"] + for num in range(1, 5): + fields.append(f"option_{num}") + fields.append(f"is_correct_{num}") + fields.append(f"explanation_{num}") + fields.append(f"possibility_{num}") + + questions = frappe.get_all( + "LMS Quiz Question", {"parent": quiz.name}, pluck="question", order_by="idx" + ) + + for question in questions: + details = frappe.db.get_value("LMS Question", question, fields, as_dict=1) + quiz.questions.append(details) + no_of_attempts = frappe.db.count( "LMS Quiz Submission", {"owner": frappe.session.user, "quiz": quiz_name} ) diff --git a/lms/templates/quiz/quiz.html b/lms/templates/quiz/quiz.html index 41e9cf7f..082f7d38 100644 --- a/lms/templates/quiz/quiz.html +++ b/lms/templates/quiz/quiz.html @@ -51,7 +51,8 @@ data-multi="{{ question.multiple }}" data-qt-index="{{ loop.index }}">
- {{ _("Question ") }}{{ loop.index }}: {{ instruction }}
+ {{ _("Question ") }}{{ loop.index }}: {{ instruction }} +
{{ question.question }}