fix: quiz max attempts
This commit is contained in:
@@ -39,7 +39,7 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "1",
|
||||||
"fieldname": "max_attempts",
|
"fieldname": "max_attempts",
|
||||||
"fieldtype": "Int",
|
"fieldtype": "Int",
|
||||||
"label": "Max Attempts"
|
"label": "Max Attempts"
|
||||||
@@ -48,6 +48,7 @@
|
|||||||
"default": "0",
|
"default": "0",
|
||||||
"fieldname": "time",
|
"fieldname": "time",
|
||||||
"fieldtype": "Int",
|
"fieldtype": "Int",
|
||||||
|
"hidden": 1,
|
||||||
"label": "Time Per Question (in Seconds)"
|
"label": "Time Per Question (in Seconds)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -69,7 +70,7 @@
|
|||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-11-15 15:36:39.585488",
|
"modified": "2023-06-21 09:13:01.322701",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "LMS Quiz",
|
"name": "LMS Quiz",
|
||||||
|
|||||||
@@ -2,12 +2,11 @@
|
|||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import cstr
|
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):
|
class LMSQuiz(Document):
|
||||||
@@ -126,7 +125,7 @@ def quiz_summary(quiz, results):
|
|||||||
score += correct
|
score += correct
|
||||||
del result["question_index"]
|
del result["question_index"]
|
||||||
|
|
||||||
frappe.get_doc(
|
submission = frappe.get_doc(
|
||||||
{
|
{
|
||||||
"doctype": "LMS Quiz Submission",
|
"doctype": "LMS Quiz Submission",
|
||||||
"quiz": quiz,
|
"quiz": quiz,
|
||||||
@@ -134,19 +133,32 @@ def quiz_summary(quiz, results):
|
|||||||
"score": score,
|
"score": score,
|
||||||
"member": frappe.session.user,
|
"member": frappe.session.user,
|
||||||
}
|
}
|
||||||
).save(ignore_permissions=True)
|
)
|
||||||
|
submission.save(ignore_permissions=True)
|
||||||
|
|
||||||
return score
|
return {
|
||||||
|
"score": score,
|
||||||
|
"submission": submission.name,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@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:
|
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
|
return quiz
|
||||||
else:
|
else:
|
||||||
doc = frappe.new_doc("LMS Quiz")
|
doc = frappe.new_doc("LMS Quiz")
|
||||||
doc.update({"title": quiz_title})
|
doc.update(values)
|
||||||
doc.save(ignore_permissions=True)
|
doc.save(ignore_permissions=True)
|
||||||
return doc.name
|
return doc.name
|
||||||
|
|
||||||
|
|||||||
@@ -110,19 +110,29 @@ def quiz_renderer(quiz_name):
|
|||||||
+"</div>"
|
+"</div>"
|
||||||
|
|
||||||
quiz = frappe.get_doc("LMS Quiz", quiz_name)
|
quiz = frappe.get_doc("LMS Quiz", quiz_name)
|
||||||
context = {"quiz": quiz}
|
|
||||||
|
|
||||||
no_of_attempts = frappe.db.count(
|
no_of_attempts = frappe.db.count(
|
||||||
"LMS Quiz Submission", {"owner": frappe.session.user, "quiz": quiz_name}
|
"LMS Quiz Submission", {"owner": frappe.session.user, "quiz": quiz_name}
|
||||||
)
|
)
|
||||||
|
|
||||||
if quiz.max_attempts and no_of_attempts >= quiz.max_attempts:
|
all_submissions = frappe.get_all(
|
||||||
last_attempt_score = frappe.db.get_value(
|
"LMS Quiz Submission",
|
||||||
"LMS Quiz Submission", {"owner": frappe.session.user, "quiz": quiz_name}, ["score"]
|
{
|
||||||
)
|
"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(
|
||||||
return frappe.render_template("templates/quiz/quiz.html", context)
|
"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):
|
def exercise_renderer(argument):
|
||||||
|
|||||||
@@ -1,16 +1,26 @@
|
|||||||
{% if attempts_exceeded %}
|
{% if not hide_quiz %}
|
||||||
<div class="">
|
<div class="mt-4">
|
||||||
<h2 class="mt-3">
|
<ul class="alert alert-info pl-8">
|
||||||
{{ quiz.title }}
|
|
||||||
</h2>
|
<li>
|
||||||
<div class="alert alert-info medium mb-0">
|
{{ _("This quiz consists of {0} questions.").format(quiz.questions | length) }}
|
||||||
{{ _("You have already exceeded the maximum number of attempts allowed for this quiz.") }}
|
</li>
|
||||||
{{ _("Your latest score is {0}.").format(last_attempt_score) }}
|
|
||||||
</div>
|
{% if quiz.max_attempts %}
|
||||||
</div>
|
{% set suffix = "times" if quiz.max_attempts > 1 else "time" %}
|
||||||
|
<li>
|
||||||
|
{{ _("You can attempt this quiz only {0} {1}").format(quiz.max_attempts, suffix) }}
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if quiz.time %}
|
||||||
|
<li class="alert alert-info medium">
|
||||||
|
{{ _("The quiz has a time limit. For each question you will be given {0} seconds.").format(quiz.time) }}
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
{% else %}
|
|
||||||
<div class="">
|
|
||||||
<div id="start-banner" class="common-card-style column-card align-items-center">
|
<div id="start-banner" class="common-card-style column-card align-items-center">
|
||||||
|
|
||||||
<div class="text-center my-10">
|
<div class="text-center my-10">
|
||||||
@@ -18,30 +28,17 @@
|
|||||||
{{ quiz.title }}
|
{{ quiz.title }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="">
|
{% if not quiz.max_attempts or no_of_attempts < quiz.max_attempts %}
|
||||||
{{ _("This quiz consists of {0} questions.").format(quiz.questions | length) }}
|
<button class="btn btn-secondary btn-sm btn-start-quiz mt-4">
|
||||||
|
{{ _("Start") }}
|
||||||
|
</button>
|
||||||
|
{% else %}
|
||||||
|
<div>
|
||||||
|
{{ _("You have already exceeded the maximum number of attempts allowed for this quiz.") }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if quiz.max_attempts %}
|
|
||||||
{% set suffix = "times" if quiz.max_attempts > 1 else "time" %}
|
|
||||||
<div class="alert alert-info medium">
|
|
||||||
{{ _("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) }}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if quiz.time %}
|
|
||||||
<div class="alert alert-info medium">
|
|
||||||
{{ _("The quiz has a time limit. For each question you will be given {0} seconds.").format(quiz.time) }}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<button class="btn btn-secondary btn-sm btn-start-quiz mt-4">
|
|
||||||
{{ _("Start the Quiz") }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form id="quiz-form" class="common-card-style column-card hide">
|
<form id="quiz-form" class="common-card-style column-card hide">
|
||||||
@@ -108,20 +105,50 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
<button class="btn btn-secondary btn-sm pull-right" id="check" disabled>
|
<button class="btn btn-secondary btn-sm pull-right" id="check" disabled>
|
||||||
{{ _("Check") }}
|
{{ _("Check") }}
|
||||||
</button>
|
</button>
|
||||||
<div class="btn btn-secondary btn-sm hide" id="next">
|
<button class="btn btn-secondary btn-sm hide" id="next">
|
||||||
{{ _("Next Question") }}
|
{{ _("Next Question") }}
|
||||||
</div>
|
</button>
|
||||||
<div class="btn btn-secondary btn-sm hide" id="summary">
|
<button class="btn btn-secondary btn-sm hide" id="summary">
|
||||||
{{ _("Submit") }}
|
{{ _("Submit") }}
|
||||||
</div>
|
</button>
|
||||||
<div class="btn btn-secondary btn-sm hide" id="try-again">
|
<button class="btn btn-secondary btn-sm mx-auto hide" id="try-again">
|
||||||
{{ _("Try Again") }}
|
{{ _("Try Again") }}
|
||||||
</div>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if all_submissions | length %}
|
||||||
|
<article class="mt-8">
|
||||||
|
<div class="field-label">
|
||||||
|
{{ _("All Submissions") }}
|
||||||
|
</div>
|
||||||
|
<div class="form-grid">
|
||||||
|
<div class="grid-heading-row">
|
||||||
|
<div class="grid-row">
|
||||||
|
<div class="data-row row">
|
||||||
|
<div class="col grid-static-col">{{ _("No.") }}</div>
|
||||||
|
<div class="col grid-static-col">{{ _("Date") }}</div>
|
||||||
|
<div class="col grid-static-col text-right">{{ _("Score") }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{% for submission in all_submissions %}
|
||||||
|
<div class="grid-row">
|
||||||
|
<div class="data-row row">
|
||||||
|
<div class="col grid-static-col">{{ loop.index }}</div>
|
||||||
|
<div class="col grid-static-col">{{ frappe.utils.format_datetime(submission.creation, "medium") }}</div>
|
||||||
|
<div class="col grid-static-col text-right">{{ submission.score }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</article>
|
||||||
|
{% endif %}
|
||||||
@@ -3,6 +3,7 @@ frappe.ready(() => {
|
|||||||
this.answer = [];
|
this.answer = [];
|
||||||
this.is_correct = [];
|
this.is_correct = [];
|
||||||
const self = this;
|
const self = this;
|
||||||
|
localStorage.removeItem($("#quiz-title").data("name"));
|
||||||
|
|
||||||
$(".btn-start-quiz").click((e) => {
|
$(".btn-start-quiz").click((e) => {
|
||||||
$("#start-banner").addClass("hide");
|
$("#start-banner").addClass("hide");
|
||||||
@@ -19,15 +20,18 @@ frappe.ready(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
$("#summary").click((e) => {
|
$("#summary").click((e) => {
|
||||||
|
e.preventDefault();
|
||||||
add_to_local_storage();
|
add_to_local_storage();
|
||||||
quiz_summary(e);
|
quiz_summary(e);
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#check").click((e) => {
|
$("#check").click((e) => {
|
||||||
|
e.preventDefault();
|
||||||
check_answer(e);
|
check_answer(e);
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#next").click((e) => {
|
$("#next").click((e) => {
|
||||||
|
e.preventDefault();
|
||||||
add_to_local_storage();
|
add_to_local_storage();
|
||||||
mark_active_question(e);
|
mark_active_question(e);
|
||||||
});
|
});
|
||||||
@@ -35,15 +39,6 @@ frappe.ready(() => {
|
|||||||
$("#try-again").click((e) => {
|
$("#try-again").click((e) => {
|
||||||
try_quiz_again(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) => {
|
const mark_active_question = (e = undefined) => {
|
||||||
@@ -122,13 +117,15 @@ const quiz_summary = (e = undefined) => {
|
|||||||
callback: (data) => {
|
callback: (data) => {
|
||||||
$(".question").addClass("hide");
|
$(".question").addClass("hide");
|
||||||
$("#summary").addClass("hide");
|
$("#summary").addClass("hide");
|
||||||
$(".quiz-footer").prepend(
|
$(".quiz-footer span").addClass("hide");
|
||||||
`<div class="summary">
|
$("#quiz-form").prepend(
|
||||||
<div class="font-weight-bold"> ${__("Score")}: ${
|
`<div class="summary bold-heading text-center">
|
||||||
data.message
|
${__("Your score is ")} ${data.message.score} ${__(
|
||||||
}/${total_questions} </div>
|
" out of "
|
||||||
</div>`
|
)} ${total_questions}
|
||||||
|
</div>`
|
||||||
);
|
);
|
||||||
|
$("#try-again").data("submission", data.message.submission);
|
||||||
$("#try-again").removeClass("hide");
|
$("#try-again").removeClass("hide");
|
||||||
self.quiz_submitted = true;
|
self.quiz_submitted = true;
|
||||||
},
|
},
|
||||||
@@ -136,7 +133,14 @@ const quiz_summary = (e = undefined) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const try_quiz_again = (e) => {
|
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) => {
|
const check_answer = (e = undefined) => {
|
||||||
|
|||||||
@@ -32,6 +32,8 @@
|
|||||||
<a class="dark-links" href="/classes">
|
<a class="dark-links" href="/classes">
|
||||||
{{ _("All Classes") }}
|
{{ _("All Classes") }}
|
||||||
</a>
|
</a>
|
||||||
|
<img class="icon icon-sm mr-0" src="/assets/lms/icons/chevron-right.svg">
|
||||||
|
<span class="breadcrumb-destination">{{ _("Assignment Submission") }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ frappe.ready(() => {
|
|||||||
let self = this;
|
let self = this;
|
||||||
|
|
||||||
frappe.telemetry.capture("on_lesson_page", "lms");
|
frappe.telemetry.capture("on_lesson_page", "lms");
|
||||||
localStorage.removeItem($("#quiz-title").data("name"));
|
|
||||||
|
|
||||||
fetch_assignments();
|
fetch_assignments();
|
||||||
|
|
||||||
|
|||||||
@@ -58,13 +58,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if quiz.name %}
|
|
||||||
<div class="align-self-center">
|
<div class="align-self-center">
|
||||||
<button class="btn btn-primary btn-sm btn-add-question">
|
{% if quiz.name %}
|
||||||
|
<button class="btn btn-secondary btn-sm btn-add-question mr-2">
|
||||||
{{ _("Add Question") }}
|
{{ _("Add Question") }}
|
||||||
</button>
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
<button class="btn btn-primary btn-sm btn-save-quiz">
|
||||||
|
{{ _("Save") }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -86,6 +89,19 @@
|
|||||||
<input type="text" class="field-input" id="quiz-title" {% if quiz.name %} value="{{ quiz.title }}" data-title="{{ quiz.title }}" {% endif %}>
|
<input type="text" class="field-input" id="quiz-title" {% if quiz.name %} value="{{ quiz.title }}" data-title="{{ quiz.title }}" {% endif %}>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="field-group">
|
||||||
|
<div class="field-label">
|
||||||
|
{{ _("Max Attempts") }}
|
||||||
|
</div>
|
||||||
|
<div class="field-description">
|
||||||
|
{{ _("Enter the maximum number of times a user can attempt this quiz") }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{% set max_attempts = quiz.max_attempts if quiz.name else 1 %}
|
||||||
|
<input type="number" class="field-input" id="max-attempts" value="{{ max_attempts }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
frappe.ready(() => {
|
frappe.ready(() => {
|
||||||
$("#quiz-title").focusout((e) => {
|
$(".btn-save-quiz").click((e) => {
|
||||||
if ($("#quiz-title").val() != $("#quiz-title").data("title")) {
|
save_quiz({
|
||||||
save_quiz({ quiz_title: $("#quiz-title").val() });
|
quiz_title: $("#quiz-title").val(),
|
||||||
}
|
max_attempts: $("#max-attempts").val(),
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$(".question-row").click((e) => {
|
$(".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 = {}) => {
|
const show_question_modal = (values = {}) => {
|
||||||
let fields = get_question_fields(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",
|
method: "lms.lms.doctype.lms_quiz.lms_quiz.save_quiz",
|
||||||
args: {
|
args: {
|
||||||
quiz_title: values.quiz_title,
|
quiz_title: values.quiz_title,
|
||||||
|
max_attempts: values.max_attempts,
|
||||||
quiz: $("#quiz-form").data("name") || "",
|
quiz: $("#quiz-form").data("name") || "",
|
||||||
},
|
},
|
||||||
callback: (data) => {
|
callback: (data) => {
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ def get_context(context):
|
|||||||
else:
|
else:
|
||||||
fields_arr = ["name", "question", "type"]
|
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(
|
context.quiz.questions = frappe.get_all(
|
||||||
"LMS Quiz Question", {"parent": quizname}, fields_arr, order_by="idx"
|
"LMS Quiz Question", {"parent": quizname}, fields_arr, order_by="idx"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{% extends "lms/templates/lms_base.html" %}
|
{% extends "lms/templates/lms_base.html" %}
|
||||||
{% block title %}
|
{% block title %}
|
||||||
{{ student.first_name }} 's {{ _("Progress") }}
|
{{ student.first_name }}'s {{ _("Progress") }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
@@ -15,61 +15,93 @@
|
|||||||
|
|
||||||
{% macro Header() %}
|
{% macro Header() %}
|
||||||
<header class="sticky mb-5">
|
<header class="sticky mb-5">
|
||||||
<div class="container form-width">
|
<div class="container form-width">
|
||||||
<div class="edit-header">
|
<div class="edit-header">
|
||||||
<div>
|
<div>
|
||||||
<div class="page-title">
|
<div class="page-title">
|
||||||
{{ _("{0}'s Progress").format(student.full_name) }}
|
{{ _("{0}").format(student.full_name) }}
|
||||||
</div>
|
</div>
|
||||||
<div class="vertically-center small">
|
<div class="vertically-center small">
|
||||||
<a class="dark-links" href="/classes">
|
<a class="dark-links" href="/classes">
|
||||||
{{ _("All Classes") }}
|
{{ _("All Classes") }}
|
||||||
</a>
|
</a>
|
||||||
<img class="icon icon-sm mr-0" src="/assets/lms/icons/chevron-right.svg">
|
<img class="icon icon-sm mr-0" src="/assets/lms/icons/chevron-right.svg">
|
||||||
<a class="dark-links" href="/classes/{{ class_info.name }}">
|
<a class="dark-links" href="/classes/{{ class_info.name }}">
|
||||||
{{ class_info.name }}
|
{{ class_info.name }}
|
||||||
</a>
|
</a>
|
||||||
<img class="icon icon-sm mr-0" src="/assets/lms/icons/chevron-right.svg">
|
<img class="icon icon-sm mr-0" src="/assets/lms/icons/chevron-right.svg">
|
||||||
<span class="breadcrumb-destination">{{ _("{0}'s Progress").format(student.full_name) }}</span>
|
<span class="breadcrumb-destination">{{ _("Student Progress").format(student.full_name) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if is_moderator %}
|
{% if is_moderator %}
|
||||||
<div class="align-self-center">
|
<div class="align-self-center">
|
||||||
<a class="btn btn-primary btn-sm btn-evaluate" href=/evaluation/new?member={{student.name}}&date={{frappe.utils.getdate()}}&class={{class_info.name}}">
|
<a class="btn btn-default btn-sm mr-2" href="/users/{{ student.username }}">
|
||||||
{{ _("Evaluate") }}
|
{{ _("View Profile") }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
<a class="btn btn-primary btn-sm btn-evaluate" href=/evaluation/new?member={{student.name}}&date={{frappe.utils.getdate()}}&class={{class_info.name}}">
|
||||||
|
{{ _("Evaluate") }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
|
||||||
{% macro Progress(class_info, student) %}
|
{% macro Progress(class_info, student) %}
|
||||||
{% if assessments | length %}
|
{% if assessments | length %}
|
||||||
<article>
|
<article class="form-grid">
|
||||||
{% for assessment in assessments %}
|
<div class="grid-heading-row">
|
||||||
<div class="list-row level">
|
<div class="grid-row">
|
||||||
<a {% if is_moderator and assessment.submission or frappe.session.user == student.name %} class="clickable" href="{{ assessment.url }}" {% endif %}>
|
<div class="data-row row">
|
||||||
{{ assessment.title }}
|
<div class="col grid-static-col">
|
||||||
</a>
|
{{ _("Assessment") }}
|
||||||
|
</div>
|
||||||
{% if assessment.submission %}
|
<div class="col grid-static-col">
|
||||||
{% set status = assessment.submission.status %}
|
{{ _("Type") }}
|
||||||
{% set color = "green" if status == "Pass" else "red" if status == "Fail" else "orange" %}
|
</div>
|
||||||
<div>
|
<div class="col grid-static-col">
|
||||||
<div class="indicator-pill {{ color }}">
|
{{ _("Status/Score") }}
|
||||||
{{ assessment.submission.status }}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
</div>
|
||||||
<div class="indicator-pill red">
|
|
||||||
{{ _("Not Attempted") }}
|
</div>
|
||||||
|
{% for assessment in assessments %}
|
||||||
|
{% set has_access = is_moderator and assessment.submission or frappe.session.user == student.name %}
|
||||||
|
<div class="grid-row">
|
||||||
|
<div class="data-row row">
|
||||||
|
<a class="col grid-static-col {% if has_access %} clickable {% endif %}" {% if has_access %} href="{{ assessment.url }}" {% endif %}>
|
||||||
|
{{ assessment.title }}
|
||||||
|
</a>
|
||||||
|
<div class="col grid-static-col">
|
||||||
|
{{ (assessment.assessment_type).split("LMS ")[1] }}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
|
||||||
|
<div class="col grid-static-col mb-2">
|
||||||
|
{% 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" %}
|
||||||
|
<div class="indicator-pill {{ color }}">
|
||||||
|
{{ status }}
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div>
|
||||||
|
{{ assessment.submission.score }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
<div class="indicator-pill red">
|
||||||
|
{{ _("Not Attempted") }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</article>
|
</article>
|
||||||
|
|||||||
@@ -21,16 +21,13 @@
|
|||||||
<div class="page-title">
|
<div class="page-title">
|
||||||
{{ quiz.title }}
|
{{ quiz.title }}
|
||||||
</div>
|
</div>
|
||||||
{% if submission.score %}
|
|
||||||
<div>
|
|
||||||
{{ submission.score }}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="vertically-center small">
|
<div class="vertically-center small">
|
||||||
<a class="dark-links" href="/classes">
|
<a class="dark-links" href="/classes">
|
||||||
{{ _("All Classes") }}
|
{{ _("All Classes") }}
|
||||||
</a>
|
</a>
|
||||||
|
<img class="icon icon-sm mr-0" src="/assets/lms/icons/chevron-right.svg">
|
||||||
|
<span class="breadcrumb-destination">{{ _("Quiz Submission") }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,25 @@ def get_context(context):
|
|||||||
["name", "score", "member", "member_name"],
|
["name", "score", "member", "member_name"],
|
||||||
as_dict=True,
|
as_dict=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
if not context.is_moderator and frappe.session.user != context.submission.member:
|
if not context.is_moderator and frappe.session.user != context.submission.member:
|
||||||
raise frappe.PermissionError(_("You don't have permission to access this page."))
|
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"))
|
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)
|
||||||
|
|||||||
@@ -110,22 +110,21 @@ def get_assignment_details(assessment, member):
|
|||||||
def get_quiz_details(assessment, member):
|
def get_quiz_details(assessment, member):
|
||||||
assessment.title = frappe.db.get_value("LMS Quiz", assessment.assessment_name, "title")
|
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,
|
"member": member,
|
||||||
"quiz": assessment.assessment_name,
|
"quiz": assessment.assessment_name,
|
||||||
}
|
},
|
||||||
|
["name", "score"],
|
||||||
|
order_by="creation desc",
|
||||||
)
|
)
|
||||||
|
|
||||||
if existing_submission:
|
if len(existing_submission):
|
||||||
assessment.submission = frappe.db.get_value(
|
assessment.submission = existing_submission[0]
|
||||||
"LMS Quiz Submission",
|
|
||||||
existing_submission,
|
|
||||||
["name", "score"],
|
|
||||||
as_dict=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
assessment.edit_url = f"/quizzes/{assessment.assessment_name}"
|
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}"
|
assessment.url = f"/quiz-submission/{assessment.assessment_name}/{submission_name}"
|
||||||
|
|||||||
Reference in New Issue
Block a user