From 70e1e550e342b1972f48fa6aaeb43568051598a6 Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Mon, 11 Apr 2022 15:35:40 +0530 Subject: [PATCH] fix: certificate request flow and other minor changes --- .../course_evaluator/course_evaluator.py | 16 +- lms/lms/doctype/lms_course/lms_course.json | 3 +- lms/public/css/style.css | 4 + lms/www/batch/learn.html | 12 +- lms/www/batch/learn.js | 100 ++++++++---- lms/www/courses/course.html | 13 +- lms/www/courses/course.js | 143 +++++++++++------- lms/www/courses/course.py | 3 +- lms/www/courses/index.py | 6 +- 9 files changed, 193 insertions(+), 107 deletions(-) diff --git a/lms/lms/doctype/course_evaluator/course_evaluator.py b/lms/lms/doctype/course_evaluator/course_evaluator.py index 282881e4..33b5ed1c 100644 --- a/lms/lms/doctype/course_evaluator/course_evaluator.py +++ b/lms/lms/doctype/course_evaluator/course_evaluator.py @@ -33,6 +33,18 @@ class CourseEvaluator(Document): frappe.throw(_("Slot Times are overlapping for some schedules.")) @frappe.whitelist() -def get_schedule(course): +def get_schedule(course, date): evaluator = frappe.db.get_value("LMS Course", course, "evaluator") - return frappe.get_all("Evaluator Schedule", filters={"parent": evaluator}, fields=["day", "start_time", "end_time"]) + all_slots = frappe.get_all("Evaluator Schedule", + filters = { "parent": evaluator }, + fields = ["day", "start_time", "end_time"]) + booked_slots = frappe.get_all("LMS Certificate Request", + filters = {"course": course, "date": date}, + fields = ["start_time"]) + + for slot in booked_slots: + same_slot = list(filter(lambda x: x.start_time == slot.start_time, all_slots)) + if len(same_slot): + all_slots.remove(same_slot[0]) + + return all_slots diff --git a/lms/lms/doctype/lms_course/lms_course.json b/lms/lms/doctype/lms_course/lms_course.json index 76cfcb11..8aa9b069 100644 --- a/lms/lms/doctype/lms_course/lms_course.json +++ b/lms/lms/doctype/lms_course/lms_course.json @@ -191,6 +191,7 @@ "fieldname": "evaluator", "fieldtype": "Link", "label": "Evaluator", + "mandatory_depends_on": "eval: doc.grant_certificate_after == \"Evaluation\"", "options": "Course Evaluator" }, { @@ -236,7 +237,7 @@ "link_fieldname": "course" } ], - "modified": "2022-04-07 12:27:05.353788", + "modified": "2022-04-08 14:36:22.254656", "modified_by": "Administrator", "module": "LMS", "name": "LMS Course", diff --git a/lms/public/css/style.css b/lms/public/css/style.css index 1d953fee..9c1b0986 100644 --- a/lms/public/css/style.css +++ b/lms/public/css/style.css @@ -1410,3 +1410,7 @@ pre { .course-content-parent .course-details-outline .course-home-headings { display: none; } + +.btn-outline-primary { + border: 1px solid var(--primary-color); +} diff --git a/lms/www/batch/learn.html b/lms/www/batch/learn.html index a9924c1d..9b218b71 100644 --- a/lms/www/batch/learn.html +++ b/lms/www/batch/learn.html @@ -34,11 +34,11 @@ {% macro BreadCrumb(course, lesson) %} {% endmacro %} @@ -94,7 +94,7 @@ {{ render_html(lesson.body) }} {% else %}
- {{ _("Start Learning") }} +
{{ _("Start Learning") }}
{{ _("This lesson is not available for preview. Please join the course to access it.") }}
{% endif %} diff --git a/lms/www/batch/learn.js b/lms/www/batch/learn.js index 21521e2f..5520e8f6 100644 --- a/lms/www/batch/learn.js +++ b/lms/www/batch/learn.js @@ -1,49 +1,53 @@ frappe.ready(() => { - localStorage.removeItem($("#quiz-title").text()); - fetch_assignments(); + localStorage.removeItem($("#quiz-title").text()); + fetch_assignments(); - save_current_lesson(); + save_current_lesson(); - $(".option").click((e) => { - enable_check(e); - }) + $(".option").click((e) => { + enable_check(e); + }) - $(".mark-progress").click((e) => { - mark_progress(e); - }); + $(".mark-progress").click((e) => { + mark_progress(e); + }); - $(".next").click((e) => { - mark_progress(e); - }); + $(".next").click((e) => { + mark_progress(e); + }); - $("#summary").click((e) => { - quiz_summary(e); - }); + $("#summary").click((e) => { + quiz_summary(e); + }); - $("#check").click((e) => { - check_answer(e); - }); + $("#check").click((e) => { + check_answer(e); + }); - $("#next").click((e) => { - mark_active_question(e); - }); + $("#next").click((e) => { + mark_active_question(e); + }); - $("#try-again").click((e) => { - try_quiz_again(e); - }); + $("#try-again").click((e) => { + try_quiz_again(e); + }); - $("#certification").click((e) => { - create_certificate(e); - }); + $("#certification").click((e) => { + create_certificate(e); + }); - $(".submit-work").click((e) => { - attach_work(e); - }); + $(".submit-work").click((e) => { + attach_work(e); + }); - $(".clear-work").click((e) => { - clear_work(e); - }); + $(".clear-work").click((e) => { + clear_work(e); + }); + + $(".join-batch").click((e) => { + join_course(e) + }); }); @@ -240,6 +244,36 @@ const add_to_local_storage = (quiz_name, current_index, answer, is_correct) => { localStorage.setItem(quiz_name, JSON.stringify(quiz_stored)) }; +const join_course = (e) => { + e.preventDefault(); + let course = $(e.currentTarget).attr("data-course") + if (frappe.session.user == "Guest") { + window.location.href = `/login?redirect-to=/courses/${course}`; + return; + } + + let batch = $(e.currentTarget).attr("data-batch"); + batch = batch ? decodeURIComponent(batch) : ""; + frappe.call({ + "method": "lms.lms.doctype.lms_batch_membership.lms_batch_membership.create_membership", + "args": { + "batch": batch ? batch : "", + "course": course + }, + "callback": (data) => { + if (data.message == "OK") { + frappe.msgprint({ + "title": __("Successfully Enrolled"), + "message": __("You are now a student of this course.") + }); + setTimeout(function () { + window.location.href = `/courses/${course}/learn/1.1`; + }, 2000); + } + } + }); +}; + const create_certificate = (e) => { e.preventDefault(); course = $(".title").attr("data-course"); diff --git a/lms/www/courses/course.html b/lms/www/courses/course.html index 418156d2..fff5f929 100644 --- a/lms/www/courses/course.html +++ b/lms/www/courses/course.html @@ -105,7 +105,7 @@ {{ _("You have opted to be notified for this course. You will receive an email when the course becomes available.") }} - {% if certificate_request %} + {% if certificate_request and not certificate %}

{{ _("Evaluation On: ") }} {{ _("{0} at {1}").format(frappe.utils.format_date(certificate_request.date, "medium"), frappe.utils.format_time(certificate_request.start_time, "short")) }}

@@ -199,7 +199,6 @@ {% endif %} - {% set certificate = is_certified(course.name) %} {% set progress = frappe.utils.cint(membership.progress) %} {% if membership and course.enable_certification %} {% if certificate %} @@ -225,7 +224,7 @@
- +
@@ -256,6 +255,10 @@

{{ _("There are no slots available on this day.") }}

+ diff --git a/lms/www/courses/course.js b/lms/www/courses/course.js index bed20644..dd6b102b 100644 --- a/lms/www/courses/course.js +++ b/lms/www/courses/course.js @@ -7,7 +7,7 @@ frappe.ready(() => { }); $(".join-batch").click((e) => { - join_course(e) + join_course(e); }); $(".view-all-mentors").click((e) => { @@ -46,10 +46,18 @@ frappe.ready(() => { display_slots(e); }); - $(document).on("click", ".slot", (e) => { + $("#submit-slot").click((e) => { submit_slot(e); }); + $(".close-slot-modal").click((e) => { + close_slot_modal(e); + }); + + $(document).on("click", ".slot", (e) => { + select_slot(e); + }); + $(document).scroll(function() { let timer; clearTimeout(timer); @@ -93,31 +101,35 @@ var cancel_mentor_request = (e) => { }) } -var join_course = (e) => { - e.preventDefault(); - var course = $(e.currentTarget).attr("data-course") - if (frappe.session.user == "Guest") { - window.location.href = `/login?redirect-to=/courses/${course}`; - return; - } - var batch = $(e.currentTarget).attr("data-batch"); - batch = batch ? decodeURIComponent(batch) : ""; - frappe.call({ - "method": "lms.lms.doctype.lms_batch_membership.lms_batch_membership.create_membership", - "args": { - "batch": batch ? batch : "", - "course": course - }, - "callback": (data) => { - if (data.message == "OK") { - frappe.msgprint(__("You are now a student of this course.")); - setTimeout(function () { - window.location.href = `/courses/${course}/learn/1.1`; - }, 2000); - } +const join_course = (e) => { + e.preventDefault(); + let course = $(e.currentTarget).attr("data-course"); + if (frappe.session.user == "Guest") { + window.location.href = `/login?redirect-to=/courses/${course}`; + return; } - }) -} + + let batch = $(e.currentTarget).attr("data-batch"); + batch = batch ? decodeURIComponent(batch) : ""; + frappe.call({ + "method": "lms.lms.doctype.lms_batch_membership.lms_batch_membership.create_membership", + "args": { + "batch": batch ? batch : "", + "course": course + }, + "callback": (data) => { + if (data.message == "OK") { + frappe.msgprint({ + "title": __("Successfully Enrolled"), + "message": __("You are now a student of this course.") + }); + setTimeout(function () { + window.location.href = `/courses/${course}/learn/1.1`; + }, 2000); + } + } + }) +}; var view_all_mentors = (e) => { $(".wrapped").each((i, element) => { @@ -251,29 +263,14 @@ const submit_for_review = (e) => { }; const apply_cetificate = (e) => { - frappe.call({ - method: "lms.lms.doctype.course_evaluator.course_evaluator.get_schedule", - args: { - "course": $(e.currentTarget).data("course") - }, - callback: (data) => { - let options = ""; - data.message.forEach((obj) => { - options += ``; - }); - e.preventDefault(); - $("#slot-modal .slots").html(options); - $("#slot-modal").modal("show"); - } - }) + $("#slot-modal").modal("show"); + + }; const submit_slot = (e) => { e.preventDefault(); - const slot = $(e.currentTarget); + const slot = window.selected_slot; frappe.call({ method: "lms.lms.doctype.lms_certificate_request.lms_certificate_request.create_certificate_request", args: { @@ -294,17 +291,51 @@ const submit_slot = (e) => { }; const display_slots = (e) => { - const weekday = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]; - const day = weekday[new Date($(e.currentTarget).val()).getDay()] + frappe.call({ + method: "lms.lms.doctype.course_evaluator.course_evaluator.get_schedule", + args: { + "course": $(e.currentTarget).data("course"), + "date": $(e.currentTarget).val() + }, + callback: (data) => { + let options = ""; + data.message.forEach((obj) => { + options += ``; + }); + e.preventDefault(); + $("#slot-modal .slots").html(options); + const weekday = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]; + const day = weekday[new Date($(e.currentTarget).val()).getDay()] - $(".slot").addClass("hide"); - $(".slot-label").addClass("hide"); + $(".slot").addClass("hide"); + $(".slot-label").addClass("hide"); - if ($(`[data-day='${day}']`).length) { - $(".slot-label").removeClass("hide"); - $(`[data-day='${day}']`).removeClass("hide"); - $("#no-slots-message").addClass("hide"); - } else { - $("#no-slots-message").removeClass("hide"); - } + if ($(`[data-day='${day}']`).length) { + $(".slot-label").removeClass("hide"); + $(`[data-day='${day}']`).removeClass("hide"); + $("#no-slots-message").addClass("hide"); + } else { + $("#no-slots-message").removeClass("hide"); + } + } + }); }; + +const select_slot = (e) => { + $(".slot").removeClass("btn-outline-primary"); + $(e.currentTarget).addClass("btn-outline-primary"); + window.selected_slot = $(e.currentTarget); +}; + +const format_time = (time) => { + let date = moment(new Date()).format("ddd MMM DD YYYY"); + return moment(`${date} ${time}`).format("HH:mm a"); +}; + +const close_slot_modal = (e) => { + $("#slot-date").val(""); + $(".slot-label").addClass("hide"); +} diff --git a/lms/www/courses/course.py b/lms/www/courses/course.py index 8bb55ffb..23598ba1 100644 --- a/lms/www/courses/course.py +++ b/lms/www/courses/course.py @@ -1,6 +1,6 @@ import frappe from lms.lms.doctype.lms_settings.lms_settings import check_profile_restriction -from lms.lms.utils import get_membership, is_instructor +from lms.lms.utils import get_membership, is_instructor, is_certified def get_context(context): context.no_cache = 1 @@ -32,6 +32,7 @@ def get_context(context): context.membership = membership context.restriction = check_profile_restriction() context.show_start_learing_cta = show_start_learing_cta(course, membership, context.restriction) + context.certificate = is_certified(course.name) context.certificate_request = frappe.db.get_value("LMS Certificate Request", { "course": course.name, diff --git a/lms/www/courses/index.py b/lms/www/courses/index.py index f35b3cc7..d7fda57d 100644 --- a/lms/www/courses/index.py +++ b/lms/www/courses/index.py @@ -14,9 +14,9 @@ def get_context(context): def get_courses(): courses = frappe.get_all("LMS Course", - filters={"published": True}, - fields=["name", "upcoming", "title", "image", "enable_certification", - "paid_certificate", "price_certificate", "currency"]) + filters={"published": True}, + fields=["name", "upcoming", "title", "image", "enable_certification", + "paid_certificate", "price_certificate", "currency"]) live_courses, upcoming_courses = [], [] for course in courses: