diff --git a/lms/hooks.py b/lms/hooks.py index 64cc885a..4f2589f8 100644 --- a/lms/hooks.py +++ b/lms/hooks.py @@ -137,8 +137,8 @@ website_route_rules = [ {"from_route": "/courses//", "to_route": "courses/certificate"}, {"from_route": "/courses//learn", "to_route": "batch/learn"}, {"from_route": "/courses//learn/.", "to_route": "batch/learn"}, - {"from_route": "/courses/quiz-list", "to_route": "batch/quiz_list"}, - {"from_route": "/courses/quiz/", "to_route": "batch/quiz"}, + {"from_route": "/quizzes", "to_route": "batch/quiz_list"}, + {"from_route": "/quizzes/", "to_route": "batch/quiz"}, {"from_route": "/courses//progress", "to_route": "batch/progress"}, {"from_route": "/courses//join", "to_route": "batch/join"}, {"from_route": "/courses//manage", "to_route": "cohorts"}, diff --git a/lms/lms/doctype/lms_quiz/lms_quiz.py b/lms/lms/doctype/lms_quiz/lms_quiz.py index a4ad0e61..22d19337 100644 --- a/lms/lms/doctype/lms_quiz/lms_quiz.py +++ b/lms/lms/doctype/lms_quiz/lms_quiz.py @@ -10,6 +10,7 @@ class LMSQuiz(Document): def validate(self): self.validate_correct_answers() + def validate_correct_answers(self): for question in self.questions: correct_options = self.get_correct_options(question) @@ -20,10 +21,12 @@ class LMSQuiz(Document): if not len(correct_options): frappe.throw(_("At least one answer must be correct for this question: {0}").format(frappe.bold(question.question))) + def get_correct_options(self, question): correct_option_fields = ["is_correct_1", "is_correct_2", "is_correct_3", "is_correct_4"] return list(filter(lambda x: question.get(x) == 1, correct_option_fields)) + def get_last_submission_details(self): """Returns the latest submission for this user. """ @@ -43,6 +46,7 @@ class LMSQuiz(Document): if result: return result[0] + @frappe.whitelist() def quiz_summary(quiz, results): score = 0 diff --git a/lms/lms/widgets/CourseOutline.html b/lms/lms/widgets/CourseOutline.html index 72e76130..b146c3fd 100644 --- a/lms/lms/widgets/CourseOutline.html +++ b/lms/lms/widgets/CourseOutline.html @@ -1,14 +1,14 @@
{% if course.edit_mode and course.name %} -
- -
+ {% endif %} -
+ {% if course.name and (course.edit_mode or get_chapters(course.name) | length) %} +
{{ _("Course Content") }}
+ {% endif %} {% if get_chapters(course.name) | length %} diff --git a/lms/public/css/style.css b/lms/public/css/style.css index d1665e34..c0c223a4 100644 --- a/lms/public/css/style.css +++ b/lms/public/css/style.css @@ -910,8 +910,9 @@ pre { } .empty-state-text { - flex: 1; - margin-left: 1.25rem; + flex: 1; + margin-left: 1.25rem; + text-align: center; } .empty-state-heading { @@ -1546,6 +1547,7 @@ li { border-radius: var(--border-radius); border: 1px dashed var(--gray-600); padding: 0.5rem 0.75rem; + color: var(--gray-900); } [contenteditable]:empty:before { @@ -1599,3 +1601,22 @@ li { .table { margin-bottom: 0; } + +.quiz-card { + border: 1px solid var(--dark-border-color); + border-radius: var(--border-radius); + padding: 1.25rem; + margin-top: 1.25rem; + font-size: var(--text-base); +} + +.option-input { + width: 45%; + margin-right: 1rem; +} + +.option-checkbox { + width: 15%; + display: flex; + align-items: center; +} diff --git a/lms/public/js/common_functions.js b/lms/public/js/common_functions.js index b3ac0892..dedd0250 100644 --- a/lms/public/js/common_functions.js +++ b/lms/public/js/common_functions.js @@ -109,14 +109,16 @@ const add_chapter = (e) => { return; } - let next_index = $("[data-index]").last().data("index"); + let next_index = $("[data-index]").last().data("index") || 1; + let add_after = $(`.chapter-parent:last`).length ? $(`.chapter-parent:last`) : $("#outline-heading"); + console.log(add_after) $(`
-
+
-
`).insertAfter(`.chapter-parent:last`); +
`).insertAfter(add_after); scroll_to_chapter_container(); }; @@ -133,7 +135,9 @@ const scroll_to_chapter_container = () => { const save_chapter = (e) => { let target = $(e.currentTarget); let parent = target.closest(".chapter-parent"); - + console.log(parent) + console.log(parent.find(".chapter-description")) + debugger; frappe.call({ method: "lms.lms.doctype.lms_course.lms_course.save_chapter", args: { diff --git a/lms/www/batch/learn.html b/lms/www/batch/learn.html index fc837fe5..fdb5d9a7 100644 --- a/lms/www/batch/learn.html +++ b/lms/www/batch/learn.html @@ -186,7 +186,7 @@
- {{ _("Create a Quiz") }} + {{ _("Create a Quiz") }}
diff --git a/lms/www/batch/quiz.html b/lms/www/batch/quiz.html index e69de29b..619439a7 100644 --- a/lms/www/batch/quiz.html +++ b/lms/www/batch/quiz.html @@ -0,0 +1,74 @@ +{% extends "templates/base.html" %} +{% block title %} + {{ _("Quiz List") }} +{% endblock %} + + +{% block head_include %} + {% include "public/icons/symbol-defs.svg" %} +{% endblock %} + + +{% block content %} +
+
+ {{ BreadCrumb(quiz) }} + {{ QuizCard(quiz) }} +
+
+{% endblock %} + + +{% macro BreadCrumb(quiz) %} + +{% endmacro %} + + +{% macro QuizCard(quiz) %} +
+ +
{% if quiz.title %}{{ quiz.title }}{% endif %}
+ +
+ + +
+ + + {% if quiz.question %} + {% for question in quiz.questions %} +
+
{% if question.question %} {{ question.question }} {% endif %}
+ + {% for num in range(1,5) %} + {% set option = question["option_" + frappe.utils.cstr(num)] %} + {% set explanation = question["explanation_" + frappe.utils.cstr(num)] %} + +
+ +
+
{% if option %}{{ option }}{% endif %}
+
{% if explanation %}{{ explanation }}{% endif %}
+
+ + +
+
+
+ {% endfor %} +
+ + {% endfor %} + {% endif %} +
+{% endmacro %} + + diff --git a/lms/www/batch/quiz.js b/lms/www/batch/quiz.js new file mode 100644 index 00000000..86b98ddd --- /dev/null +++ b/lms/www/batch/quiz.js @@ -0,0 +1,91 @@ +frappe.ready(() => { + + $(".btn-question").click((e) => { + add_question(e); + }); + + $(".btn-save-question").click((e) => { + save_question(e); + }); + +}); + + +const add_question = (e) => { + let add_after = $(".quiz-card").length ? $(".quiz-card") : $("#quiz-title"); + let question_template = `
+
+
`; + $(question_template).insertAfter(add_after); + get_question_template(); + $(".btn-save-question").removeClass("hide"); +}; + + +const get_question_template = () => { + Array.from({length: 4}, (x, num) => { + let option_template = get_option_template(num + 1); + let add_after = $(".quiz-card:last .option-group").length ? $(".quiz-card:last .option-group").last() : $(".question:last"); + question_template = $(option_template).insertAfter(add_after); + }); +}; + + +const get_option_template = (num) => { + return `
+ +
+
+
+
+ + +
+
+
`; +}; + + +const save_question = (e) => { + if (!$("#quiz-title").text()) { + frappe.throw(__("Quiz Title is mandatory.")); + } + console.log(get_questions()); + debugger; + frappe.call({ + method: lms.lms.doctype.lms_quiz.lms_quiz.save_quiz, + args: { + "quiz-title": $("#quiz-title"), + "questions": get_questions() + }, + callback: (data) => { + + } + }); +}; + + +const get_questions = () => { + let questions = []; + + $(".quiz-card").each((i, elem) => { + + if (!$(elem).find(".question").text()) + return; + + let question_details = {}; + question_details["question"] = $(elem).find(".question").text(); + + Array.from({length: 4}, (x, i) => { + let num = i + 1; + question_details[`option_${num}`] = $(`.option-${num} .option-input:first`).text(); + question_details[`explanation_${num}`] = $(`.option-${num} .option-input:last`).text(); + question_details[`is_correct_${num}`] = $(`.option-${num} .option-checkbox`).find("input").prop("checked"); + }); + questions.push(question_details); + }); + + return questions +}; diff --git a/lms/www/batch/quiz.py b/lms/www/batch/quiz.py index e69de29b..ee0247df 100644 --- a/lms/www/batch/quiz.py +++ b/lms/www/batch/quiz.py @@ -0,0 +1,20 @@ +import frappe +from frappe.utils import cstr + + +def get_context(context): + quizname = frappe.form_dict["quizname"] + if quizname == "new-quiz": + context.quiz = frappe._dict() + context.quiz.edit_mode = 1 + else: + fields_arr = [] + for num in range(1,5): + fields_arr.append("option_" + cstr(num)) + fields_arr.append("is_correct_" + cstr(num)) + fields_arr.append("explanation_" + cstr(num)) + fields_arr.append("question") + context.quiz = frappe.db.get_value("LMS Quiz", quizname, ["title"], as_dict=1) + context.quiz.questions = frappe.get_all("LMS Quiz Question", { + "parent": quizname + }, fields_arr) diff --git a/lms/www/batch/quiz_list.html b/lms/www/batch/quiz_list.html index 44bda479..c8e3a853 100644 --- a/lms/www/batch/quiz_list.html +++ b/lms/www/batch/quiz_list.html @@ -12,7 +12,8 @@ {% block content %}
- {{ _("Add Quiz") }} + {% if quiz_list | length %} + {{ _("Add Quiz") }}
{{ _("Quiz List") }}
@@ -24,12 +25,23 @@ {% endfor %}
{{ loop.index }} - {{ quiz.name }} + {{ quiz.name }}
+ {% else %} +
+
+
{{ _("You have not created any quiz yet.") }}
+
{{ _("Create a quiz and add it to your course to engage users.") }}
+ + {{ _("Add Quiz") }} +
+
+ {% endif %}
{% endblock %} diff --git a/lms/www/courses/course.js b/lms/www/courses/course.js index a0537241..4dae5798 100644 --- a/lms/www/courses/course.js +++ b/lms/www/courses/course.js @@ -336,7 +336,7 @@ const save_course = (e) => { "video_link": $("#video-link").text(), "image": $("#image").attr("href"), "description": $("#description").text(), - "course": $("#title").data("course") + "course": $("#title").data("course") ? $("#title").data("course") : "" }, callback: (data) => { window.location.href = `/courses/${data.message}?edit=1`; @@ -344,6 +344,7 @@ const save_course = (e) => { }); }; + const remove_tag = (e) => { $(e.currentTarget).closest(".course-card-pills").remove(); }; diff --git a/lms/www/courses/course.py b/lms/www/courses/course.py index 8cfc2feb..fb83fd9b 100644 --- a/lms/www/courses/course.py +++ b/lms/www/courses/course.py @@ -11,6 +11,8 @@ def get_context(context): redirect_to_courses_list() if course_name == "new-course": + if frappe.session.user == "Guest": + redirect_to_courses_list() context.course = frappe._dict() context.course.edit_mode = True context.membership = None