diff --git a/lms/lms/doctype/lms_batch/lms_batch.js b/lms/lms/doctype/lms_batch/lms_batch.js index 45f9d0b5..3028e564 100644 --- a/lms/lms/doctype/lms_batch/lms_batch.js +++ b/lms/lms/doctype/lms_batch/lms_batch.js @@ -10,25 +10,56 @@ frappe.ui.form.on("LMS Batch", { }, }; }); - }, - fetch_lessons: (frm) => { - frm.clear_table("scheduled_flow"); - frappe.call({ - method: "lms.lms.doctype.lms_batch.lms_batch.fetch_lessons", - args: { - courses: frm.doc.courses, - }, - callback: (r) => { - if (r.message) { - r.message.forEach((lesson) => { - let row = frm.add_child("scheduled_flow"); - row.lesson = lesson.name; - row.lesson_title = lesson.title; - }); - frm.refresh_field("scheduled_flow"); - } - }, + frm.set_query("reference_doctype", "timetable", function () { + let doctypes = ["Course Lesson", "LMS Quiz", "LMS Assignment"]; + return { + filters: { + name: ["in", doctypes], + }, + }; }); }, + + timetable_template: function (frm) { + if (frm.doc.timetable_template) { + frm.clear_table("timetable"); + frm.refresh_fields(); + + frappe.call({ + method: "frappe.client.get_list", + args: { + doctype: "LMS Batch Timetable", + parent: "LMS Timetable Template", + fields: [ + "reference_doctype", + "reference_docname", + "date", + "start_time", + "end_time", + ], + filters: { + parent: frm.doc.timetable_template, + }, + order_by: "idx", + }, + callback: (data) => { + add_timetable_rows(frm, data.message); + }, + }); + } + }, }); + +const add_timetable_rows = (frm, timetable) => { + timetable.forEach((row) => { + let child = frm.add_child("timetable"); + child.reference_doctype = row.reference_doctype; + child.reference_docname = row.reference_docname; + child.date = row.date; + child.start_time = row.start_time; + child.end_time = row.end_time; + }); + frm.refresh_field("timetable"); + frm.save(); +}; diff --git a/lms/lms/doctype/lms_batch/lms_batch.json b/lms/lms/doctype/lms_batch/lms_batch.json index a7949ce3..2f916303 100644 --- a/lms/lms/doctype/lms_batch/lms_batch.json +++ b/lms/lms/doctype/lms_batch/lms_batch.json @@ -35,8 +35,11 @@ "assessment_tab", "assessment", "schedule_tab", - "fetch_lessons", - "scheduled_flow" + "timetable_template", + "column_break_anya", + "show_live_class", + "section_break_ontp", + "timetable" ], "fields": [ { @@ -146,25 +149,14 @@ "fieldtype": "Autocomplete", "label": "Category" }, - { - "fieldname": "scheduled_flow", - "fieldtype": "Table", - "label": "Scheduled Flow", - "options": "Scheduled Flow" - }, { "fieldname": "section_break_ubxi", "fieldtype": "Section Break" }, - { - "fieldname": "fetch_lessons", - "fieldtype": "Button", - "label": "Fetch Lessons" - }, { "fieldname": "schedule_tab", "fieldtype": "Tab Break", - "label": "Schedule" + "label": "Timetable" }, { "fieldname": "section_break_gsac", @@ -199,11 +191,37 @@ "fieldname": "published", "fieldtype": "Check", "label": "Published" + }, + { + "fieldname": "timetable", + "fieldtype": "Table", + "label": "Timetable", + "options": "LMS Batch Timetable" + }, + { + "fieldname": "timetable_template", + "fieldtype": "Link", + "label": "Timetable Template", + "options": "LMS Timetable Template" + }, + { + "fieldname": "column_break_anya", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "show_live_class", + "fieldtype": "Check", + "label": "Show Live Class" + }, + { + "fieldname": "section_break_ontp", + "fieldtype": "Section Break" } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2023-09-12 12:30:06.565104", + "modified": "2023-09-20 11:25:10.683688", "modified_by": "Administrator", "module": "LMS", "name": "LMS Batch", diff --git a/lms/lms/doctype/lms_batch/lms_batch.py b/lms/lms/doctype/lms_batch/lms_batch.py index cff87d28..f7e824d7 100644 --- a/lms/lms/doctype/lms_batch/lms_batch.py +++ b/lms/lms/doctype/lms_batch/lms_batch.py @@ -6,9 +6,11 @@ import requests import base64 import json from frappe import _ +from datetime import timedelta from frappe.model.document import Document from frappe.utils import cint, format_date, format_datetime -from lms.lms.utils import get_lessons +from lms.lms.utils import get_lessons, get_lesson_index, get_lesson_url +from lms.www.utils import get_quiz_details, get_assignment_details class LMSBatch(Document): @@ -19,7 +21,7 @@ class LMSBatch(Document): self.validate_duplicate_students() self.validate_duplicate_assessments() self.validate_membership() - self.validate_schedule() + self.validate_timetable() def validate_duplicate_students(self): students = [row.student for row in self.students] @@ -68,8 +70,8 @@ class LMSBatch(Document): if cint(self.seat_count) < len(self.students): frappe.throw(_("There are no seats available in this batch.")) - def validate_schedule(self): - for schedule in self.scheduled_flow: + def validate_timetable(self): + for schedule in self.timetable: if schedule.start_time and schedule.end_time: if ( schedule.start_time > schedule.end_time or schedule.start_time == schedule.end_time @@ -266,3 +268,66 @@ def add_course(course, parent, name=None, evaluator=None): doc.save() return doc.name + + +@frappe.whitelist() +def get_batch_timetable(batch): + timetable = frappe.get_all( + "LMS Batch Timetable", + filters={"parent": batch}, + fields=["reference_doctype", "reference_docname", "date", "start_time", "end_time"], + order_by="date", + ) + + show_live_class = frappe.db.get_value("LMS Batch", batch, "show_live_class") + if show_live_class: + live_classes = get_live_classes(batch) + timetable.extend(live_classes) + + timetable = get_timetable_details(timetable) + return timetable + + +def get_live_classes(batch): + live_classes = frappe.get_all( + "LMS Live Class", + {"batch_name": batch}, + ["name", "title", "date", "time as start_time", "duration", "join_url as url"], + order_by="date", + ) + for class_ in live_classes: + class_.end_time = class_.start_time + timedelta(minutes=class_.duration) + class_.reference_doctype = "LMS Live Class" + class_.reference_docname = class_.name + class_.icon = "icon-call" + + return live_classes + + +def get_timetable_details(timetable): + for entry in timetable: + entry.title = frappe.db.get_value( + entry.reference_doctype, entry.reference_docname, "title" + ) + assessment = frappe._dict({"assessment_name": entry.reference_docname}) + + if entry.reference_doctype == "Course Lesson": + entry.icon = "icon-list" + course = frappe.db.get_value( + entry.reference_doctype, entry.reference_docname, "course" + ) + entry.url = get_lesson_url(course, get_lesson_index(entry.reference_docname)) + + elif entry.reference_doctype == "LMS Quiz": + entry.icon = "icon-quiz" + entry.url = "/quizzes" + details = get_quiz_details(assessment, frappe.session.user) + entry.update(details) + + elif entry.reference_doctype == "LMS Assignment": + entry.icon = "icon-quiz" + details = get_assignment_details(assessment, frappe.session.user) + entry.update(details) + + timetable = sorted(timetable, key=lambda k: k["date"]) + return timetable diff --git a/lms/lms/doctype/lms_batch_timetable/__init__.py b/lms/lms/doctype/lms_batch_timetable/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/lms/lms/doctype/lms_batch_timetable/lms_batch_timetable.js b/lms/lms/doctype/lms_batch_timetable/lms_batch_timetable.js new file mode 100644 index 00000000..4cecd9c3 --- /dev/null +++ b/lms/lms/doctype/lms_batch_timetable/lms_batch_timetable.js @@ -0,0 +1,8 @@ +// Copyright (c) 2023, Frappe and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("LMS Batch Timetable", { +// refresh(frm) { + +// }, +// }); diff --git a/lms/lms/doctype/lms_batch_timetable/lms_batch_timetable.json b/lms/lms/doctype/lms_batch_timetable/lms_batch_timetable.json new file mode 100644 index 00000000..1eacc738 --- /dev/null +++ b/lms/lms/doctype/lms_batch_timetable/lms_batch_timetable.json @@ -0,0 +1,81 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "hash", + "creation": "2023-09-14 12:44:51.098956", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "column_break_htdc", + "reference_doctype", + "reference_docname", + "date", + "column_break_merq", + "start_time", + "end_time", + "duration" + ], + "fields": [ + { + "fieldname": "reference_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Reference DocType", + "options": "DocType" + }, + { + "fieldname": "reference_docname", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "label": "Reference DocName", + "options": "reference_doctype" + }, + { + "fieldname": "column_break_merq", + "fieldtype": "Column Break" + }, + { + "fieldname": "date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Date" + }, + { + "fieldname": "start_time", + "fieldtype": "Time", + "in_list_view": 1, + "label": "Start Time" + }, + { + "fieldname": "duration", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Duration" + }, + { + "fieldname": "column_break_htdc", + "fieldtype": "Column Break" + }, + { + "fieldname": "end_time", + "fieldtype": "Time", + "in_list_view": 1, + "label": "End Time" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2023-09-15 10:35:40.642660", + "modified_by": "Administrator", + "module": "LMS", + "name": "LMS Batch Timetable", + "naming_rule": "Random", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/lms/lms/doctype/lms_batch_timetable/lms_batch_timetable.py b/lms/lms/doctype/lms_batch_timetable/lms_batch_timetable.py new file mode 100644 index 00000000..257896a6 --- /dev/null +++ b/lms/lms/doctype/lms_batch_timetable/lms_batch_timetable.py @@ -0,0 +1,9 @@ +# Copyright (c) 2023, Frappe and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class LMSBatchTimetable(Document): + pass diff --git a/lms/lms/doctype/lms_batch_timetable/test_lms_batch_timetable.py b/lms/lms/doctype/lms_batch_timetable/test_lms_batch_timetable.py new file mode 100644 index 00000000..68754a5f --- /dev/null +++ b/lms/lms/doctype/lms_batch_timetable/test_lms_batch_timetable.py @@ -0,0 +1,9 @@ +# Copyright (c) 2023, Frappe and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestLMSBatchTimetable(FrappeTestCase): + pass diff --git a/lms/lms/doctype/lms_live_class/lms_live_class.json b/lms/lms/doctype/lms_live_class/lms_live_class.json index 1e68dbfc..74e21479 100644 --- a/lms/lms/doctype/lms_live_class/lms_live_class.json +++ b/lms/lms/doctype/lms_live_class/lms_live_class.json @@ -10,16 +10,16 @@ "title", "host", "batch_name", - "password", - "auto_recording", "column_break_astv", - "description", - "section_break_glxh", "date", - "timezone", - "column_break_spvt", "time", "duration", + "section_break_glxh", + "description", + "column_break_spvt", + "timezone", + "password", + "auto_recording", "section_break_yrpq", "start_url", "column_break_yokr", @@ -126,7 +126,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2023-03-14 18:44:48.813103", + "modified": "2023-09-20 11:29:20.899897", "modified_by": "Administrator", "module": "LMS", "name": "LMS Live Class", @@ -157,8 +157,10 @@ "write": 1 } ], + "show_title_field_in_link": 1, "sort_field": "modified", "sort_order": "DESC", "states": [], + "title_field": "title", "track_changes": 1 } \ No newline at end of file diff --git a/lms/lms/doctype/lms_timetable_template/__init__.py b/lms/lms/doctype/lms_timetable_template/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/lms/lms/doctype/lms_timetable_template/lms_timetable_template.js b/lms/lms/doctype/lms_timetable_template/lms_timetable_template.js new file mode 100644 index 00000000..a9bff04e --- /dev/null +++ b/lms/lms/doctype/lms_timetable_template/lms_timetable_template.js @@ -0,0 +1,15 @@ +// Copyright (c) 2023, Frappe and contributors +// For license information, please see license.txt + +frappe.ui.form.on("LMS Timetable Template", { + refresh(frm) { + frm.set_query("reference_doctype", "timetable", function () { + let doctypes = ["Course Lesson", "LMS Quiz", "LMS Assignment"]; + return { + filters: { + name: ["in", doctypes], + }, + }; + }); + }, +}); diff --git a/lms/lms/doctype/lms_timetable_template/lms_timetable_template.json b/lms/lms/doctype/lms_timetable_template/lms_timetable_template.json new file mode 100644 index 00000000..99c88628 --- /dev/null +++ b/lms/lms/doctype/lms_timetable_template/lms_timetable_template.json @@ -0,0 +1,65 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "hash", + "creation": "2023-09-18 14:16:16.964077", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "title", + "timetable" + ], + "fields": [ + { + "fieldname": "title", + "fieldtype": "Data", + "label": "Title" + }, + { + "fieldname": "timetable", + "fieldtype": "Table", + "label": "Timetable", + "options": "LMS Batch Timetable" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2023-09-18 17:57:15.819072", + "modified_by": "Administrator", + "module": "LMS", + "name": "LMS Timetable Template", + "naming_rule": "Random", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Moderator", + "share": 1, + "write": 1 + } + ], + "show_title_field_in_link": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "title" +} \ No newline at end of file diff --git a/lms/lms/doctype/lms_timetable_template/lms_timetable_template.py b/lms/lms/doctype/lms_timetable_template/lms_timetable_template.py new file mode 100644 index 00000000..21e7c70d --- /dev/null +++ b/lms/lms/doctype/lms_timetable_template/lms_timetable_template.py @@ -0,0 +1,9 @@ +# Copyright (c) 2023, Frappe and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class LMSTimetableTemplate(Document): + pass diff --git a/lms/lms/doctype/lms_timetable_template/test_lms_timetable_template.py b/lms/lms/doctype/lms_timetable_template/test_lms_timetable_template.py new file mode 100644 index 00000000..090d1a58 --- /dev/null +++ b/lms/lms/doctype/lms_timetable_template/test_lms_timetable_template.py @@ -0,0 +1,9 @@ +# Copyright (c) 2023, Frappe and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestLMSTimetableTemplate(FrappeTestCase): + pass diff --git a/lms/public/css/style.css b/lms/public/css/style.css index e414f449..e79eec3b 100644 --- a/lms/public/css/style.css +++ b/lms/public/css/style.css @@ -2352,4 +2352,94 @@ select { left: 80%; z-index: 10; width: fit-content; +} + +.toastui-calendar-milestone { + display: none; +} + +.toastui-calendar-task { + display: none; +} + +.toastui-calendar-panel-resizer { + display: none; +} + +.toastui-calendar-day-name__date { + font-size: var(--text-base) !important; +} + +.toastui-calendar-day-name__name { + font-size: var(--text-base) !important; +} + +.toastui-calendar-day-view-day-names, .toastui-calendar-week-view-day-names { + border-bottom: none !important; +} + +.toastui-calendar-layout { + border: 1px solid var(--gray-200) !important; + border-radius: var(--border-radius-md) !important; + background-color: var(--gray-100) !important; +} + +.toastui-calendar-panel .toastui-calendar-day-names.toastui-calendar-week { + border-top: none !important; +} + +.toastui-calendar-panel.toastui-calendar-time { + height: 80% !important; +} + +.toastui-calendar-panel.toastui-calendar-week-view-day-names { + background-color: var(--gray-50) !important; +} + +.toastui-calendar-allday { + border-bottom: 1px solid var(--gray-200) !important; +} + +.calendar-navigation { + display: flex; + justify-content: center; + align-items: center; + margin-bottom: 1rem; +} + +.calendar-range { + margin: 0 2rem; + font-weight: 500; + color: var(--text-color); +} + +.calendar-event-title { + font-size: var(--text-md); + font-weight: 500; + margin-top: 0.2rem; +} + +.legend-color { + width: 50px; + height: 20px; + border-radius: var(--border-radius-sm); + margin-right: 0.25rem; +} + +.legend-item { + display: flex; + align-items: center; +} + +.legend-text { + color: var(--text-color); + font-weight: 500; +} + +.calendar-legends { + display: flex; + align-items: center; + justify-content: space-between; + width: 50%; + margin: 0 auto 1rem; } \ No newline at end of file diff --git a/lms/www/batches/batch.html b/lms/www/batches/batch.html index b6398a26..bbfa676c 100644 --- a/lms/www/batches/batch.html +++ b/lms/www/batches/batch.html @@ -105,13 +105,10 @@ - {% if flow | length %} + {% if show_timetable %} {% endif %} @@ -169,9 +166,9 @@ {{ CoursesSection(batch_info, batch_courses) }} - {% if flow | length %} -
- {{ ScheduleSection(flow) }} + {% if show_timetable %} +
+ {{ Timetable() }}
{% endif %} @@ -513,79 +510,38 @@ {% endmacro %} -{% macro ScheduleSection(flow) %} +{% macro Timetable() %}
- {{ _("Schedule") }} + {{ _("Timetable") }}
- -
- {% for chapter in flow %} -
-
- -
- {{ chapter.chapter_title }} -
-
- -
+
+ + + +
+
+ {% for legend in legends %} +
+
+
{{ legend.title }}
+
{% endfor %}
+
+
+
{% endmacro %} @@ -595,4 +551,6 @@ frappe.boot.single_types = [] let courses = {{ course_list | json }}; + + {% endblock %} \ No newline at end of file diff --git a/lms/www/batches/batch.js b/lms/www/batches/batch.js index 1fdefaa3..8dfc84a9 100644 --- a/lms/www/batches/batch.js +++ b/lms/www/batches/batch.js @@ -2,6 +2,24 @@ frappe.ready(() => { let self = this; frappe.require("controls.bundle.js"); + if ($("#calendar").length) { + setup_timetable(); + } + + if ($("#calendar").length) { + $(document).on("click", "#prev-week", (e) => { + this.calendar_ && this.calendar_.prev(); + set_calendar_range(this.calendar_, this.events); + }); + } + + if ($("#calendar").length) { + $(document).on("click", "#next-week", (e) => { + this.calendar_ && this.calendar_.next(); + set_calendar_range(this.calendar_, this.events); + }); + } + if ($("#live-class-form").length) { setTimeout(() => { make_live_class_form(); @@ -606,3 +624,144 @@ const submit_evaluation_form = (values) => { }, }); }; + +const setup_timetable = () => { + let self = this; + frappe.call({ + method: "lms.lms.doctype.lms_batch.lms_batch.get_batch_timetable", + args: { + batch: $(".class-details").data("batch"), + }, + callback: (r) => { + if (r.message.length) { + setup_calendar(r.message); + self.events = r.message; + } + }, + }); +}; + +const setup_calendar = (events) => { + const element = $("#calendar"); + const Calendar = tui.Calendar; + const calendar_id = "calendar1"; + const container = element[0]; + const options = get_calendar_options(element, calendar_id); + const calendar = new Calendar(container, options); + this.calendar_ = calendar; + console.log(options); + create_events(calendar, events); + add_links_to_events(calendar, events); + scroll_to_date(calendar, events); + set_calendar_range(calendar, events); +}; + +const get_calendar_options = (element, calendar_id) => { + const start_time = element.data("start"); + const end_time = element.data("end"); + + return { + defaultView: "week", + usageStatistics: false, + week: { + narrowWeekend: true, + hourStart: parseInt(start_time.split(":")[0]) - 1, + /* hourEnd: parseInt(end_time.split(":")[0]) + 1, */ + }, + month: { + narrowWeekend: true, + }, + taskView: false, + isReadOnly: true, + calendars: [ + { + id: calendar_id, + name: "Timetable", + backgroundColor: "var(--fg-color)", + }, + ], + template: { + time: function (event) { + return `
+
${frappe.datetime.get_time(event.start.d.d)} - + ${frappe.datetime.get_time(event.end.d.d)}
+
${event.title}
+
`; + }, + }, + }; +}; + +const create_events = (calendar, events, calendar_id) => { + let calendar_events = []; + + events.forEach((event, idx) => { + calendar_events.push({ + id: `event${idx}`, + calendarId: calendar_id, + title: event.title, + start: `${event.date}T${event.start_time}`, + end: `${event.date}T${event.end_time}`, + isAllday: event.start_time ? false : true, + borderColor: get_background_color(event.reference_doctype), + backgroundColor: "var(--fg-color)", + customStyle: { + borderRadius: "var(--border-radius-md)", + boxShadow: "var(--shadow-base)", + borderWidth: "8px", + padding: "0.25rem 0.5rem 0.5rem", + }, + raw: { + url: event.url, + }, + }); + }); + + calendar.createEvents(calendar_events); +}; + +const add_links_to_events = (calendar, events) => { + calendar.on("clickEvent", ({ event }) => { + const el = document.getElementById("clicked-event"); + window.open(event.raw.url, "_blank"); + }); +}; + +const scroll_to_date = (calendar, events) => { + if ( + new Date() < new Date(events[0].date) || + new Date() > new Date(events.slice(-1).date) + ) { + calendar.setDate(new Date(events[0].date)); + } +}; + +const set_calendar_range = (calendar, events) => { + let week_start = moment(calendar.getDateRangeStart().d.d); + let week_end = moment(calendar.getDateRangeEnd().d.d); + + $(".calendar-range").text( + `${moment(week_start).format("DD MMMM YYYY")} - ${moment( + week_end + ).format("DD MMMM YYYY")}` + ); + + if (week_start.diff(moment(events[0].date), "days") <= 0) { + $("#prev-week").hide(); + } else { + $("#prev-week").show(); + } + + if (week_end.diff(moment(events.slice(-1)[0].date), "days") > 0) { + $("#next-week").hide(); + } else { + $("#next-week").show(); + } +}; + +const get_background_color = (doctype) => { + if (doctype == "Course Lesson") return "var(--blue-400)"; + if (doctype == "LMS Quiz") return "var(--green-400)"; + if (doctype == "LMS Assignment") return "var(--orange-400)"; + if (doctype == "LMS Live Class") return "var(--purple-400)"; +}; diff --git a/lms/www/batches/batch.py b/lms/www/batches/batch.py index 05d7b001..00aac227 100644 --- a/lms/www/batches/batch.py +++ b/lms/www/batches/batch.py @@ -1,6 +1,6 @@ from frappe import _ import frappe -from frappe.utils import getdate, cint +from frappe.utils import getdate, get_datetime from lms.www.utils import get_assessments, is_student from lms.lms.utils import ( has_course_moderator_role, @@ -89,7 +89,13 @@ def get_context(context): ) context.all_assignments = get_all_assignments(batch_name) context.all_quizzes = get_all_quizzes(batch_name) - context.flow = get_scheduled_flow(batch_name) + context.show_timetable = frappe.db.count( + "LMS Batch Timetable", + { + "parent": batch_name, + }, + ) + context.legends = get_legends() def get_all_quizzes(batch_name): @@ -210,38 +216,6 @@ def sort_students(batch_students): return batch_students -def get_scheduled_flow(batch_name): - chapters = [] - - lessons = frappe.get_all( - "Scheduled Flow", - {"parent": batch_name}, - ["name", "lesson", "date", "start_time", "end_time"], - order_by="idx", - ) - - for lesson in lessons: - lesson = get_lesson_details(lesson, batch_name) - chapter_exists = [ - chapter for chapter in chapters if chapter.chapter == lesson.chapter - ] - - if len(chapter_exists) == 0: - chapters.append( - frappe._dict( - { - "chapter": lesson.chapter, - "chapter_title": frappe.db.get_value("Course Chapter", lesson.chapter, "title"), - "lessons": [lesson], - } - ) - ) - else: - chapter_exists[0]["lessons"].append(lesson) - - return chapters - - def get_lesson_details(lesson, batch_name): lesson.update( frappe.db.get_value( @@ -277,3 +251,24 @@ def get_course_progress(batch_courses, student_details): student_details.courses[course.course] = membership.progress else: student_details.courses[course.course] = 0 + + +def get_legends(): + return [ + { + "title": "Lesson", + "color": "var(--blue-400)", + }, + { + "title": "Quiz", + "color": "var(--green-400)", + }, + { + "title": "Assignment", + "color": "var(--orange-400)", + }, + { + "title": "Live Class", + "color": "var(--purple-400)", + }, + ] diff --git a/lms/www/batches/batch_details.html b/lms/www/batches/batch_details.html index 5ed9fe76..b1967306 100644 --- a/lms/www/batches/batch_details.html +++ b/lms/www/batches/batch_details.html @@ -136,9 +136,9 @@ {% endif %}
- {% if is_moderator or is_evaluator or is_student %} + {% if is_moderator or is_evaluator %} - {{ _("Checkout Batch") }} + {{ _("Manage Batch") }} {% elif batch_info.paid_batch %}