feat: batch timetable
This commit is contained in:
@@ -88,7 +88,7 @@
|
||||
|
||||
<ul class="nav lms-nav" id="batches-tab">
|
||||
|
||||
{% if is_student %}
|
||||
{% if is_student or is_moderator %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if is_student %} active {% endif %}" data-toggle="tab" href="#dashboard">
|
||||
{{ _("Dashboard") }}
|
||||
@@ -105,13 +105,10 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
{% if flow | length %}
|
||||
{% if show_timetable %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="tab" href="#schedule">
|
||||
{{ _("Schedule") }}
|
||||
<span class="course-list-count">
|
||||
{{ flow | length }}
|
||||
</span>
|
||||
<a class="nav-link" data-toggle="tab" href="#timetable">
|
||||
{{ _("Timetable") }}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
@@ -169,9 +166,9 @@
|
||||
{{ CoursesSection(batch_info, batch_courses) }}
|
||||
</div>
|
||||
|
||||
{% if flow | length %}
|
||||
<div class="tab-pane" id="schedule" role="tabpanel" aria-labelledby="schedule">
|
||||
{{ ScheduleSection(flow) }}
|
||||
{% if show_timetable %}
|
||||
<div class="tab-pane" id="timetable" role="tabpanel" aria-labelledby="timetable">
|
||||
{{ Timetable() }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
@@ -513,79 +510,49 @@
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{% macro ScheduleSection(flow) %}
|
||||
{% macro Timetable() %}
|
||||
<article>
|
||||
<header class="edit-header mb-5">
|
||||
<div class="bold-heading">
|
||||
{{ _("Schedule") }}
|
||||
{{ _("Timetable") }}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div>
|
||||
{% for chapter in flow %}
|
||||
<div class="chapter-parent">
|
||||
<div class="chapter-title" data-toggle="collapse" data-target="#{{ get_slugified_chapter_title(chapter.chapter_title) }}">
|
||||
<img class="chapter-icon" src="/assets/lms/icons/chevron-right.svg">
|
||||
<div class="chapter-title-main">
|
||||
{{ chapter.chapter_title }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="chapter-content lessons collapse navbar-collapse" id="{{ get_slugified_chapter_title(chapter.chapter_title) }}">
|
||||
|
||||
<div class="schedule-header">
|
||||
<div class="w-50">
|
||||
{{ _("Lesson") }}
|
||||
</div>
|
||||
<div class="w-25">
|
||||
{{ _("Date") }}
|
||||
</div>
|
||||
<div class="w-25 text-center">
|
||||
{{ _("Start Time") }}
|
||||
</div>
|
||||
<div class="w-25 text-center">
|
||||
{{ _("End Time") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% for lesson in chapter.lessons %}
|
||||
<div class="lesson-info flex align-center">
|
||||
<a class="lesson-links w-50" href="{{ lesson.url }}">
|
||||
<svg class="icon icon-sm mr-2">
|
||||
<use class="" href="#{{ lesson.icon }}">
|
||||
</svg>
|
||||
|
||||
{{ lesson.title }}
|
||||
|
||||
{% if current_student.name and get_membership(lesson.course, current_student.name) %}
|
||||
{% set lesson_progress = get_progress(lesson.course, lesson.name, current_student.name) %}
|
||||
<svg class="icon icon-md lesson-progress-tick ml-3 {% if lesson_progress != 'Complete' %} hide {% endif %}">
|
||||
<use class="" href="#icon-success">
|
||||
</svg>
|
||||
{% endif %}
|
||||
</a>
|
||||
<div class="w-25">
|
||||
{{ frappe.utils.format_date(lesson.date, "medium") }}
|
||||
</div>
|
||||
<div class="w-25 text-center">
|
||||
{% if lesson.start_time %}
|
||||
{{ frappe.utils.format_time(lesson.start_time, "HH:mm a") }}
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="w-25 text-center">
|
||||
{% if lesson.end_time %}
|
||||
{{ frappe.utils.format_time(lesson.end_time, "HH:mm a") }}
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="calendar-navigation">
|
||||
<button class="btn icon-btn btn-default" id="prev-week">
|
||||
<svg class="icon icon-md">
|
||||
<use href="#icon-left"></use>
|
||||
</svg>
|
||||
</button>
|
||||
<span class="calendar-range"></span>
|
||||
<button class="btn icon-btn btn-default" id="next-week">
|
||||
<svg class="icon icon-md">
|
||||
<use href="#icon-right"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div id="calendar" class="timetable-calendar" style="height: 700px"
|
||||
data-start="{{ batch_info.start_time }}" data-end="{{ batch_info.end_time }}">
|
||||
</div>
|
||||
|
||||
<!-- <div>
|
||||
{% for week in timetable %}
|
||||
<div>
|
||||
<div class="bold-heading">
|
||||
{{ _("Week ") }} {{ loop.index }}
|
||||
</div>
|
||||
<div>
|
||||
{% for entry in timetable[week] %}
|
||||
<a {% if is_student %} href="{{ entry.url }}" {% endif %}>
|
||||
<svg class="icon icon-md">
|
||||
<use href="#{{ entry.icon }}"></use>
|
||||
</svg>
|
||||
{{ entry.title }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div> -->
|
||||
</article>
|
||||
{% endmacro %}
|
||||
|
||||
@@ -595,4 +562,6 @@
|
||||
frappe.boot.single_types = []
|
||||
let courses = {{ course_list | json }};
|
||||
</script>
|
||||
<link rel="stylesheet" href="https://uicdn.toast.com/calendar/latest/toastui-calendar.min.css" />
|
||||
<script src="https://uicdn.toast.com/calendar/latest/toastui-calendar.min.js"></script>
|
||||
{% endblock %}
|
||||
@@ -2,6 +2,22 @@ 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();
|
||||
});
|
||||
}
|
||||
|
||||
if ($("#calendar").length) {
|
||||
$(document).on("click", "#next-week", (e) => {
|
||||
this.calendar_ && this.calendar_.next();
|
||||
});
|
||||
}
|
||||
|
||||
if ($("#live-class-form").length) {
|
||||
setTimeout(() => {
|
||||
make_live_class_form();
|
||||
@@ -606,3 +622,124 @@ const submit_evaluation_form = (values) => {
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const setup_timetable = () => {
|
||||
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);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const setup_calendar = (events) => {
|
||||
const element = $("#calendar");
|
||||
const Calendar = tui.Calendar;
|
||||
let calendar_events = [];
|
||||
let calendar_id = "calendar1";
|
||||
const container = element[0];
|
||||
const start_time = $(elemet).data("start");
|
||||
const end_time = $(elemet).data("end");
|
||||
|
||||
const options = {
|
||||
defaultView: "week",
|
||||
usageStatistics: false,
|
||||
week: {
|
||||
narrowWeekend: true,
|
||||
hourStart: 7,
|
||||
hourEnd: 18,
|
||||
},
|
||||
month: {
|
||||
narrowWeekend: true,
|
||||
},
|
||||
taskView: false,
|
||||
isReadOnly: true,
|
||||
calendars: [
|
||||
{
|
||||
id: calendar_id,
|
||||
name: "Timetable",
|
||||
backgroundColor: "#ffffff",
|
||||
},
|
||||
],
|
||||
template: {
|
||||
time: function (event) {
|
||||
return `<div class="calendar-event-time">
|
||||
<div> ${frappe.datetime.get_time(event.start.d.d)} -
|
||||
${frappe.datetime.get_time(event.end.d.d)} </div>
|
||||
<div class="calendar-event-title"> ${event.title} </div>
|
||||
</div>`;
|
||||
},
|
||||
},
|
||||
};
|
||||
const calendar = new Calendar(container, options);
|
||||
this.calendar_ = calendar;
|
||||
|
||||
events.forEach((event, idx) => {
|
||||
let colors = get_background_color(event.reference_doctype);
|
||||
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: colors.dark,
|
||||
customStyle: {
|
||||
borderRadius: "var(--border-radius-md)",
|
||||
boxShadow: "var(--shadow-base)",
|
||||
borderWidth: "8px",
|
||||
padding: "1rem",
|
||||
},
|
||||
raw: {
|
||||
url: event.url,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
calendar.createEvents(calendar_events);
|
||||
|
||||
calendar.on("clickEvent", ({ event }) => {
|
||||
const el = document.getElementById("clicked-event");
|
||||
window.open(event.raw.url, "_blank");
|
||||
});
|
||||
|
||||
if (new Date().getMonth() < new Date(events[0].date).getMonth()) {
|
||||
calendar.setDate(new Date(events[0].date));
|
||||
}
|
||||
|
||||
let week_start = frappe.datetime.global_date_format(
|
||||
calendar.getDateRangeStart().d.d
|
||||
);
|
||||
let week_end = frappe.datetime.global_date_format(
|
||||
calendar.getDateRangeEnd().d.d
|
||||
);
|
||||
$(".calendar-range").text(`${week_start} - ${week_end}`);
|
||||
};
|
||||
|
||||
const get_background_color = (doctype) => {
|
||||
if (doctype == "Course Lesson")
|
||||
return {
|
||||
light: "var(--blue-50)",
|
||||
dark: "var(--blue-400)",
|
||||
};
|
||||
if (doctype == "LMS Quiz")
|
||||
return {
|
||||
light: "var(--green-50)",
|
||||
dark: "var(--green-400)",
|
||||
};
|
||||
if (doctype == "LMS Assignment")
|
||||
return {
|
||||
light: "var(--orange-50)",
|
||||
dark: "var(--orange-400)",
|
||||
};
|
||||
if (doctype == "LMS Live Class")
|
||||
return {
|
||||
light: "var(--red-50)",
|
||||
dark: "var(--red-400)",
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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,20 @@ 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,
|
||||
},
|
||||
)
|
||||
print(
|
||||
frappe.db.count(
|
||||
"LMS Batch Timetable",
|
||||
{
|
||||
"parent": batch_name,
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def get_all_quizzes(batch_name):
|
||||
@@ -210,38 +223,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(
|
||||
|
||||
@@ -31,7 +31,6 @@ def get_context(context):
|
||||
batch.seats_left = (
|
||||
batch.seat_count - batch.student_count if batch.seat_count else None
|
||||
)
|
||||
print(batch.name, batch.published)
|
||||
if not batch.published:
|
||||
private_batches.append(batch)
|
||||
elif getdate(batch.start_date) < getdate():
|
||||
|
||||
@@ -108,6 +108,8 @@ def get_assignment_details(assessment, member):
|
||||
f"/assignment-submission/{assessment.assessment_name}/{submission_name}"
|
||||
)
|
||||
|
||||
return assessment
|
||||
|
||||
|
||||
def get_quiz_details(assessment, member):
|
||||
assessment.title = frappe.db.get_value("LMS Quiz", assessment.assessment_name, "title")
|
||||
@@ -131,6 +133,8 @@ def get_quiz_details(assessment, member):
|
||||
)
|
||||
assessment.url = f"/quiz-submission/{assessment.assessment_name}/{submission_name}"
|
||||
|
||||
return assessment
|
||||
|
||||
|
||||
def is_student(batch, member=None):
|
||||
if not member:
|
||||
|
||||
Reference in New Issue
Block a user