diff --git a/lms/lms/doctype/lms_quiz/lms_quiz.json b/lms/lms/doctype/lms_quiz/lms_quiz.json index a3221040..7f3a8d48 100644 --- a/lms/lms/doctype/lms_quiz/lms_quiz.json +++ b/lms/lms/doctype/lms_quiz/lms_quiz.json @@ -39,7 +39,7 @@ "read_only": 1 }, { - "default": "0", + "default": "1", "fieldname": "max_attempts", "fieldtype": "Int", "label": "Max Attempts" @@ -48,6 +48,7 @@ "default": "0", "fieldname": "time", "fieldtype": "Int", + "hidden": 1, "label": "Time Per Question (in Seconds)" }, { @@ -69,7 +70,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2022-11-15 15:36:39.585488", + "modified": "2023-06-21 09:13:01.322701", "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 66ea1a3e..e8e80382 100644 --- a/lms/lms/doctype/lms_quiz/lms_quiz.py +++ b/lms/lms/doctype/lms_quiz/lms_quiz.py @@ -2,12 +2,11 @@ # For license information, please see license.txt import json - import frappe from frappe import _ from frappe.model.document import Document from frappe.utils import cstr -from lms.lms.utils import generate_slug, has_course_moderator_role +from lms.lms.utils import generate_slug, has_course_moderator_role, can_create_courses class LMSQuiz(Document): @@ -126,7 +125,7 @@ def quiz_summary(quiz, results): score += correct del result["question_index"] - frappe.get_doc( + submission = frappe.get_doc( { "doctype": "LMS Quiz Submission", "quiz": quiz, @@ -134,19 +133,32 @@ def quiz_summary(quiz, results): "score": score, "member": frappe.session.user, } - ).save(ignore_permissions=True) + ) + submission.save(ignore_permissions=True) - return score + return { + "score": score, + "submission": submission.name, + } @frappe.whitelist() -def save_quiz(quiz_title, quiz): +def save_quiz(quiz_title, max_attempts=1, quiz=None): + if not can_create_courses(): + return + + values = { + "title": quiz_title, + "max_attempts": max_attempts, + } + if quiz: - frappe.db.set_value("LMS Quiz", quiz, "title", quiz_title) + print(max_attempts) + frappe.db.set_value("LMS Quiz", quiz, values) return quiz else: doc = frappe.new_doc("LMS Quiz") - doc.update({"title": quiz_title}) + doc.update(values) doc.save(ignore_permissions=True) return doc.name diff --git a/lms/plugins.py b/lms/plugins.py index 7542756d..28badff8 100644 --- a/lms/plugins.py +++ b/lms/plugins.py @@ -110,19 +110,29 @@ def quiz_renderer(quiz_name): +"" quiz = frappe.get_doc("LMS Quiz", quiz_name) - context = {"quiz": quiz} - no_of_attempts = frappe.db.count( "LMS Quiz Submission", {"owner": frappe.session.user, "quiz": quiz_name} ) - if quiz.max_attempts and no_of_attempts >= quiz.max_attempts: - last_attempt_score = frappe.db.get_value( - "LMS Quiz Submission", {"owner": frappe.session.user, "quiz": quiz_name}, ["score"] - ) + all_submissions = frappe.get_all( + "LMS Quiz Submission", + { + "quiz": quiz.name, + "member": frappe.session.user, + }, + ["name", "score", "creation"], + order_by="creation desc", + ) - context.update({"attempts_exceeded": True, "last_attempt_score": last_attempt_score}) - return frappe.render_template("templates/quiz/quiz.html", context) + return frappe.render_template( + "templates/quiz/quiz.html", + { + "quiz": quiz, + "no_of_attempts": no_of_attempts, + "all_submissions": all_submissions, + "no_of_attempts": no_of_attempts, + }, + ) def exercise_renderer(argument): diff --git a/lms/templates/quiz/quiz.html b/lms/templates/quiz/quiz.html index 0e9fd8ba..cf218135 100644 --- a/lms/templates/quiz/quiz.html +++ b/lms/templates/quiz/quiz.html @@ -1,16 +1,26 @@ -{% if attempts_exceeded %} -
-

- {{ quiz.title }} -

-
- {{ _("You have already exceeded the maximum number of attempts allowed for this quiz.") }} - {{ _("Your latest score is {0}.").format(last_attempt_score) }} -
-
+{% if not hide_quiz %} +
+ + -{% else %} -
@@ -18,30 +28,17 @@ {{ quiz.title }}
-
- {{ _("This quiz consists of {0} questions.").format(quiz.questions | length) }} + {% if not quiz.max_attempts or no_of_attempts < quiz.max_attempts %} + + {% else %} +
+ {{ _("You have already exceeded the maximum number of attempts allowed for this quiz.") }} +
+ {% endif %}
- {% if quiz.max_attempts %} - {% set suffix = "times" if quiz.max_attempts > 1 else "time" %} -
- {{ _("This quiz can only be taken {0} {1}. If you attempt the quiz but leave the page before submitting, - the quiz will be automatically submitted.").format(quiz.max_attempts, suffix) }} -
- {% endif %} - - {% if quiz.time %} -
- {{ _("The quiz has a time limit. For each question you will be given {0} seconds.").format(quiz.time) }} -
- {% endif %} - - -
- -
@@ -108,20 +105,50 @@
{% endif %} - - -
+ +
-
+ +
+ {% endif %} + +{% if all_submissions | length %} +
+
+ {{ _("All Submissions") }} +
+
+
+
+
+
{{ _("No.") }}
+
{{ _("Date") }}
+
{{ _("Score") }}
+
+
+
+
+ {% for submission in all_submissions %} +
+
+
{{ loop.index }}
+
{{ frappe.utils.format_datetime(submission.creation, "medium") }}
+
{{ submission.score }}
+
+
+ {% endfor %} +
+
+ +
+{% endif %} \ No newline at end of file diff --git a/lms/templates/quiz/quiz.js b/lms/templates/quiz/quiz.js index de63c050..05fd5889 100644 --- a/lms/templates/quiz/quiz.js +++ b/lms/templates/quiz/quiz.js @@ -3,6 +3,7 @@ frappe.ready(() => { this.answer = []; this.is_correct = []; const self = this; + localStorage.removeItem($("#quiz-title").data("name")); $(".btn-start-quiz").click((e) => { $("#start-banner").addClass("hide"); @@ -19,15 +20,18 @@ frappe.ready(() => { }); $("#summary").click((e) => { + e.preventDefault(); add_to_local_storage(); quiz_summary(e); }); $("#check").click((e) => { + e.preventDefault(); check_answer(e); }); $("#next").click((e) => { + e.preventDefault(); add_to_local_storage(); mark_active_question(e); }); @@ -35,15 +39,6 @@ frappe.ready(() => { $("#try-again").click((e) => { try_quiz_again(e); }); - - if ($("#quiz-title").data("max-attempts")) { - window.addEventListener("beforeunload", (e) => { - e.returnValue = ""; - if ($(".active-question").length && !self.quiz_submitted) { - quiz_summary(); - } - }); - } }); const mark_active_question = (e = undefined) => { @@ -122,13 +117,15 @@ const quiz_summary = (e = undefined) => { callback: (data) => { $(".question").addClass("hide"); $("#summary").addClass("hide"); - $(".quiz-footer").prepend( - `
-
${__("Score")}: ${ - data.message - }/${total_questions}
-
` + $(".quiz-footer span").addClass("hide"); + $("#quiz-form").prepend( + `
+ ${__("Your score is ")} ${data.message.score} ${__( + " out of " + )} ${total_questions} +
` ); + $("#try-again").data("submission", data.message.submission); $("#try-again").removeClass("hide"); self.quiz_submitted = true; }, @@ -136,7 +133,14 @@ const quiz_summary = (e = undefined) => { }; const try_quiz_again = (e) => { - window.location.reload(); + if (window.location.href.includes("new-submission")) { + window.location.href = window.location.pathname.replace( + "new-submission", + $ + ); + } else { + window.location.reload(); + } }; const check_answer = (e = undefined) => { diff --git a/lms/www/assignment_submission/assignment_submission.html b/lms/www/assignment_submission/assignment_submission.html index 6afb2192..551c829f 100644 --- a/lms/www/assignment_submission/assignment_submission.html +++ b/lms/www/assignment_submission/assignment_submission.html @@ -32,6 +32,8 @@ {{ _("All Classes") }} + + {{ _("Assignment Submission") }} diff --git a/lms/www/batch/learn.js b/lms/www/batch/learn.js index 8eb95256..3b1554f3 100644 --- a/lms/www/batch/learn.js +++ b/lms/www/batch/learn.js @@ -3,7 +3,6 @@ frappe.ready(() => { let self = this; frappe.telemetry.capture("on_lesson_page", "lms"); - localStorage.removeItem($("#quiz-title").data("name")); fetch_assignments(); diff --git a/lms/www/batch/quiz.html b/lms/www/batch/quiz.html index 16a2e4c2..25078a62 100644 --- a/lms/www/batch/quiz.html +++ b/lms/www/batch/quiz.html @@ -58,13 +58,16 @@ - {% if quiz.name %}
- + {% endif %} +
- {% endif %} @@ -86,6 +89,19 @@ + +
+
+ {{ _("Max Attempts") }} +
+
+ {{ _("Enter the maximum number of times a user can attempt this quiz") }} +
+
+ {% set max_attempts = quiz.max_attempts if quiz.name else 1 %} + +
+
{% endmacro %} diff --git a/lms/www/batch/quiz.js b/lms/www/batch/quiz.js index 9f592b74..29ba3519 100644 --- a/lms/www/batch/quiz.js +++ b/lms/www/batch/quiz.js @@ -1,8 +1,9 @@ frappe.ready(() => { - $("#quiz-title").focusout((e) => { - if ($("#quiz-title").val() != $("#quiz-title").data("title")) { - save_quiz({ quiz_title: $("#quiz-title").val() }); - } + $(".btn-save-quiz").click((e) => { + save_quiz({ + quiz_title: $("#quiz-title").val(), + max_attempts: $("#max-attempts").val(), + }); }); $(".question-row").click((e) => { @@ -14,26 +15,6 @@ frappe.ready(() => { }); }); -const show_quiz_modal = () => { - let quiz_dialog = new frappe.ui.Dialog({ - title: __("Create Quiz"), - fields: [ - { - fieldtype: "Data", - label: __("Quiz Title"), - fieldname: "quiz_title", - reqd: 1, - }, - ], - primary_action: (values) => { - quiz_dialog.hide(); - save_quiz(values); - }, - }); - - quiz_dialog.show(); -}; - const show_question_modal = (values = {}) => { let fields = get_question_fields(values); @@ -142,6 +123,7 @@ const save_quiz = (values) => { method: "lms.lms.doctype.lms_quiz.lms_quiz.save_quiz", args: { quiz_title: values.quiz_title, + max_attempts: values.max_attempts, quiz: $("#quiz-form").data("name") || "", }, callback: (data) => { diff --git a/lms/www/batch/quiz.py b/lms/www/batch/quiz.py index 2d83c588..e46292bc 100644 --- a/lms/www/batch/quiz.py +++ b/lms/www/batch/quiz.py @@ -20,7 +20,9 @@ def get_context(context): else: fields_arr = ["name", "question", "type"] - context.quiz = frappe.db.get_value("LMS Quiz", quizname, ["title", "name"], as_dict=1) + context.quiz = frappe.db.get_value( + "LMS Quiz", quizname, ["title", "name", "max_attempts"], as_dict=1 + ) context.quiz.questions = frappe.get_all( "LMS Quiz Question", {"parent": quizname}, fields_arr, order_by="idx" ) diff --git a/lms/www/classes/progress.html b/lms/www/classes/progress.html index 4f7be796..87479189 100644 --- a/lms/www/classes/progress.html +++ b/lms/www/classes/progress.html @@ -1,6 +1,6 @@ {% extends "lms/templates/lms_base.html" %} {% block title %} - {{ student.first_name }} 's {{ _("Progress") }} + {{ student.first_name }}'s {{ _("Progress") }} {% endblock %} @@ -15,61 +15,93 @@ {% macro Header() %}
-
-
-
-
- {{ _("{0}'s Progress").format(student.full_name) }} -
-
- - {{ _("All Classes") }} - - +
+
+
+
+ {{ _("{0}").format(student.full_name) }} +
+
+ + {{ _("All Classes") }} + + - {{ class_info.name }} - - - {{ _("{0}'s Progress").format(student.full_name) }} -
-
+ {{ class_info.name }} + + + {{ _("Student Progress").format(student.full_name) }} +
+
{% if is_moderator %} - {% endif %} -
-
+
+
{% endmacro %} {% macro Progress(class_info, student) %} {% if assessments | length %} -
- {% for assessment in assessments %} -
- - {{ assessment.title }} - - - {% if assessment.submission %} - {% set status = assessment.submission.status %} - {% set color = "green" if status == "Pass" else "red" if status == "Fail" else "orange" %} -
-
- {{ assessment.submission.status }} +
+
+
+
+
+ {{ _("Assessment") }} +
+
+ {{ _("Type") }} +
+
+ {{ _("Status/Score") }}
- {% else %} -
- {{ _("Not Attempted") }} +
+ +
+ {% for assessment in assessments %} + {% set has_access = is_moderator and assessment.submission or frappe.session.user == student.name %} +
+
+ + {{ assessment.title }} + +
+ {{ (assessment.assessment_type).split("LMS ")[1] }}
- {% endif %} + +
+ {% if assessment.submission %} + {% if assessment.assessment_type == "LMS Assignment" %} + {% set status = assessment.submission.status %} + {% set color = "green" if status == "Pass" else "red" if status == "Fail" else "orange" %} +
+ {{ status }} +
+ {% else %} +
+ {{ assessment.submission.score }} +
+ {% endif %} + {% else %} +
+ {{ _("Not Attempted") }} +
+ {% endif %} +
+ +
{% endfor %}
diff --git a/lms/www/quiz_submission/quiz_submission.html b/lms/www/quiz_submission/quiz_submission.html index 87f86caa..cba6b20d 100644 --- a/lms/www/quiz_submission/quiz_submission.html +++ b/lms/www/quiz_submission/quiz_submission.html @@ -21,16 +21,13 @@
{{ quiz.title }}
- {% if submission.score %} -
- {{ submission.score }} -
- {% endif %}
{{ _("All Classes") }} + + {{ _("Quiz Submission") }}
diff --git a/lms/www/quiz_submission/quiz_submission.py b/lms/www/quiz_submission/quiz_submission.py index d50acf03..4519f89b 100644 --- a/lms/www/quiz_submission/quiz_submission.py +++ b/lms/www/quiz_submission/quiz_submission.py @@ -24,8 +24,25 @@ def get_context(context): ["name", "score", "member", "member_name"], as_dict=True, ) + if not context.is_moderator and frappe.session.user != context.submission.member: raise frappe.PermissionError(_("You don't have permission to access this page.")) - if not context.assignment or not context.submission: + if not context.quiz or not context.submission: raise frappe.PermissionError(_("Invalid Submission URL")) + + context.all_submissions = frappe.get_all( + "LMS Quiz Submission", + { + "quiz": context.quiz.name, + "member": context.submission.member, + }, + ["name", "score", "creation"], + order_by="creation desc", + ) + + context.no_of_attempts = len(context.all_submissions) or 0 + context.hide_quiz = ( + context.is_moderator and context.submission.member != frappe.session.user + ) + print(context.no_of_attempts) diff --git a/lms/www/utils.py b/lms/www/utils.py index 4855700e..a99046bc 100644 --- a/lms/www/utils.py +++ b/lms/www/utils.py @@ -110,22 +110,21 @@ def get_assignment_details(assessment, member): def get_quiz_details(assessment, member): assessment.title = frappe.db.get_value("LMS Quiz", assessment.assessment_name, "title") - existing_submission = frappe.db.exists( + existing_submission = frappe.get_all( + "LMS Quiz Submission", { - "doctype": "LMS Quiz Submission", "member": member, "quiz": assessment.assessment_name, - } + }, + ["name", "score"], + order_by="creation desc", ) - if existing_submission: - assessment.submission = frappe.db.get_value( - "LMS Quiz Submission", - existing_submission, - ["name", "score"], - as_dict=True, - ) + if len(existing_submission): + assessment.submission = existing_submission[0] assessment.edit_url = f"/quizzes/{assessment.assessment_name}" - submission_name = existing_submission if existing_submission else "new-submission" + submission_name = ( + existing_submission[0].name if len(existing_submission) else "new-submission" + ) assessment.url = f"/quiz-submission/{assessment.assessment_name}/{submission_name}"