fix: renamed class to batch
This commit is contained in:
@@ -1,607 +0,0 @@
|
||||
{% extends "lms/templates/lms_base.html" %}
|
||||
{% block title %}
|
||||
{{ _(class_info.title) }}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block page_content %}
|
||||
<div class="common-page-style lms-page-style">
|
||||
<div class="container">
|
||||
{{ BreadCrumb(class_info) }}
|
||||
<div class="">
|
||||
{{ ClassDetails(class_info) }}
|
||||
{{ ClassSections(class_info, class_courses, class_students, flow) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
<!-- BreadCrumb -->
|
||||
{% macro BreadCrumb(class_info) %}
|
||||
<div class="breadcrumb">
|
||||
<a class="dark-links" href="/classes">{{ _("All Classes") }}</a>
|
||||
<img class="ml-1 mr-1" src="/assets/lms/icons/chevron-right.svg">
|
||||
<span class="breadcrumb-destination">{{ class_info.title }}</span>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
<!-- Class Details -->
|
||||
{% macro ClassDetails(class_info) %}
|
||||
<div class="class-details" data-class="{{ class_info.name }}">
|
||||
|
||||
<div class="page-title">
|
||||
{{ class_info.title }}
|
||||
</div>
|
||||
|
||||
{% if class_info.description %}
|
||||
<div class="mb-4">
|
||||
{{ class_info.description }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="vertically-center">
|
||||
<div class="">
|
||||
<svg class="icon icon-sm">
|
||||
<use href="#icon-calendar"></use>
|
||||
</svg>
|
||||
<span>
|
||||
{{ frappe.utils.format_date(class_info.start_date, "long") }} -
|
||||
</span>
|
||||
<span>
|
||||
{{ frappe.utils.format_date(class_info.end_date, "long") }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<span class="seperator"></span>
|
||||
|
||||
<div class="">
|
||||
<svg class="icon icon-md">
|
||||
<use href="#icon-education"></use>
|
||||
</svg>
|
||||
{{ class_courses | length }} {{ _("Courses") }}
|
||||
</div>
|
||||
|
||||
<span class="seperator"></span>
|
||||
|
||||
<div class="">
|
||||
<svg class="icon icon-md">
|
||||
<use href="#icon-users"></use>
|
||||
</svg>
|
||||
{{ class_students | length }} {{ _("Students") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% if class_info.custom_component %}
|
||||
{{ class_info.custom_component }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
<!-- Class Sections -->
|
||||
{% macro ClassSections(class_info, class_courses, class_students, flow) %}
|
||||
<div class="mt-4">
|
||||
|
||||
<ul class="nav lms-nav" id="classes-tab">
|
||||
|
||||
{% if is_student %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if is_student %} active {% endif %}" data-toggle="tab" href="#dashboard">
|
||||
{{ _("Dashboard") }}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if not is_student %} active {% endif %}" data-toggle="tab" href="#courses">
|
||||
{{ _("Courses") }}
|
||||
<span class="course-list-count">
|
||||
{{ class_courses | length }}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
{% if flow | length %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="tab" href="#schedule">
|
||||
{{ _("Schedule") }}
|
||||
<span class="course-list-count">
|
||||
{{ flow | length }}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="tab" href="#students">
|
||||
{{ _("Students") }}
|
||||
<span class="course-list-count">
|
||||
{{ class_students | length }}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
{% if is_moderator %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="tab" href="#assessments">
|
||||
{{ _("Assessments") }}
|
||||
<span class="course-list-count">
|
||||
{{ assessments | length }}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% if class_students | length and (is_moderator or is_student) %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="tab" href="#discussions">
|
||||
{{ _("Discussions") }}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="tab" href="#live-class">
|
||||
{{ _("Live Class") }}
|
||||
<span class="course-list-count">
|
||||
{{ live_classes | length }}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
</ul>
|
||||
|
||||
<div class="border-bottom mb-4"></div>
|
||||
|
||||
<div class="tab-content">
|
||||
|
||||
{% if is_student %}
|
||||
<div class="tab-pane {% if is_student %} active {% endif %}" id="dashboard" role="tabpanel" aria-labelledby="dashboard">
|
||||
{{ Dashboard(class_info, class_courses, current_student) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="tab-pane {% if not is_student %} active {% endif %}" id="courses" role="tabpanel" aria-labelledby="courses">
|
||||
{{ CoursesSection(class_info, class_courses) }}
|
||||
</div>
|
||||
|
||||
{% if flow | length %}
|
||||
<div class="tab-pane" id="schedule" role="tabpanel" aria-labelledby="schedule">
|
||||
{{ ScheduleSection(flow) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="tab-pane" id="students" role="tabpanel" aria-labelledby="students">
|
||||
{{ StudentsSection(class_info, class_students) }}
|
||||
</div>
|
||||
|
||||
{% if is_moderator %}
|
||||
<div class="tab-pane" id="assessments" role="tabpanel" aria-labelledby="assessments">
|
||||
{{ AssessmentsSection(class_info) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if class_students | length and (is_moderator or is_student or is_evaluator) %}
|
||||
<div class="tab-pane" id="discussions" role="tabpanel" aria-labelledby="discussions">
|
||||
{{ Discussions(class_info) }}
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="live-class" role="tabpanel" aria-labelledby="live-class">
|
||||
{{ LiveClassSection(class_info, live_classes) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro Dashboard(class_info, class_courses, current_student) %}
|
||||
|
||||
{% set upcoming_evals = current_student.upcoming_evals %}
|
||||
{% set assessments = current_student.assessments %}
|
||||
{% set student = current_student %}
|
||||
|
||||
{% if student.name == frappe.session.user %}
|
||||
<button class="btn btn-default btn-sm btn-schedule-eval ml-2 pull-right">
|
||||
{{ _("Schedule Evaluation") }}
|
||||
</button>
|
||||
{% endif %}
|
||||
|
||||
<div class="mb-8">
|
||||
{% include "lms/templates/upcoming_evals.html" %}
|
||||
</div>
|
||||
<div class="mb-8">
|
||||
{% include "lms/templates/assessments.html" %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro Discussions(class_info) %}
|
||||
<article class="class-discussion">
|
||||
{% set condition = is_moderator or is_student or is_evaluator %}
|
||||
{% set doctype, docname = _("LMS Class"), class_info.name %}
|
||||
{% set single_thread = True %}
|
||||
{% set title = "Discussions" %}
|
||||
{% set cta_title = "Post" %}
|
||||
{% set button_name = _("Start Learning") %}
|
||||
{% set redirect_to = "/classes/" + class_info.name %}
|
||||
{% set empty_state_title = _("Have a doubt?") %}
|
||||
{% set empty_state_subtitle = _("Post it here, our mentors will help you out.") %}
|
||||
{% include "frappe/templates/discussions/discussions_section.html" %}
|
||||
</article>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro CoursesSection(class_info, class_courses) %}
|
||||
<article>
|
||||
<header class="mb-5">
|
||||
<div class="edit-header">
|
||||
<div class="bold-heading">
|
||||
{{ _("Courses") }}
|
||||
</div>
|
||||
{% if is_moderator %}
|
||||
<button class="btn btn-default btn-sm btn-add-course">
|
||||
{{ _("Add Courses") }}
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{% if class_courses | length %}
|
||||
<div class="cards-parent">
|
||||
{% for course in class_courses %}
|
||||
<div class="h-100">
|
||||
{{ widgets.CourseCard(course=course, read_only=False) }}
|
||||
<button class="btn icon-btn btn-default btn-block btn-remove-course" data-course="{{ course.name }}">
|
||||
<svg class="icon icon-sm">
|
||||
<use href="#icon-delete"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="">
|
||||
{{ _("No courses") }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</article>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{% macro StudentsSection(class_info, class_students) %}
|
||||
<article>
|
||||
<header>
|
||||
<div class="edit-header mb-5">
|
||||
<div class="bold-heading">
|
||||
{{ _("Students") }}
|
||||
</div>
|
||||
{% if is_moderator %}
|
||||
<button class="btn btn-default btn-sm btn-add-student">
|
||||
{{ _("Add Students") }}
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{% if class_students | length %}
|
||||
<div class="form-grid">
|
||||
<div class="grid-heading-row">
|
||||
<div class="grid-row">
|
||||
<div class="data-row row">
|
||||
<div class="col grid-static-col">
|
||||
{{ _("Full Name") }}
|
||||
</div>
|
||||
<div class="col grid-static-col col-xs-2 text-right">
|
||||
{{ _("Courses Completed") }}
|
||||
</div>
|
||||
<div class="col grid-static-col col-xs-2 text-right">
|
||||
{{ _("Assessments Completed") }}
|
||||
</div>
|
||||
<div class="col grid-static-col col-xs-2 text-right">
|
||||
{{ _("Assessments Graded") }}
|
||||
</div>
|
||||
<div class="col grid-static-col">
|
||||
{{ _("Last Active") }}
|
||||
</div>
|
||||
{% if is_moderator %}
|
||||
<div class="col grid-static-col col-xs-1">
|
||||
<svg class="icon icon-sm" style="filter: opacity(0.5)">
|
||||
<use class="" href="#icon-setting-gear"></use>
|
||||
</svg>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% for student in class_students %}
|
||||
{% set allow_progress = is_moderator or is_evaluator %}
|
||||
<div class="grid-row">
|
||||
<div class="data-row row">
|
||||
<a class="col grid-static-col button-links {% if allow_progress %} clickable {% endif %}" {% if allow_progress %} href="/classes/{{ class_info.name }}/students/{{ student.username }}" {% endif %}>
|
||||
{{ student.student_name }}
|
||||
</a>
|
||||
<div class="col grid-static-col col-xs-2 text-right">
|
||||
{{ student.courses_completed }}
|
||||
</div>
|
||||
<div class="col grid-static-col col-xs-2 text-right">
|
||||
{{ student.assessments_completed }}
|
||||
</div>
|
||||
<div class="col grid-static-col col-xs-2 text-right">
|
||||
{{ student.assessments_graded }}
|
||||
</div>
|
||||
<div class="col grid-static-col">
|
||||
{{ frappe.utils.pretty_date(student.last_active) }}
|
||||
</div>
|
||||
{% if is_moderator %}
|
||||
<div type="button" class="col grid-static-col col-xs-1 btn-remove-student" data-student="{{ student.student }}">
|
||||
<svg class="icon icon-sm">
|
||||
<use href="#icon-delete"></use>
|
||||
</svg>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-muted mt-3"> {{ _("No Students") }} </p>
|
||||
{% endif %}
|
||||
</article>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{% macro AssessmentsSection(class_info) %}
|
||||
<article>
|
||||
<header class="edit-header mb-5">
|
||||
<div class="bold-heading">
|
||||
{{ _("Assessments") }}
|
||||
</div>
|
||||
{% if is_moderator %}
|
||||
<button class="btn btn-default btn-sm" id="open-assessment-modal">
|
||||
{{ _("Manage Assessments") }}
|
||||
</button>
|
||||
{% endif %}
|
||||
</header>
|
||||
{{ AssessmentList(assessments) }}
|
||||
</article>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro AssessmentList(assessments) %}
|
||||
{% if assessments | length %}
|
||||
<div class="form-grid">
|
||||
<div class="grid-heading-row">
|
||||
<div class="grid-row">
|
||||
<div class="row data-row">
|
||||
<div class="col grid-static-col">
|
||||
{{ _("Title") }}
|
||||
</div>
|
||||
<div class="col grid-static-col">
|
||||
{{ _("Type") }}
|
||||
</div>
|
||||
<div class="col grid-static-col col-xs-1">
|
||||
<svg class="icon icon-sm" style="filter: opacity(0.5)">
|
||||
<use href="#icon-setting-gear"></use>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid-body">
|
||||
<div class="rows">
|
||||
{% for assessment in assessments %}
|
||||
<div class="grid-row">
|
||||
<div class="row data-row">
|
||||
<a class="col grid-static-col clickable" href="{{ assessment.edit_url }}">
|
||||
{{ assessment.title }}
|
||||
</a>
|
||||
<div class="col grid-static-col">
|
||||
{{ assessment.assessment_type.split("LMS ")[1] }}
|
||||
</div>
|
||||
<div type="button" class="col grid-static-col col-xs-1 btn-remove-assessment" data-assessment="{{ assessment.assessment_name }}">
|
||||
<svg class="icon icon-sm">
|
||||
<use href="#icon-delete"></use>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-muted mt-3"> {{ _("No Assessments") }} </p>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{% macro LiveClassSection(class_info, live_classes) %}
|
||||
<article>
|
||||
<header class="edit-header">
|
||||
<div class="bold-heading">
|
||||
{{ _("Live Class") }}
|
||||
</div>
|
||||
{% if is_moderator %}
|
||||
<button class="btn btn-default btn-sm" id="open-class-modal">
|
||||
{{ _("Create a Live Class") }}
|
||||
</button>
|
||||
{% endif %}
|
||||
</header>
|
||||
{{ CreateLiveClass(class_info) }}
|
||||
{{ LiveClassList(class_info, live_classes) }}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{% macro CreateLiveClass(class_info) %}
|
||||
{% if is_moderator %}
|
||||
<div class="modal fade live-class-modal" id="live-class-modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<div class="modal-title">{{ _("Live Class Details") }}</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="modal-body">
|
||||
<form class="live-class-form" id="live-class-form"></form>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary btn-sm mr-2" data-dismiss="modal" aria-label="Close">
|
||||
{{ _("Discard") }}
|
||||
</button>
|
||||
|
||||
<button class="btn btn-primary btn-sm" id="create-live-class">
|
||||
{{ _("Submit") }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{% macro LiveClassList(class_info, live_classes) %}
|
||||
<div class="lms-card-parent mt-5">
|
||||
{% if live_classes | length %}
|
||||
{% for class in live_classes %}
|
||||
<div class="common-card-style column-card">
|
||||
|
||||
<div class="mb-0">
|
||||
<div class="dropdown pull-right">
|
||||
<svg class="icon icon-sm dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<use href="#icon-dot-horizontal"></use>
|
||||
</svg>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton">
|
||||
{% if class.owner == frappe.session.user %}
|
||||
<li>
|
||||
<a class="dropdown-item small" href="{{ class.start_url }}"> {{ _("Start") }} </a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if is_student %}
|
||||
<li>
|
||||
<a class="dropdown-item small" href="{{ class.join_url }}"> {{ _("Join") }} </a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="bold-heading mb-4">
|
||||
{{ class.title }}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<svg class="icon icon-sm">
|
||||
<use href="#icon-calendar"></use>
|
||||
</svg>
|
||||
{{ frappe.utils.format_date(class.date, "full") }}
|
||||
</div>
|
||||
<div>
|
||||
<svg class="icon icon-sm">
|
||||
<use href="#icon-clock"></use>
|
||||
</svg>
|
||||
{{ frappe.utils.format_time(class.time, "hh:mm a") }}
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
{{ class.description }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<p class=""> {{ _("No Live Classes") }} </p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{% macro ScheduleSection(flow) %}
|
||||
<article>
|
||||
<header class="edit-header mb-5">
|
||||
<div class="bold-heading">
|
||||
{{ _("Schedule") }}
|
||||
</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>
|
||||
</article>
|
||||
{% endmacro %}
|
||||
|
||||
{%- block script %}
|
||||
{{ super() }}
|
||||
<script>
|
||||
frappe.boot.single_types = []
|
||||
let courses = {{ course_list | json }};
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -1,693 +0,0 @@
|
||||
frappe.ready(() => {
|
||||
let self = this;
|
||||
frappe.require("controls.bundle.js");
|
||||
|
||||
if ($("#live-class-form").length) {
|
||||
setTimeout(() => {
|
||||
make_live_class_form();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
$(".btn-add-student").click((e) => {
|
||||
show_student_modal(e);
|
||||
});
|
||||
|
||||
$(".btn-remove-student").click((e) => {
|
||||
remove_student(e);
|
||||
});
|
||||
|
||||
$("#open-class-modal").click((e) => {
|
||||
e.preventDefault();
|
||||
$("#live-class-modal").modal("show");
|
||||
});
|
||||
|
||||
$("#create-live-class").click((e) => {
|
||||
create_live_class(e);
|
||||
});
|
||||
|
||||
$(".btn-add-course").click((e) => {
|
||||
show_course_modal(e);
|
||||
});
|
||||
$(".btn-remove-course").click((e) => {
|
||||
remove_course(e);
|
||||
});
|
||||
|
||||
$(".btn-remove-assessment").click((e) => {
|
||||
remove_assessment(e);
|
||||
});
|
||||
|
||||
$("#open-assessment-modal").click((e) => {
|
||||
e.preventDefault();
|
||||
show_assessment_modal();
|
||||
});
|
||||
|
||||
$(".btn-close").click((e) => {
|
||||
window.location.reload();
|
||||
});
|
||||
|
||||
$(".btn-schedule-eval").click((e) => {
|
||||
open_evaluation_form(e);
|
||||
});
|
||||
|
||||
$(document).on("click", ".slot", (e) => {
|
||||
mark_active_slot(e);
|
||||
});
|
||||
});
|
||||
|
||||
const create_live_class = (e) => {
|
||||
let class_name = $(".class-details").data("class");
|
||||
frappe.call({
|
||||
method: "lms.lms.doctype.lms_class.lms_class.create_live_class",
|
||||
args: {
|
||||
class_name: class_name,
|
||||
title: $("input[data-fieldname='meeting_title']").val(),
|
||||
duration: $("input[data-fieldname='meeting_duration']").val(),
|
||||
date: $("input[data-fieldname='meeting_date']").val(),
|
||||
time: $("input[data-fieldname='meeting_time']").val(),
|
||||
timezone: $('select[data-fieldname="meeting_timezone"]').val(),
|
||||
auto_recording: $(
|
||||
'select[data-fieldname="meeting_recording"]'
|
||||
).val(),
|
||||
description: $(
|
||||
"textarea[data-fieldname='meeting_description']"
|
||||
).val(),
|
||||
},
|
||||
callback: (data) => {
|
||||
$("#live-class-modal").modal("hide");
|
||||
frappe.show_alert(
|
||||
{
|
||||
message: __("Live Class added successfully"),
|
||||
indicator: "green",
|
||||
},
|
||||
3
|
||||
);
|
||||
setTimeout(function () {
|
||||
window.location.reload();
|
||||
}, 1000);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const make_live_class_form = (e) => {
|
||||
this.field_group = new frappe.ui.FieldGroup({
|
||||
fields: [
|
||||
{
|
||||
fieldname: "meeting_title",
|
||||
fieldtype: "Data",
|
||||
options: "",
|
||||
label: "Title",
|
||||
reqd: 1,
|
||||
},
|
||||
{
|
||||
fieldname: "meeting_time",
|
||||
fieldtype: "Time",
|
||||
options: "",
|
||||
label: "Time",
|
||||
reqd: 1,
|
||||
},
|
||||
{
|
||||
fieldname: "meeting_timezone",
|
||||
label: __("Time Zone"),
|
||||
fieldtype: "Select",
|
||||
options: get_timezones().join("\n"),
|
||||
reqd: 1,
|
||||
},
|
||||
{
|
||||
fieldname: "meeting_col",
|
||||
fieldtype: "Column Break",
|
||||
options: "",
|
||||
},
|
||||
{
|
||||
fieldname: "meeting_date",
|
||||
fieldtype: "Date",
|
||||
options: "",
|
||||
label: "Date",
|
||||
reqd: 1,
|
||||
},
|
||||
{
|
||||
fieldname: "meeting_duration",
|
||||
fieldtype: "Int",
|
||||
options: "",
|
||||
label: "Duration (in Minutes)",
|
||||
reqd: 1,
|
||||
},
|
||||
{
|
||||
fieldname: "meeting_recording",
|
||||
fieldtype: "Select",
|
||||
options: "No Recording\nLocal\nCloud",
|
||||
label: "Auto Recording",
|
||||
default: "No Recording",
|
||||
},
|
||||
{
|
||||
fieldname: "meeting_sec",
|
||||
fieldtype: "Section Break",
|
||||
options: "",
|
||||
},
|
||||
{
|
||||
fieldname: "meeting_description",
|
||||
fieldtype: "Small Text",
|
||||
options: "",
|
||||
max_height: 100,
|
||||
min_lines: 5,
|
||||
label: "Description",
|
||||
},
|
||||
],
|
||||
body: $("#live-class-form").get(0),
|
||||
});
|
||||
|
||||
this.field_group.make();
|
||||
$("#live-class-form .form-section:last").removeClass("empty-section");
|
||||
$("#live-class-form .frappe-control").removeClass("hide-control");
|
||||
};
|
||||
|
||||
const get_timezones = () => {
|
||||
return [
|
||||
"Pacific/Midway",
|
||||
"Pacific/Pago_Pago",
|
||||
"Pacific/Honolulu",
|
||||
"America/Anchorage",
|
||||
"America/Vancouver",
|
||||
"America/Los_Angeles",
|
||||
"America/Tijuana",
|
||||
"America/Edmonton",
|
||||
"America/Denver",
|
||||
"America/Phoenix",
|
||||
"America/Mazatlan",
|
||||
"America/Winnipeg",
|
||||
"America/Regina",
|
||||
"America/Chicago",
|
||||
"America/Mexico_City",
|
||||
"America/Guatemala",
|
||||
"America/El_Salvador",
|
||||
"America/Managua",
|
||||
"America/Costa_Rica",
|
||||
"America/Montreal",
|
||||
"America/New_York",
|
||||
"America/Indianapolis",
|
||||
"America/Panama",
|
||||
"America/Bogota",
|
||||
"America/Lima",
|
||||
"America/Halifax",
|
||||
"America/Puerto_Rico",
|
||||
"America/Caracas",
|
||||
"America/Santiago",
|
||||
"America/St_Johns",
|
||||
"America/Montevideo",
|
||||
"America/Araguaina",
|
||||
"America/Argentina/Buenos_Aires",
|
||||
"America/Godthab",
|
||||
"America/Sao_Paulo",
|
||||
"Atlantic/Azores",
|
||||
"Canada/Atlantic",
|
||||
"Atlantic/Cape_Verde",
|
||||
"UTC",
|
||||
"Etc/Greenwich",
|
||||
"Europe/Belgrade",
|
||||
"CET",
|
||||
"Atlantic/Reykjavik",
|
||||
"Europe/Dublin",
|
||||
"Europe/London",
|
||||
"Europe/Lisbon",
|
||||
"Africa/Casablanca",
|
||||
"Africa/Nouakchott",
|
||||
"Europe/Oslo",
|
||||
"Europe/Copenhagen",
|
||||
"Europe/Brussels",
|
||||
"Europe/Berlin",
|
||||
"Europe/Helsinki",
|
||||
"Europe/Amsterdam",
|
||||
"Europe/Rome",
|
||||
"Europe/Stockholm",
|
||||
"Europe/Vienna",
|
||||
"Europe/Luxembourg",
|
||||
"Europe/Paris",
|
||||
"Europe/Zurich",
|
||||
"Europe/Madrid",
|
||||
"Africa/Bangui",
|
||||
"Africa/Algiers",
|
||||
"Africa/Tunis",
|
||||
"Africa/Harare",
|
||||
"Africa/Nairobi",
|
||||
"Europe/Warsaw",
|
||||
"Europe/Prague",
|
||||
"Europe/Budapest",
|
||||
"Europe/Sofia",
|
||||
"Europe/Istanbul",
|
||||
"Europe/Athens",
|
||||
"Europe/Bucharest",
|
||||
"Asia/Nicosia",
|
||||
"Asia/Beirut",
|
||||
"Asia/Damascus",
|
||||
"Asia/Jerusalem",
|
||||
"Asia/Amman",
|
||||
"Africa/Tripoli",
|
||||
"Africa/Cairo",
|
||||
"Africa/Johannesburg",
|
||||
"Europe/Moscow",
|
||||
"Asia/Baghdad",
|
||||
"Asia/Kuwait",
|
||||
"Asia/Riyadh",
|
||||
"Asia/Bahrain",
|
||||
"Asia/Qatar",
|
||||
"Asia/Aden",
|
||||
"Asia/Tehran",
|
||||
"Africa/Khartoum",
|
||||
"Africa/Djibouti",
|
||||
"Africa/Mogadishu",
|
||||
"Asia/Dubai",
|
||||
"Asia/Muscat",
|
||||
"Asia/Baku",
|
||||
"Asia/Kabul",
|
||||
"Asia/Yekaterinburg",
|
||||
"Asia/Tashkent",
|
||||
"Asia/Calcutta",
|
||||
"Asia/Kathmandu",
|
||||
"Asia/Novosibirsk",
|
||||
"Asia/Almaty",
|
||||
"Asia/Dacca",
|
||||
"Asia/Krasnoyarsk",
|
||||
"Asia/Dhaka",
|
||||
"Asia/Bangkok",
|
||||
"Asia/Saigon",
|
||||
"Asia/Jakarta",
|
||||
"Asia/Irkutsk",
|
||||
"Asia/Shanghai",
|
||||
"Asia/Hong_Kong",
|
||||
"Asia/Taipei",
|
||||
"Asia/Kuala_Lumpur",
|
||||
"Asia/Singapore",
|
||||
"Australia/Perth",
|
||||
"Asia/Yakutsk",
|
||||
"Asia/Seoul",
|
||||
"Asia/Tokyo",
|
||||
"Australia/Darwin",
|
||||
"Australia/Adelaide",
|
||||
"Asia/Vladivostok",
|
||||
"Pacific/Port_Moresby",
|
||||
"Australia/Brisbane",
|
||||
"Australia/Sydney",
|
||||
"Australia/Hobart",
|
||||
"Asia/Magadan",
|
||||
"SST",
|
||||
"Pacific/Noumea",
|
||||
"Asia/Kamchatka",
|
||||
"Pacific/Fiji",
|
||||
"Pacific/Auckland",
|
||||
"Asia/Kolkata",
|
||||
"Europe/Kiev",
|
||||
"America/Tegucigalpa",
|
||||
"Pacific/Apia",
|
||||
];
|
||||
};
|
||||
|
||||
const show_course_modal = () => {
|
||||
let course_modal = new frappe.ui.Dialog({
|
||||
title: "Add Course",
|
||||
fields: [
|
||||
{
|
||||
fieldtype: "Link",
|
||||
options: "LMS Course",
|
||||
label: __("Course"),
|
||||
fieldname: "course",
|
||||
reqd: 1,
|
||||
only_select: 1,
|
||||
},
|
||||
{
|
||||
fieldtype: "Link",
|
||||
options: "Course Evaluator",
|
||||
label: __("Course Evaluator"),
|
||||
fieldname: "evaluator",
|
||||
only_select: 1,
|
||||
},
|
||||
],
|
||||
primary_action_label: __("Add"),
|
||||
primary_action(values) {
|
||||
add_course(values);
|
||||
course_modal.hide();
|
||||
},
|
||||
});
|
||||
course_modal.show();
|
||||
setTimeout(() => {
|
||||
$(".modal-body").css("min-height", "200px");
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const add_course = (values) => {
|
||||
frappe.call({
|
||||
method: "frappe.client.insert",
|
||||
args: {
|
||||
doc: {
|
||||
doctype: "Class Course",
|
||||
course: values.course,
|
||||
parenttype: "LMS Class",
|
||||
parentfield: "courses",
|
||||
parent: $(".class-details").data("class"),
|
||||
},
|
||||
},
|
||||
callback(r) {
|
||||
frappe.show_alert(
|
||||
{
|
||||
message: __("Course Added"),
|
||||
indicator: "green",
|
||||
},
|
||||
2000
|
||||
);
|
||||
window.location.reload();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const remove_course = (e) => {
|
||||
frappe.confirm("Are you sure you want to remove this course?", () => {
|
||||
frappe.call({
|
||||
method: "lms.lms.doctype.lms_class.lms_class.remove_course",
|
||||
args: {
|
||||
course: $(e.currentTarget).data("course"),
|
||||
parent: $(".class-details").data("class"),
|
||||
},
|
||||
callback(r) {
|
||||
frappe.show_alert(
|
||||
{
|
||||
message: __("Course Removed"),
|
||||
indicator: "green",
|
||||
},
|
||||
2000
|
||||
);
|
||||
window.location.reload();
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const show_student_modal = () => {
|
||||
let student_modal = new frappe.ui.Dialog({
|
||||
title: "Add Student",
|
||||
fields: [
|
||||
{
|
||||
fieldtype: "Link",
|
||||
options: "User",
|
||||
label: __("Student"),
|
||||
fieldname: "student",
|
||||
reqd: 1,
|
||||
only_select: 1,
|
||||
filters: {
|
||||
ignore_user_type: 1,
|
||||
},
|
||||
filter_description: " ",
|
||||
},
|
||||
],
|
||||
primary_action_label: __("Add"),
|
||||
primary_action(values) {
|
||||
add_student(values);
|
||||
student_modal.hide();
|
||||
},
|
||||
});
|
||||
student_modal.show();
|
||||
setTimeout(() => {
|
||||
$(".modal-body").css("min-height", "200px");
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const add_student = (values) => {
|
||||
frappe.call({
|
||||
method: "frappe.client.insert",
|
||||
args: {
|
||||
doc: {
|
||||
doctype: "Class Student",
|
||||
student: values.student,
|
||||
parenttype: "LMS Class",
|
||||
parentfield: "students",
|
||||
parent: $(".class-details").data("class"),
|
||||
},
|
||||
},
|
||||
callback(r) {
|
||||
frappe.show_alert(
|
||||
{
|
||||
message: __("Student Added"),
|
||||
indicator: "green",
|
||||
},
|
||||
2000
|
||||
);
|
||||
window.location.reload();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const remove_student = (e) => {
|
||||
frappe.confirm(
|
||||
"Are you sure you want to remove this student from the class?",
|
||||
() => {
|
||||
frappe.call({
|
||||
method: "lms.lms.doctype.lms_class.lms_class.remove_student",
|
||||
args: {
|
||||
student: $(e.currentTarget).data("student"),
|
||||
class_name: $(".class-details").data("class"),
|
||||
},
|
||||
callback: (data) => {
|
||||
frappe.show_alert(
|
||||
{
|
||||
message: __("Student removed successfully"),
|
||||
indicator: "green",
|
||||
},
|
||||
2000
|
||||
);
|
||||
window.location.reload();
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const show_assessment_modal = (e) => {
|
||||
let assessment_modal = new frappe.ui.Dialog({
|
||||
title: "Manage Assessments",
|
||||
fields: [
|
||||
{
|
||||
fieldtype: "Link",
|
||||
options: "DocType",
|
||||
label: __("Assessment Type"),
|
||||
fieldname: "assessment_type",
|
||||
reqd: 1,
|
||||
only_select: 1,
|
||||
filters: {
|
||||
name: ["in", ["LMS Assignment", "LMS Quiz"]],
|
||||
},
|
||||
filter_description: " ",
|
||||
},
|
||||
{
|
||||
fieldtype: "Dynamic Link",
|
||||
options: "assessment_type",
|
||||
label: __("Assessment"),
|
||||
fieldname: "assessment_name",
|
||||
reqd: 1,
|
||||
only_select: 1,
|
||||
},
|
||||
{
|
||||
fieldtype: "Section Break",
|
||||
label: __("OR"),
|
||||
},
|
||||
{
|
||||
fieldtype: "Button",
|
||||
label: __("Create Assignment"),
|
||||
fieldname: "create_assignment",
|
||||
click: () => {
|
||||
window.location.href = "/assignments";
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldtype: "Column Break",
|
||||
},
|
||||
{
|
||||
fieldtype: "Button",
|
||||
label: __("Create Quiz"),
|
||||
fieldname: "create_quiz",
|
||||
click: () => {
|
||||
window.location.href = "/quizzes";
|
||||
},
|
||||
},
|
||||
],
|
||||
primary_action_label: __("Add"),
|
||||
primary_action(values) {
|
||||
add_addessment(values);
|
||||
assessment_modal.hide();
|
||||
},
|
||||
});
|
||||
assessment_modal.show();
|
||||
setTimeout(() => {
|
||||
$(".modal-body").css("min-height", "300px");
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const add_addessment = (values) => {
|
||||
frappe.call({
|
||||
method: "frappe.client.insert",
|
||||
args: {
|
||||
doc: {
|
||||
doctype: "LMS Assessment",
|
||||
assessment_type: values.assessment_type,
|
||||
assessment_name: values.assessment_name,
|
||||
parenttype: "LMS Class",
|
||||
parentfield: "assessment",
|
||||
parent: $(".class-details").data("class"),
|
||||
},
|
||||
},
|
||||
callback(r) {
|
||||
frappe.show_alert(
|
||||
{
|
||||
message: __("Assessment Added"),
|
||||
indicator: "green",
|
||||
},
|
||||
2000
|
||||
);
|
||||
window.location.reload();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const remove_assessment = (e) => {
|
||||
frappe.confirm("Are you sure you want to remove this assessment?", () => {
|
||||
frappe.call({
|
||||
method: "lms.lms.doctype.lms_class.lms_class.remove_assessment",
|
||||
args: {
|
||||
assessment: $(e.currentTarget).data("assessment"),
|
||||
parent: $(".class-details").data("class"),
|
||||
},
|
||||
callback(r) {
|
||||
frappe.show_alert(
|
||||
{
|
||||
message: __("Assessment Removed"),
|
||||
indicator: "green",
|
||||
},
|
||||
2000
|
||||
);
|
||||
window.location.reload();
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const open_evaluation_form = (e) => {
|
||||
this.eval_form = new frappe.ui.Dialog({
|
||||
title: __("Schedule Evaluation"),
|
||||
fields: [
|
||||
{
|
||||
fieldtype: "Link",
|
||||
fieldname: "course",
|
||||
label: __("Course"),
|
||||
options: "LMS Course",
|
||||
reqd: 1,
|
||||
filters: {
|
||||
name: ["in", courses],
|
||||
},
|
||||
filter_description: " ",
|
||||
},
|
||||
{
|
||||
fieldtype: "Date",
|
||||
fieldname: "date",
|
||||
label: __("Date"),
|
||||
reqd: 1,
|
||||
min_date: new Date(
|
||||
frappe.datetime.add_days(frappe.datetime.get_today(), 1)
|
||||
),
|
||||
change: () => {
|
||||
get_slots();
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldtype: "HTML",
|
||||
fieldname: "slots",
|
||||
label: __("Slots"),
|
||||
},
|
||||
],
|
||||
primary_action: (values) => {
|
||||
submit_evaluation_form(values);
|
||||
},
|
||||
});
|
||||
this.eval_form.show();
|
||||
setTimeout(() => {
|
||||
$(".modal-body").css("min-height", "300px");
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const get_slots = () => {
|
||||
frappe.call({
|
||||
method: "lms.lms.doctype.course_evaluator.course_evaluator.get_schedule",
|
||||
args: {
|
||||
course: this.eval_form.get_value("course"),
|
||||
date: this.eval_form.get_value("date"),
|
||||
class_name: $(".class-details").data("class"),
|
||||
},
|
||||
callback: (r) => {
|
||||
if (r.message) {
|
||||
display_slots(r.message);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const display_slots = (slots) => {
|
||||
let slot_html = "";
|
||||
let slots_available = false;
|
||||
if (slots.length) {
|
||||
slot_html = `<div>
|
||||
<div class="mb-2"> ${__("Select a Slot")} </div>
|
||||
<div class="slots-parent">`;
|
||||
let day = moment(this.eval_form.get_value("date")).format("dddd");
|
||||
|
||||
slots.forEach((slot) => {
|
||||
if (slot.day == day) {
|
||||
slots_available = true;
|
||||
slot_html += `<div class="btn btn-sm btn-default slot" data-day="${
|
||||
slot.day
|
||||
}"
|
||||
data-start="${slot.start_time}" data-end="${slot.end_time}">
|
||||
${moment(slot.start_time, "hh:mm").format("hh:mm a")} -
|
||||
${moment(slot.end_time, "hh:mm").format("hh:mm a")}
|
||||
</div>`;
|
||||
}
|
||||
});
|
||||
slot_html += "</div> </div>";
|
||||
}
|
||||
|
||||
if (!slots_available) {
|
||||
slot_html = `<div class="alert alert-danger" role="alert">
|
||||
No slots available for this date.
|
||||
</div>`;
|
||||
}
|
||||
|
||||
$("[data-fieldname='slots']").html(slot_html);
|
||||
};
|
||||
|
||||
const mark_active_slot = (e) => {
|
||||
$(".slot").removeClass("btn-outline-primary");
|
||||
$(e.currentTarget).addClass("btn-outline-primary");
|
||||
this.current_slot = $(e.currentTarget);
|
||||
};
|
||||
|
||||
const submit_evaluation_form = (values) => {
|
||||
if (!this.current_slot) {
|
||||
frappe.throw(__("Please select a slot"));
|
||||
}
|
||||
|
||||
frappe.call({
|
||||
method: "lms.lms.doctype.lms_certificate_request.lms_certificate_request.create_certificate_request",
|
||||
args: {
|
||||
course: values.course,
|
||||
date: values.date,
|
||||
start_time: this.current_slot.data("start"),
|
||||
end_time: this.current_slot.data("end"),
|
||||
day: this.current_slot.data("day"),
|
||||
class_name: $(".class-details").data("class"),
|
||||
},
|
||||
callback: (r) => {
|
||||
this.eval_form.hide();
|
||||
frappe.show_alert({
|
||||
message: __("Evaluation scheduled successfully"),
|
||||
indicator: "green",
|
||||
});
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 1000);
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -1,278 +0,0 @@
|
||||
from frappe import _
|
||||
import frappe
|
||||
from frappe.utils import getdate, cint
|
||||
from lms.www.utils import get_assessments, is_student
|
||||
from lms.lms.utils import (
|
||||
has_course_moderator_role,
|
||||
has_course_evaluator_role,
|
||||
get_upcoming_evals,
|
||||
has_submitted_assessment,
|
||||
has_graded_assessment,
|
||||
get_lesson_index,
|
||||
get_lesson_url,
|
||||
get_lesson_icon,
|
||||
get_membership,
|
||||
)
|
||||
|
||||
|
||||
def get_context(context):
|
||||
context.no_cache = 1
|
||||
class_name = frappe.form_dict["classname"]
|
||||
context.is_moderator = has_course_moderator_role()
|
||||
context.is_evaluator = has_course_evaluator_role()
|
||||
|
||||
context.class_info = frappe.db.get_value(
|
||||
"LMS Class",
|
||||
class_name,
|
||||
[
|
||||
"name",
|
||||
"title",
|
||||
"start_date",
|
||||
"end_date",
|
||||
"description",
|
||||
"medium",
|
||||
"custom_component",
|
||||
"seat_count",
|
||||
"start_time",
|
||||
"end_time",
|
||||
"category",
|
||||
"paid_class",
|
||||
"amount",
|
||||
"currency",
|
||||
"class_details",
|
||||
],
|
||||
as_dict=True,
|
||||
)
|
||||
|
||||
context.reference_doctype = "LMS Class"
|
||||
context.reference_name = class_name
|
||||
|
||||
class_courses = frappe.get_all(
|
||||
"Class Course",
|
||||
{"parent": class_name},
|
||||
["name", "course", "title"],
|
||||
order_by="creation desc",
|
||||
)
|
||||
|
||||
class_students = frappe.get_all(
|
||||
"Class Student",
|
||||
{"parent": class_name},
|
||||
["name", "student", "student_name", "username"],
|
||||
order_by="creation desc",
|
||||
)
|
||||
|
||||
context.class_courses = get_class_course_details(class_courses)
|
||||
context.course_list = [course.course for course in context.class_courses]
|
||||
context.all_courses = frappe.get_all(
|
||||
"LMS Course", fields=["name", "title"], limit_page_length=0
|
||||
)
|
||||
context.course_name_list = [course.course for course in context.class_courses]
|
||||
context.assessments = get_assessments(class_name)
|
||||
context.class_students = get_class_student_details(
|
||||
class_students, class_courses, context.assessments
|
||||
)
|
||||
context.is_student = is_student(class_name)
|
||||
|
||||
if not context.is_student and not context.is_moderator and not context.is_evaluator:
|
||||
raise frappe.PermissionError(_("You don't have permission to access this page."))
|
||||
|
||||
context.live_classes = frappe.get_all(
|
||||
"LMS Live Class",
|
||||
{"class_name": class_name, "date": [">=", getdate()]},
|
||||
["title", "description", "time", "date", "start_url", "join_url", "owner"],
|
||||
order_by="date",
|
||||
)
|
||||
|
||||
context.current_student = (
|
||||
get_current_student_details(class_courses, class_name) if context.is_student else None
|
||||
)
|
||||
context.all_assignments = get_all_assignments(class_name)
|
||||
context.all_quizzes = get_all_quizzes(class_name)
|
||||
context.flow = get_scheduled_flow(class_name)
|
||||
|
||||
|
||||
def get_all_quizzes(class_name):
|
||||
filters = {} if has_course_moderator_role() else {"owner": frappe.session.user}
|
||||
all_quizzes = frappe.get_all("LMS Quiz", filters, ["name", "title"])
|
||||
for quiz in all_quizzes:
|
||||
quiz.checked = frappe.db.exists(
|
||||
{
|
||||
"doctype": "LMS Assessment",
|
||||
"assessment_type": "LMS Quiz",
|
||||
"assessment_name": quiz.name,
|
||||
"parent": class_name,
|
||||
}
|
||||
)
|
||||
return all_quizzes
|
||||
|
||||
|
||||
def get_all_assignments(class_name):
|
||||
filters = {} if has_course_moderator_role() else {"owner": frappe.session.user}
|
||||
all_assignments = frappe.get_all("LMS Assignment", filters, ["name", "title"])
|
||||
for assignment in all_assignments:
|
||||
assignment.checked = frappe.db.exists(
|
||||
{
|
||||
"doctype": "LMS Assessment",
|
||||
"assessment_type": "LMS Assignment",
|
||||
"assessment_name": assignment.name,
|
||||
"parent": class_name,
|
||||
}
|
||||
)
|
||||
return all_assignments
|
||||
|
||||
|
||||
def get_class_course_details(class_courses):
|
||||
for course in class_courses:
|
||||
details = frappe.db.get_value(
|
||||
"LMS Course",
|
||||
course.course,
|
||||
[
|
||||
"name",
|
||||
"title",
|
||||
"image",
|
||||
"upcoming",
|
||||
"short_introduction",
|
||||
"image",
|
||||
"paid_course",
|
||||
"course_price",
|
||||
"enable_certification",
|
||||
"currency",
|
||||
],
|
||||
as_dict=True,
|
||||
)
|
||||
course.update(details)
|
||||
return class_courses
|
||||
|
||||
|
||||
def get_class_student_details(class_students, class_courses, assessments):
|
||||
for student in class_students:
|
||||
student.update(
|
||||
frappe.db.get_value(
|
||||
"User", student.student, ["name", "full_name", "username", "headline"], as_dict=1
|
||||
)
|
||||
)
|
||||
student.update(frappe.db.get_value("User", student.student, "last_active", as_dict=1))
|
||||
get_progress_info(student, class_courses)
|
||||
get_assessment_info(student, assessments)
|
||||
|
||||
return sort_students(class_students)
|
||||
|
||||
|
||||
def get_progress_info(student, class_courses):
|
||||
courses_completed = 0
|
||||
student["courses"] = frappe._dict()
|
||||
for course in class_courses:
|
||||
membership = get_membership(course.course, student.student)
|
||||
if membership and membership.progress == 100:
|
||||
courses_completed += 1
|
||||
|
||||
student["courses_completed"] = courses_completed
|
||||
return student
|
||||
|
||||
|
||||
def get_assessment_info(student, assessments):
|
||||
assessments_completed = 0
|
||||
assessments_graded = 0
|
||||
for assessment in assessments:
|
||||
submission = has_submitted_assessment(
|
||||
assessment.assessment_name, assessment.assessment_type, student.student
|
||||
)
|
||||
if submission:
|
||||
assessments_completed += 1
|
||||
|
||||
if (
|
||||
assessment.assessment_type == "LMS Assignment" and has_graded_assessment(submission)
|
||||
):
|
||||
assessments_graded += 1
|
||||
elif assessment.assessment_type == "LMS Quiz":
|
||||
assessments_graded += 1
|
||||
|
||||
student["assessments_completed"] = assessments_completed
|
||||
student["assessments_graded"] = assessments_graded
|
||||
|
||||
return student
|
||||
|
||||
|
||||
def sort_students(class_students):
|
||||
session_user = []
|
||||
remaining_students = []
|
||||
|
||||
for student in class_students:
|
||||
if student.student == frappe.session.user:
|
||||
session_user.append(student)
|
||||
else:
|
||||
remaining_students.append(student)
|
||||
|
||||
if len(session_user):
|
||||
return session_user + remaining_students
|
||||
else:
|
||||
return class_students
|
||||
|
||||
|
||||
def get_scheduled_flow(class_name):
|
||||
chapters = []
|
||||
|
||||
lessons = frappe.get_all(
|
||||
"Scheduled Flow",
|
||||
{"parent": class_name},
|
||||
["name", "lesson", "date", "start_time", "end_time"],
|
||||
order_by="idx",
|
||||
)
|
||||
|
||||
for lesson in lessons:
|
||||
lesson = get_lesson_details(lesson, class_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, class_name):
|
||||
lesson.update(
|
||||
frappe.db.get_value(
|
||||
"Course Lesson",
|
||||
lesson.lesson,
|
||||
["name", "title", "body", "course", "chapter"],
|
||||
as_dict=True,
|
||||
)
|
||||
)
|
||||
lesson.index = get_lesson_index(lesson.lesson)
|
||||
lesson.url = get_lesson_url(lesson.course, lesson.index) + "?class=" + class_name
|
||||
lesson.icon = get_lesson_icon(lesson.body)
|
||||
return lesson
|
||||
|
||||
|
||||
def get_current_student_details(class_courses, class_name):
|
||||
student_details = frappe._dict()
|
||||
student_details.courses = frappe._dict()
|
||||
course_list = [course.course for course in class_courses]
|
||||
|
||||
get_course_progress(class_courses, student_details)
|
||||
student_details.name = frappe.session.user
|
||||
student_details.assessments = get_assessments(class_name, frappe.session.user)
|
||||
student_details.upcoming_evals = get_upcoming_evals(frappe.session.user, course_list)
|
||||
|
||||
return student_details
|
||||
|
||||
|
||||
def get_course_progress(class_courses, student_details):
|
||||
for course in class_courses:
|
||||
membership = get_membership(course.course, frappe.session.user)
|
||||
if membership:
|
||||
student_details.courses[course.course] = membership.progress
|
||||
else:
|
||||
student_details.courses[course.course] = 0
|
||||
@@ -1,209 +0,0 @@
|
||||
{% extends "lms/templates/lms_base.html" %}
|
||||
{% block title %}
|
||||
{{ _(class_info.title) }}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block page_content %}
|
||||
<div class="common-page-style lms-page-style">
|
||||
{{ ClassHeader(class_info) }}
|
||||
<div class="container">
|
||||
{{ CourseHeaderOverlay(class_info, courses, students) }}
|
||||
<div class="pt-10">
|
||||
{{ ClassDetails(class_info) }}
|
||||
{{ CourseList(courses) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% macro ClassHeader(class_info) %}
|
||||
<div class="course-head-container">
|
||||
<div class="container">
|
||||
<div class="course-card-wide">
|
||||
{{ BreadCrumb(class_info) }}
|
||||
{{ ClassHeaderDetails(class_info, courses, students) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro BreadCrumb(class_info) %}
|
||||
<article class="mb-8">
|
||||
<a class="dark-links" href="/classes">
|
||||
{{ _("All Classes") }}
|
||||
</a>
|
||||
<img class="" src="/assets/lms/icons/chevron-right.svg">
|
||||
<span class="breadcrumb-destination">
|
||||
{{ _("Class Details") }}
|
||||
</span>
|
||||
</article>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro ClassHeaderDetails(class_info, courses, students) %}
|
||||
<div class="class-details" data-class="{{ class_info.name }}">
|
||||
|
||||
<div class="page-title">
|
||||
{{ class_info.title }}
|
||||
</div>
|
||||
|
||||
<div class="">
|
||||
{{ class_info.description }}
|
||||
</div>
|
||||
|
||||
<div class="mt-8">
|
||||
<svg class="icon icon-sm">
|
||||
<use href="#icon-calendar"></use>
|
||||
</svg>
|
||||
<span>
|
||||
{{ frappe.utils.format_date(class_info.start_date, "long") }} -
|
||||
</span>
|
||||
<span>
|
||||
{{ frappe.utils.format_date(class_info.end_date, "long") }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{% if class_info.start_time and class_info.end_time %}
|
||||
<div class="mt-1">
|
||||
<svg class="icon icon-sm">
|
||||
<use href="#icon-clock"></use>
|
||||
</svg>
|
||||
<span>
|
||||
{{ frappe.utils.format_time(class_info.start_time, "hh:mm a") }} -
|
||||
</span>
|
||||
<span>
|
||||
{{ frappe.utils.format_time(class_info.end_time, "hh:mm a") }}
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro CourseHeaderOverlay(class_info, courses, students) %}
|
||||
<div class="course-overlay-card class-overlay">
|
||||
|
||||
<div class="course-overlay-content">
|
||||
|
||||
{% if class_info.seat_count %}
|
||||
{% if seats_left %}
|
||||
<div class="indicator-pill green pull-right">
|
||||
{{ _("Seats Available") }}: {{ seats_left }}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="indicator-pill red pull-right">
|
||||
{{ _("No seats left") }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if class_info.paid_class %}
|
||||
<div class="bold-heading">
|
||||
{{ frappe.utils.fmt_money(class_info.amount, 0, class_info.currency) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="vertically-center mt-2">
|
||||
<svg class="icon icon-md mr-1">
|
||||
<use href="#icon-education"></use>
|
||||
</svg>
|
||||
{{ courses | length }} {{ _("Courses") }}
|
||||
</div>
|
||||
|
||||
<div class="mt-2">
|
||||
<svg class="icon icon-sm">
|
||||
<use href="#icon-calendar"></use>
|
||||
</svg>
|
||||
<span>
|
||||
{{ frappe.utils.format_date(class_info.start_date, "long") }} -
|
||||
</span>
|
||||
<span>
|
||||
{{ frappe.utils.format_date(class_info.end_date, "long") }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{% if class_info.start_time and class_info.end_time %}
|
||||
<div class="mt-2">
|
||||
<svg class="icon icon-sm">
|
||||
<use href="#icon-clock"></use>
|
||||
</svg>
|
||||
<span>
|
||||
{{ frappe.utils.format_time(class_info.start_time, "hh:mm a") }} -
|
||||
</span>
|
||||
<span>
|
||||
{{ frappe.utils.format_time(class_info.end_time, "hh:mm a") }}
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="mt-2">
|
||||
{% if is_moderator or is_evaluator or is_student %}
|
||||
<a class="btn btn-primary wide-button" href="/classes/{{ class_info.name }}">
|
||||
{{ _("Checkout Class") }}
|
||||
</a>
|
||||
{% elif class_info.paid_class %}
|
||||
<a class="btn btn-primary wide-button {% if class_info.seat_count and not seats_left %} hide {% endif %}"
|
||||
href="/billing/class/{{ class_info.name }}">
|
||||
{{ _("Register Now") }}
|
||||
</a>
|
||||
{% else %}
|
||||
<div class="alert alert-info">
|
||||
{{ _("To join this class, please contact the Administrator.") }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if is_moderator %}
|
||||
<div class="mt-2">
|
||||
<div class="btn btn-secondary wide-button" id="create-class">
|
||||
{{ _("Edit Class") }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{% macro ClassDetails(class_info) %}
|
||||
<div class="course-description-section w-50">
|
||||
<div class="mt-2">
|
||||
{{ class_info.class_details }}
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{% macro CourseList(courses) %}
|
||||
<div>
|
||||
<div class="page-title">
|
||||
{{ _("Courses") }}
|
||||
</div>
|
||||
{% if courses | length %}
|
||||
<div class="cards-parent mt-2">
|
||||
{% for course in courses %}
|
||||
<div class="h-100">
|
||||
{{ widgets.CourseCard(course=course, read_only=False) }}
|
||||
<button class="btn icon-btn btn-default btn-block btn-remove-course" data-course="{{ course.name }}">
|
||||
<svg class="icon icon-sm">
|
||||
<use href="#icon-delete"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="">
|
||||
{{ _("No courses") }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{%- block script %}
|
||||
{{ super() }}
|
||||
{% if is_moderator %}
|
||||
<script>
|
||||
let class_info = {{ class_info | json }};
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -1,3 +0,0 @@
|
||||
frappe.ready(() => {
|
||||
frappe.require("controls.bundle.js");
|
||||
});
|
||||
@@ -1,49 +0,0 @@
|
||||
import frappe
|
||||
from lms.lms.utils import has_course_moderator_role, has_course_evaluator_role
|
||||
from lms.www.utils import is_student
|
||||
|
||||
|
||||
def get_context(context):
|
||||
context.no_cache = 1
|
||||
class_name = frappe.form_dict["classname"]
|
||||
|
||||
context.class_info = frappe.db.get_value(
|
||||
"LMS Class",
|
||||
class_name,
|
||||
[
|
||||
"name",
|
||||
"title",
|
||||
"description",
|
||||
"class_details",
|
||||
"start_date",
|
||||
"end_date",
|
||||
"paid_class",
|
||||
"amount",
|
||||
"currency",
|
||||
"start_time",
|
||||
"end_time",
|
||||
"seat_count",
|
||||
],
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
context.courses = frappe.get_all(
|
||||
"Class Course",
|
||||
{"parent": class_name},
|
||||
["name", "course", "title"],
|
||||
order_by="creation desc",
|
||||
)
|
||||
|
||||
for course in context.courses:
|
||||
course.update(
|
||||
frappe.db.get_value(
|
||||
"LMS Course", course.course, ["name", "short_introduction", "image"], as_dict=1
|
||||
)
|
||||
)
|
||||
|
||||
context.student_count = frappe.db.count("Class Student", {"parent": class_name})
|
||||
context.seats_left = context.class_info.seat_count - context.student_count
|
||||
|
||||
context.is_moderator = has_course_moderator_role()
|
||||
context.is_evaluator = has_course_evaluator_role()
|
||||
context.is_student = is_student(class_name)
|
||||
@@ -1,183 +0,0 @@
|
||||
{% extends "lms/templates/lms_base.html" %}
|
||||
{% block title %}
|
||||
{{ _("All Classes") }}
|
||||
{% endblock %}
|
||||
|
||||
{% block page_content %}
|
||||
<div class="common-page-style lms-page-style">
|
||||
<div class="container">
|
||||
{{ Header() }}
|
||||
{% if past_classes | length or upcoming_classes | length %}
|
||||
{{ ClassTabs(past_classes, upcoming_classes, my_classes) }}
|
||||
{% else %}
|
||||
{{ EmptyState() }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% macro Header() %}
|
||||
<header class="edit-header">
|
||||
<div class="page-title mb-6"> {{ _("All Classes") }} </div>
|
||||
{% if is_moderator %}
|
||||
<button class="btn btn-default btn-sm pull-right" id="create-class">
|
||||
{{ _("Create Class") }}
|
||||
</button>
|
||||
{% endif %}
|
||||
</header>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro ClassTabs(past_classes, upcoming_classes, my_classes) %}
|
||||
<article>
|
||||
<ul class="nav lms-nav" id="courses-tab">
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="tab" href="#upcoming">
|
||||
{{ _("Upcoming") }}
|
||||
<span class="course-list-count">
|
||||
{{ upcoming_classes | length }}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
{% if is_moderator %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="tab" href="#past">
|
||||
{{ _("Archived") }}
|
||||
<span class="course-list-count">
|
||||
{{ past_classes | length }}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% if frappe.session.user != "Guest" %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="tab" href="#my-class">
|
||||
{{ _("Enrolled") }}
|
||||
<span class="course-list-count">
|
||||
{{ my_classes | length }}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
</ul>
|
||||
|
||||
<div class="border-bottom mb-4"></div>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="upcoming" role="tabpanel" aria-labelledby="upcoming">
|
||||
{{ ClassCards(upcoming_classes, show_price=True) }}
|
||||
</div>
|
||||
|
||||
{% if is_moderator %}
|
||||
<div class="tab-pane" id="past" role="tabpanel" aria-labelledby="past">
|
||||
{{ ClassCards(past_classes, show_price=False) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if frappe.session.user != "Guest" %}
|
||||
<div class="tab-pane" id="my-class" role="tabpanel" aria-labelledby="my-classes">
|
||||
{{ ClassCards(my_classes, show_price=False) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</article>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro ClassCards(classes, show_price=False) %}
|
||||
<div class="lms-card-parent">
|
||||
{% for class in classes %}
|
||||
|
||||
<div class="common-card-style column-card" style="min-height: 150px;">
|
||||
|
||||
{% if class.seat_count %}
|
||||
{% if class.seats_left > 0 %}
|
||||
<div class="indicator-pill green align-self-start mb-2">
|
||||
{{ _("Seats Available") }}: {{ class.seats_left }}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="indicator-pill red align-self-start mb-2">
|
||||
{{ _("No Seats Left") }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<div class="bold-heading">
|
||||
{{ class.title }}
|
||||
</div>
|
||||
|
||||
{% if class.description %}
|
||||
<div class="short-introduction">
|
||||
{{ class.description }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if show_price and class.paid_class %}
|
||||
<div class="bold-heading mb-2">
|
||||
{{ frappe.utils.fmt_money(class.amount, 0, class.currency) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="mt-auto mb-2">
|
||||
<svg class="icon icon-sm">
|
||||
<use href="#icon-calendar"></use>
|
||||
</svg>
|
||||
<span>
|
||||
{{ frappe.utils.format_date(class.start_date, "medium") }} -
|
||||
</span>
|
||||
<span>
|
||||
{{ frappe.utils.format_date(class.end_date, "medium") }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<svg class="icon icon-md">
|
||||
<use href="#icon-education"></use>
|
||||
</svg>
|
||||
{{ class.course_count }} {{ _("Courses") }}
|
||||
</div>
|
||||
|
||||
<!-- <div class="mb-2">
|
||||
<svg class="icon icon-md">
|
||||
<use href="#icon-users"></use>
|
||||
</svg>
|
||||
{{ student_count }} {{ _("Students") }}
|
||||
</div> -->
|
||||
|
||||
{% if is_student(class.name) %}
|
||||
<a class="stretched-link" href="/classes/{{ class.name }}"></a>
|
||||
{% else %}
|
||||
<a class="stretched-link" href="/classes/details/{{ class.name }}"></a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro EmptyState() %}
|
||||
<div class="empty-state">
|
||||
<img class="icon icon-xl" src="/assets/lms/icons/comment.svg">
|
||||
<div class="empty-state-text">
|
||||
<div class="empty-state-heading">{{ _("No Classes") }}</div>
|
||||
<div class="course-meta">{{ _("Nothing to see here.") }}</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{%- block script %}
|
||||
{{ super() }}
|
||||
{{ include_script('controls.bundle.js') }}
|
||||
{% if is_moderator %}
|
||||
<script>
|
||||
frappe.boot.user = {
|
||||
"can_create": [],
|
||||
"can_select": ["LMS Category"],
|
||||
"can_read": ["LMS Category"]
|
||||
};
|
||||
let class_info = null;
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -1,76 +0,0 @@
|
||||
import frappe
|
||||
from frappe.utils import getdate
|
||||
from lms.lms.utils import has_course_moderator_role, has_course_evaluator_role
|
||||
|
||||
|
||||
def get_context(context):
|
||||
context.no_cache = 1
|
||||
context.is_moderator = has_course_moderator_role()
|
||||
context.is_evaluator = has_course_evaluator_role()
|
||||
classes = frappe.get_all(
|
||||
"LMS Class",
|
||||
fields=[
|
||||
"name",
|
||||
"title",
|
||||
"description",
|
||||
"start_date",
|
||||
"end_date",
|
||||
"paid_class",
|
||||
"amount",
|
||||
"currency",
|
||||
"seat_count",
|
||||
],
|
||||
order_by="start_date",
|
||||
)
|
||||
|
||||
past_classes, upcoming_classes = [], []
|
||||
for class_ in classes:
|
||||
class_.student_count = frappe.db.count("Class Student", {"parent": class_.name})
|
||||
class_.course_count = frappe.db.count("Class Course", {"parent": class_.name})
|
||||
class_.seats_left = (
|
||||
class_.seat_count - class_.student_count if class_.seat_count else None
|
||||
)
|
||||
print(class_.seat_count, class_.student_count, class_.seats_left)
|
||||
if getdate(class_.start_date) < getdate():
|
||||
past_classes.append(class_)
|
||||
else:
|
||||
upcoming_classes.append(class_)
|
||||
|
||||
context.past_classes = sorted(past_classes, key=lambda d: d.start_date)
|
||||
context.upcoming_classes = sorted(upcoming_classes, key=lambda d: d.start_date)
|
||||
|
||||
if frappe.session.user != "Guest":
|
||||
my_classes_info = []
|
||||
my_classes = frappe.get_all(
|
||||
"Class Student", {"student": frappe.session.user}, pluck="parent"
|
||||
)
|
||||
|
||||
for class_ in my_classes:
|
||||
class_info = frappe.db.get_value(
|
||||
"LMS Class",
|
||||
class_,
|
||||
[
|
||||
"name",
|
||||
"title",
|
||||
"description",
|
||||
"start_date",
|
||||
"end_date",
|
||||
"paid_class",
|
||||
"amount",
|
||||
"currency",
|
||||
"seat_count",
|
||||
],
|
||||
as_dict=True,
|
||||
)
|
||||
|
||||
class_info.student_count = frappe.db.count(
|
||||
"Class Student", {"parent": class_info.name}
|
||||
)
|
||||
class_info.course_count = frappe.db.count(
|
||||
"Class Course", {"parent": class_info.name}
|
||||
)
|
||||
class_info.seats_left = class_info.seat_count - class_info.student_count
|
||||
|
||||
my_classes_info.append(class_info)
|
||||
|
||||
context.my_classes = my_classes_info
|
||||
@@ -1,90 +0,0 @@
|
||||
{% extends "lms/templates/lms_base.html" %}
|
||||
{% block title %}
|
||||
{{ student.first_name }}'s {{ _("Progress") }}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block page_content %}
|
||||
<div class="common-page-style">
|
||||
{{ Header() }}
|
||||
<div class="container">
|
||||
{{ Progress(class_info, student) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% macro Header() %}
|
||||
<header class="sticky mb-5">
|
||||
<div class="container">
|
||||
<div class="edit-header">
|
||||
<div>
|
||||
<div class="page-title">
|
||||
{{ _("{0}").format(student.full_name) }}
|
||||
</div>
|
||||
<div class="vertically-center">
|
||||
<a class="dark-links" href="/classes">
|
||||
{{ _("All Classes") }}
|
||||
</a>
|
||||
<img class="icon icon-sm mr-0" src="/assets/lms/icons/chevron-right.svg">
|
||||
<a class="dark-links" href="/classes/{{ class_info.name }}">
|
||||
{{ class_info.name }}
|
||||
</a>
|
||||
<img class="icon icon-sm mr-0" src="/assets/lms/icons/chevron-right.svg">
|
||||
<span class="breadcrumb-destination">
|
||||
{{ _("Student Progress").format(student.full_name) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="align-self-center">
|
||||
<a class="btn btn-default btn-sm" href="/users/{{ student.username }}">
|
||||
{{ _("View Profile") }}
|
||||
</a>
|
||||
{% if student.name == frappe.session.user %}
|
||||
<button class="btn btn-default btn-sm btn-schedule-eval ml-2">
|
||||
{{ _("Schedule Evaluation") }}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if is_moderator %}
|
||||
<a class="btn btn-primary btn-sm btn-evaluate ml-2" href="/evaluation/new?member={{student.name}}&date={{frappe.utils.getdate()}}&class_name={{class_info.name}}">
|
||||
{{ _("Evaluate") }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{% macro Progress(class_info, student) %}
|
||||
{{ UpcomingEvals(upcoming_evals) }}
|
||||
{{ Assessments(class_info, student) }}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro UpcomingEvals(upcoming_evals) %}
|
||||
<div class="mb-8">
|
||||
{% include "lms/templates/upcoming_evals.html" %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro Assessments(class_info, student) %}
|
||||
<div class="mb-8">
|
||||
{% include "lms/templates/assessments.html" %}
|
||||
</div>
|
||||
|
||||
{% endmacro %}
|
||||
|
||||
{%- block script %}
|
||||
{{ super() }}
|
||||
<script>
|
||||
frappe.boot.user = {
|
||||
"can_create": [],
|
||||
"can_select": ["LMS Course"],
|
||||
"can_read": ["LMS Course"]
|
||||
};
|
||||
let courses = {{ courses | json }};
|
||||
let class_name = "{{ class_info.name }}";
|
||||
</script>
|
||||
{{ include_script('controls.bundle.js') }}
|
||||
{% endblock %}
|
||||
@@ -1,5 +0,0 @@
|
||||
frappe.ready(() => {
|
||||
$(".clickable-row").click((e) => {
|
||||
window.location.href = $(e.currentTarget).data("href");
|
||||
});
|
||||
});
|
||||
@@ -1,41 +0,0 @@
|
||||
import frappe
|
||||
from lms.lms.utils import (
|
||||
has_course_moderator_role,
|
||||
has_course_evaluator_role,
|
||||
get_upcoming_evals,
|
||||
)
|
||||
from frappe import _
|
||||
from lms.www.utils import get_assessments
|
||||
|
||||
|
||||
def get_context(context):
|
||||
context.no_cache = 1
|
||||
|
||||
student = frappe.form_dict["username"]
|
||||
class_name = frappe.form_dict["classname"]
|
||||
context.is_moderator = has_course_moderator_role()
|
||||
context.is_evaluator = has_course_evaluator_role()
|
||||
|
||||
context.student = frappe.db.get_value(
|
||||
"User",
|
||||
{"username": student},
|
||||
["first_name", "full_name", "name", "last_active", "username"],
|
||||
as_dict=True,
|
||||
)
|
||||
if (
|
||||
not context.is_moderator
|
||||
and not context.is_evaluator
|
||||
and not context.student.name == frappe.session.user
|
||||
):
|
||||
raise frappe.PermissionError(_("You don't have permission to access this page."))
|
||||
|
||||
context.class_info = frappe.db.get_value(
|
||||
"LMS Class", class_name, ["name"], as_dict=True
|
||||
)
|
||||
|
||||
context.courses = frappe.get_all(
|
||||
"Class Course", {"parent": class_name}, pluck="course"
|
||||
)
|
||||
|
||||
context.assessments = get_assessments(class_name, context.student.name)
|
||||
context.upcoming_evals = get_upcoming_evals(context.student.name, context.courses)
|
||||
Reference in New Issue
Block a user