diff --git a/lms/lms/doctype/lms_quiz/lms_quiz.json b/lms/lms/doctype/lms_quiz/lms_quiz.json
index 39e9b282..dbbea28b 100644
--- a/lms/lms/doctype/lms_quiz/lms_quiz.json
+++ b/lms/lms/doctype/lms_quiz/lms_quiz.json
@@ -8,13 +8,17 @@
"engine": "InnoDB",
"field_order": [
"title",
+ "show_answers",
+ "column_break_gaac",
+ "max_attempts",
+ "show_submission_history",
+ "section_break_sbjx",
"questions",
"section_break_3",
- "max_attempts",
- "time",
- "column_break_5",
"lesson",
- "course"
+ "column_break_5",
+ "course",
+ "time"
],
"fields": [
{
@@ -66,11 +70,31 @@
{
"fieldname": "section_break_3",
"fieldtype": "Section Break"
+ },
+ {
+ "default": "1",
+ "fieldname": "show_answers",
+ "fieldtype": "Check",
+ "label": "Show Answers"
+ },
+ {
+ "fieldname": "column_break_gaac",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "section_break_sbjx",
+ "fieldtype": "Section Break"
+ },
+ {
+ "default": "0",
+ "fieldname": "show_submission_history",
+ "fieldtype": "Check",
+ "label": "Show Submission History"
}
],
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2023-06-23 12:35:25.204131",
+ "modified": "2023-07-04 15:26:24.457745",
"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 cfe1bb45..6f9531e0 100644
--- a/lms/lms/doctype/lms_quiz/lms_quiz.py
+++ b/lms/lms/doctype/lms_quiz/lms_quiz.py
@@ -143,13 +143,17 @@ def quiz_summary(quiz, results):
@frappe.whitelist()
-def save_quiz(quiz_title, max_attempts=1, quiz=None):
+def save_quiz(
+ quiz_title, max_attempts=1, quiz=None, show_answers=1, show_submission_history=0
+):
if not can_create_courses():
return
values = {
"title": quiz_title,
"max_attempts": max_attempts,
+ "show_answers": show_answers,
+ "show_submission_history": show_submission_history,
}
if quiz:
@@ -232,15 +236,17 @@ def get_question_details(question):
@frappe.whitelist()
-def check_answer(question, type, answer):
+def check_answer(question, type, answers):
+ answers = json.loads(answers)
if type == "Choices":
- return check_choice_answers(question, answer)
+ return check_choice_answers(question, answers)
else:
- return check_input_answers(question, answer)
+ return check_input_answers(question, answers[0])
-def check_choice_answers(question, answer):
+def check_choice_answers(question, answers):
fields = []
+ is_correct = []
for num in range(1, 5):
fields.append(f"option_{cstr(num)}")
fields.append(f"is_correct_{cstr(num)}")
@@ -250,9 +256,13 @@ def check_choice_answers(question, answer):
)
for num in range(1, 5):
- if question_details[f"option_{num}"] == answer:
- return question_details[f"is_correct_{num}"]
- return 0
+ print(question_details[f"option_{num}"], answers)
+ if question_details[f"option_{num}"] in answers:
+ is_correct.append(question_details[f"is_correct_{num}"])
+ else:
+ is_correct.append(0)
+
+ return is_correct
def check_input_answers(question, answer):
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 3231fc8c..8e815ee3 100644
--- a/lms/lms/doctype/lms_quiz_question/lms_quiz_question.json
+++ b/lms/lms/doctype/lms_quiz_question/lms_quiz_question.json
@@ -165,6 +165,7 @@
{
"fieldname": "type",
"fieldtype": "Select",
+ "in_list_view": 1,
"label": "Type",
"options": "Choices\nUser Input"
},
@@ -206,7 +207,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2023-06-09 17:09:53.740179",
+ "modified": "2023-07-04 16:43:49.837134",
"modified_by": "Administrator",
"module": "LMS",
"name": "LMS Quiz Question",
diff --git a/lms/plugins.py b/lms/plugins.py
index 1a66d87a..69571476 100644
--- a/lms/plugins.py
+++ b/lms/plugins.py
@@ -114,22 +114,23 @@ def quiz_renderer(quiz_name):
"LMS Quiz Submission", {"owner": frappe.session.user, "quiz": quiz_name}
)
- all_submissions = frappe.get_all(
- "LMS Quiz Submission",
- {
- "quiz": quiz.name,
- "member": frappe.session.user,
- },
- ["name", "score", "creation"],
- order_by="creation desc",
- )
+ if quiz.show_submission_history:
+ all_submissions = frappe.get_all(
+ "LMS Quiz Submission",
+ {
+ "quiz": quiz.name,
+ "member": frappe.session.user,
+ },
+ ["name", "score", "creation"],
+ order_by="creation desc",
+ )
return frappe.render_template(
"templates/quiz/quiz.html",
{
"quiz": quiz,
"no_of_attempts": no_of_attempts,
- "all_submissions": all_submissions,
+ "all_submissions": all_submissions if quiz.show_submission_history else None,
"hide_quiz": False,
},
)
diff --git a/lms/public/css/style.css b/lms/public/css/style.css
index c985aea4..c36c5f9e 100644
--- a/lms/public/css/style.css
+++ b/lms/public/css/style.css
@@ -242,7 +242,7 @@ textarea.field-input {
input[type=checkbox] {
appearance: auto;
position: relative;
- width: var(--checkbox-size)!important;
+ width: var(--checkbox-size) !important;
height: var(--checkbox-size);
margin-right: var(--checkbox-right-margin)!important;
background-repeat: no-repeat;
@@ -252,8 +252,6 @@ input[type=checkbox] {
box-shadow: 0 1px 2px #0000001a;
border-radius: 4px;
-webkit-appearance: none;
- -moz-appearance: none;
- appearance: none;
-webkit-print-color-adjust: exact;
}
@@ -1904,10 +1902,6 @@ li {
padding: 0 1rem !important;
}
-.modal-dialog-scrollable .modal-body {
- overflow-y: unset;
-}
-
.modal-dialog-scrollable .modal-content {
overflow: unset;
}
diff --git a/lms/templates/quiz/quiz.html b/lms/templates/quiz/quiz.html
index 4ae2b242..41e9cf7f 100644
--- a/lms/templates/quiz/quiz.html
+++ b/lms/templates/quiz/quiz.html
@@ -24,7 +24,8 @@
-
+
{{ quiz.title }}
@@ -105,10 +106,13 @@
{% endif %}
+ {% if quiz.show_answers %}
-
{% endif %}
-{% if all_submissions | length %}
+{% if quiz.show_submission_history and all_submissions | length %}
{{ _("All Submissions") }}
@@ -131,9 +135,9 @@
-
{{ _("No.") }}
-
{{ _("Date") }}
+
{{ _("No.") }}
{{ _("Score") }}
+
{{ _("Date") }}
@@ -141,9 +145,11 @@
{% for submission in all_submissions %}
-
{{ loop.index }}
-
{{ frappe.utils.format_datetime(submission.creation, "medium") }}
+
{{ loop.index }}
{{ submission.score }}
+
+ {{ frappe.utils.pretty_date(submission.creation) }}
+
{% endfor %}
diff --git a/lms/templates/quiz/quiz.js b/lms/templates/quiz/quiz.js
index 37fd563e..36c2ae08 100644
--- a/lms/templates/quiz/quiz.js
+++ b/lms/templates/quiz/quiz.js
@@ -1,8 +1,9 @@
frappe.ready(() => {
+ const self = this;
this.quiz_submitted = false;
this.answer = [];
this.is_correct = [];
- const self = this;
+ this.show_answers = $("#quiz-title").data("show-answers");
localStorage.removeItem($("#quiz-title").data("name"));
$(".btn-start-quiz").click((e) => {
@@ -21,8 +22,11 @@ frappe.ready(() => {
$("#summary").click((e) => {
e.preventDefault();
- add_to_local_storage();
- quiz_summary(e);
+ if (!this.show_answers) check_answer();
+
+ setTimeout(() => {
+ quiz_summary(e);
+ }, 500);
});
$("#check").click((e) => {
@@ -32,7 +36,8 @@ frappe.ready(() => {
$("#next").click((e) => {
e.preventDefault();
- add_to_local_storage();
+ if (!this.show_answers) check_answer();
+
mark_active_question(e);
});
@@ -42,21 +47,30 @@ frappe.ready(() => {
});
const mark_active_question = (e = undefined) => {
- $(".timer").addClass("hide");
- calculate_and_display_time(100);
- $(".timer").removeClass("hide");
-
+ let total_questions = $(".question").length;
let current_index = $(".active-question").attr("data-qt-index") || 0;
let next_index = parseInt(current_index) + 1;
+ if (this.show_answers) {
+ $("#next").addClass("hide");
+ } else if (!this.show_answers && next_index == total_questions) {
+ $("#next").addClass("hide");
+ $("#summary").removeClass("hide");
+ }
+
$(".question").addClass("hide").removeClass("active-question");
$(`.question[data-qt-index='${next_index}']`)
.removeClass("hide")
.addClass("active-question");
+
$(".current-question").text(`${next_index}`);
$("#check").removeClass("hide").attr("disabled", true);
- $("#next").addClass("hide");
+ $("#next").attr("disabled", true);
$(".explanation").addClass("hide");
+
+ $(".timer").addClass("hide");
+ calculate_and_display_time(100);
+ $(".timer").removeClass("hide");
initialize_timer();
};
@@ -95,6 +109,7 @@ const initialize_timer = () => {
const enable_check = (e) => {
if ($(".option:checked").length || $(".possibility").val().trim()) {
$("#check").removeAttr("disabled");
+ $("#next").removeAttr("disabled");
$(".custom-checkbox").removeClass("active-option");
$(".option:checked")
.closest(".custom-checkbox")
@@ -145,8 +160,10 @@ const try_quiz_again = (e) => {
const check_answer = (e = undefined) => {
e && e.preventDefault();
-
let answer = $(".active-question textarea");
+ let total_questions = $(".question").length;
+ let current_index = $(".active-question").attr("data-qt-index");
+
if (answer.length && !answer.val().trim()) {
frappe.throw(__("Please enter your answer"));
}
@@ -154,73 +171,77 @@ const check_answer = (e = undefined) => {
clearInterval(self.timer);
$(".timer").addClass("hide");
- let total_questions = $(".question").length;
- let current_index = $(".active-question").attr("data-qt-index");
-
$(".explanation").removeClass("hide");
$("#check").addClass("hide");
if (current_index == total_questions) {
$("#summary").removeClass("hide");
- } else {
+ } else if (this.show_answers) {
$("#next").removeClass("hide");
}
parse_options();
};
const parse_options = () => {
+ let user_answers = [];
+ let element;
let type = $(".active-question").data("type");
if (type == "Choices") {
$(".active-question input").each((i, element) => {
- is_answer_correct(type, element);
+ if ($(element).prop("checked")) {
+ user_answers.push(decodeURIComponent($(element).val()));
+ }
});
+ element = $(".active-question input");
} else {
- is_answer_correct(type, $(".active-question textarea"));
+ user_answers.push($(".active-question textarea").val());
+ element = $(".active-question textarea");
}
+
+ is_answer_correct(type, user_answers, element);
};
-const is_answer_correct = (type, element) => {
- let answer = decodeURIComponent($(element).val());
-
+const is_answer_correct = (type, user_answers, element) => {
frappe.call({
async: false,
method: "lms.lms.doctype.lms_quiz.lms_quiz.check_answer",
args: {
question: $(".active-question").data("name"),
type: type,
- answer: answer,
+ answers: user_answers,
},
callback: (data) => {
type == "Choices"
? parse_choices(element, data.message)
: parse_possible_answers(element, data.message);
+ add_to_local_storage();
},
});
};
-const parse_choices = (element, correct) => {
- if ($(element).prop("checked")) {
- self.answer.push(decodeURIComponent($(element).val()));
- correct && self.is_correct.push(1);
- correct ? add_icon(element, "check") : add_icon(element, "wrong");
- } else {
- correct && self.is_correct.push(0);
- correct
- ? add_icon(element, "minus-circle-green")
- : add_icon(element, "minus-circle");
- }
+const parse_choices = (element, is_correct) => {
+ element.each((i, elem) => {
+ if ($(elem).prop("checked")) {
+ self.answer.push(decodeURIComponent($(elem).val()));
+ self.is_correct.push(is_correct[i]);
+ if (this.show_answers)
+ is_correct[i]
+ ? add_icon(elem, "check")
+ : add_icon(elem, "wrong");
+ } else {
+ add_icon(elem, "minus-circle");
+ }
+ });
};
const parse_possible_answers = (element, correct) => {
self.answer.push(decodeURIComponent($(element).val()));
- if (correct) {
- self.is_correct.push(1);
- show_indicator("success", element);
- } else {
- self.is_correct.push(0);
- show_indicator("failure", element);
- }
+ self.is_correct.push(correct);
+ if (this.show_answers)
+ correct
+ ? show_indicator("success", element)
+ : show_indicator("failure", element);
};
const show_indicator = (class_name, element) => {
@@ -253,7 +274,7 @@ const add_to_local_storage = () => {
let quiz_stored = JSON.parse(localStorage.getItem(quiz_name));
let quiz_obj = {
- question_index: current_index,
+ question_index: current_index - 1,
answer: self.answer.join(),
is_correct: self.is_correct,
};
diff --git a/lms/www/batch/quiz.html b/lms/www/batch/quiz.html
index 25078a62..ad302f78 100644
--- a/lms/www/batch/quiz.html
+++ b/lms/www/batch/quiz.html
@@ -78,7 +78,7 @@
{% endmacro %}
diff --git a/lms/www/batch/quiz.js b/lms/www/batch/quiz.js
index 29ba3519..676cf1c0 100644
--- a/lms/www/batch/quiz.js
+++ b/lms/www/batch/quiz.js
@@ -119,12 +119,19 @@ const edit_question = (e) => {
};
const save_quiz = (values) => {
+ validate_mandatory();
frappe.call({
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") || "",
+ show_answers: $("#show-answers").is(":checked") ? 1 : 0,
+ show_submission_history: $("#show-submission-history").is(
+ ":checked"
+ )
+ ? 1
+ : 0,
},
callback: (data) => {
frappe.show_alert({
@@ -138,6 +145,17 @@ const save_quiz = (values) => {
});
};
+const validate_mandatory = () => {
+ if (!$("#quiz-title").val()) {
+ let error = $("p")
+ .addClass("error-message")
+ .text(__("Please enter a Quiz Title"));
+ $(error).insertAfter("#quiz-title");
+ $("#quiz-title").focus();
+ throw "Title is mandatory";
+ }
+};
+
const save_question = (values) => {
frappe.call({
method: "lms.lms.doctype.lms_quiz.lms_quiz.save_question",
diff --git a/lms/www/batch/quiz.py b/lms/www/batch/quiz.py
index e46292bc..f88aaef5 100644
--- a/lms/www/batch/quiz.py
+++ b/lms/www/batch/quiz.py
@@ -21,7 +21,10 @@ def get_context(context):
fields_arr = ["name", "question", "type"]
context.quiz = frappe.db.get_value(
- "LMS Quiz", quizname, ["title", "name", "max_attempts"], as_dict=1
+ "LMS Quiz",
+ quizname,
+ ["title", "name", "max_attempts", "show_answers", "show_submission_history"],
+ as_dict=1,
)
context.quiz.questions = frappe.get_all(
"LMS Quiz Question", {"parent": quizname}, fields_arr, order_by="idx"