fix: renamed class to batch

This commit is contained in:
Jannat Patel
2023-08-28 10:59:52 +05:30
parent 9376b0b010
commit 27101ffd31
40 changed files with 406 additions and 383 deletions
View File
-607
View File
@@ -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 %}
-693
View File
@@ -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);
},
});
};
-278
View File
@@ -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
-209
View File
@@ -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 %}
-3
View File
@@ -1,3 +0,0 @@
frappe.ready(() => {
frappe.require("controls.bundle.js");
});
-49
View File
@@ -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)
-183
View File
@@ -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 %}
-76
View File
@@ -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
-90
View File
@@ -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 %}
-5
View File
@@ -1,5 +0,0 @@
frappe.ready(() => {
$(".clickable-row").click((e) => {
window.location.href = $(e.currentTarget).data("href");
});
});
-41
View File
@@ -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)