feat: student dashboard

This commit is contained in:
Jannat Patel
2023-07-31 12:36:13 +05:30
parent cef3d21ab3
commit e7a2eb7373
10 changed files with 498 additions and 307 deletions

View File

@@ -57,21 +57,28 @@ def generate_slug(title, doctype):
return slugify(title, used_slugs=slugs)
def get_membership(course, member, batch=None):
def get_membership(course, member=None, batch=None):
if not member:
member = frappe.session.user
filters = {"member": member, "course": course}
if batch:
filters["batch"] = batch
membership = frappe.db.get_value(
"LMS Batch Membership",
filters,
["name", "batch", "current_lesson", "member_type", "progress"],
as_dict=True,
)
is_member = frappe.db.exists("LMS Batch Membership", filters)
if is_member:
membership = frappe.db.get_value(
"LMS Batch Membership",
filters,
["name", "batch", "current_lesson", "member_type", "progress"],
as_dict=True,
)
if membership and membership.batch:
membership.batch_title = frappe.db.get_value("LMS Batch", membership.batch, "title")
return membership
if membership and membership.batch:
membership.batch_title = frappe.db.get_value("LMS Batch", membership.batch, "title")
return membership
return False
def get_chapters(course):
@@ -524,7 +531,7 @@ def has_course_moderator_role(member=None):
def has_course_evaluator_role(member=None):
return frappe.db.get_value(
"Has Role",
{"parent": member or frappe.session.user, "role": "Evaluator"},
{"parent": member or frappe.session.user, "role": "Class Evaluator"},
"name",
)
@@ -700,7 +707,7 @@ def get_chart_data(chart_name, timespan, timegrain, from_date, to_date):
}
@frappe.whitelist(allow_guest=True)
@frappe.whitelist()
def get_course_completion_data():
all_membership = frappe.db.count("LMS Batch Membership")
completed = frappe.db.count("LMS Batch Membership", {"progress": ["like", "%100%"]})
@@ -785,3 +792,23 @@ def get_evaluator(course, class_name=None):
evaluator = frappe.db.get_value("LMS Course", course, "evaluator")
return evaluator
def get_upcoming_evals(student, courses):
print(student, courses)
upcoming_evals = frappe.get_all(
"LMS Certificate Request",
{
"member": student,
"course": ["in", courses],
"date": [">=", frappe.utils.nowdate()],
},
["date", "start_time", "course", "evaluator", "google_meet_link"],
order_by="date",
)
for evals in upcoming_evals:
evals.course_title = frappe.db.get_value("LMS Course", evals.course, "title")
evals.evaluator_name = frappe.db.get_value("User", evals.evaluator, "full_name")
print(upcoming_evals)
return upcoming_evals

View File

@@ -326,7 +326,7 @@ input[type=checkbox] {
border-radius: var(--border-radius-md);
position: relative;
border: 1px solid var(--gray-300);
box-shadow: var(--card-shadow);
box-shadow: var(--shadow-inset);
}
.course-card {
@@ -1650,10 +1650,6 @@ li {
color: var(--gray-600);
}
.discussions-parent .empty-state {
background-color: var(--gray-200);
}
.job-cards-parent {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(500px, 1fr));
@@ -2204,4 +2200,77 @@ select {
.rows .grid-row .grid-footer-toolbar,
.grid-form-heading {
cursor: none;
}
.lms-page-style .discussions-section-title {
font-size: var(--text-lg);
}
.class-dashboard .progress {
width: 150px;
height: 150px;
background: none;
position: relative;
}
.class-dashboard .progress::after {
content: "";
width: 100%;
height: 100%;
border-radius: 50%;
border: 6px solid #eee;
position: absolute;
top: 0;
left: 0;
}
.class-dashboard .progress>span {
width: 50%;
height: 100%;
overflow: hidden;
position: absolute;
top: 0;
z-index: 1;
}
.class-dashboard .progress .progress-left {
left: 0;
}
.class-dashboard .progress .progress-bar {
width: 100%;
height: 100%;
background: none;
border-width: 6px;
border-style: solid;
position: absolute;
top: 0;
}
.class-dashboard .progress .progress-left .progress-bar {
left: 100%;
border-top-right-radius: 80px;
border-bottom-right-radius: 80px;
border-left: 0;
-webkit-transform-origin: center left;
transform-origin: center left;
}
.class-dashboard .progress .progress-right {
right: 0;
}
.class-dashboard .progress .progress-right .progress-bar {
left: -100%;
border-top-left-radius: 80px;
border-bottom-left-radius: 80px;
border-right: 0;
-webkit-transform-origin: center right;
transform-origin: center right;
}
.class-dashboard .progress .progress-value {
position: absolute;
top: 0;
left: 0;
}

View File

@@ -0,0 +1,60 @@
<div>
<div class="bold-heading mb-2">
{{ _("Assessments") }}
</div>
{% if assessments | length %}
<article class="form-grid">
<div class="grid-heading-row">
<div class="grid-row">
<div class="data-row row">
<div class="col grid-static-col">
{{ _("Assessment") }}
</div>
<div class="col grid-static-col col-xs-2">
{{ _("Type") }}
</div>
<div class="col grid-static-col col-xs-2">
{{ _("Status/Score") }}
</div>
</div>
</div>
</div>
{% for assessment in assessments %}
{% set has_access = is_moderator and assessment.submission or frappe.session.user == student.name %}
<div class="grid-row">
<div class="data-row row">
<a class="col grid-static-col {% if has_access %} clickable {% endif %}" {% if has_access %} href="{{ assessment.url }}" {% endif %}>
{{ assessment.title }}
</a>
<div class="col grid-static-col col-xs-2">
{{ (assessment.assessment_type).split("LMS ")[1] }}
</div>
<div class="col grid-static-col col-xs-2 mb-2">
{% if assessment.submission %}
{% if assessment.assessment_type == "LMS Assignment" %}
{% set status = assessment.submission.status %}
{% set color = "green" if status == "Pass" else "red" if status == "Fail" else "orange" %}
<div class="indicator-pill {{ color }}">
{{ status }}
</div>
{% else %}
<div>
{{ assessment.submission.score }}
</div>
{% endif %}
{% else %}
<div class="indicator-pill red">
{{ _("Not Attempted") }}
</div>
{% endif %}
</div>
</div>
</div>
{% endfor %}
</article>
{% else %}
<p class="text-muted"> {{ _("No Assessments") }} </p>
{% endif %}
</div>

View File

@@ -0,0 +1,45 @@
<div>
<div class="bold-heading mb-2">
{{ _("Upcoming Evaluations") }}
</div>
{% if upcoming_evals | length %}
<article class="cards-parent">
{% for eval in upcoming_evals %}
<div class="common-card-style column-card">
<div class="flex align-center justify-between">
<div class="bold-heading">
{{ eval.course_title }}
</div>
{% if eval.google_meet_link %}
<a class="btn btn-default btn-sm pull-right" href="{{ eval.google_meet_link }}">
{{ _("Join") }}
</a>
{% endif %}
</div>
<div class="vertically-center">
<svg class="icon icon-sm mr-1">
<use href="#icon-calendar"></use>
</svg>
<span>
{{ frappe.utils.format_date(eval.date, "medium") }}&nbsp;-&nbsp;
</span>
<span>
{{ frappe.utils.format_time(eval.start_time, "hh:mm a") }}
</span>
</div>
<div>
<span class="field-label">
{{ _("Evaluator") }}:
</span>
<span>
{{ eval.evaluator_name }}
</span>
</div>
</div>
{% endfor %}
</article>
{% else %}
<p class="text-muted"> {{ _("No Upcoming Evaluations") }} </p>
{% endif %}
</div>

View File

@@ -10,7 +10,7 @@
{{ BreadCrumb(class_info) }}
<div class="">
{{ ClassDetails(class_info) }}
{{ ClassSections(class_info, class_courses, class_students, published_courses) }}
{{ ClassSections(class_info, class_courses, class_students) }}
</div>
</div>
</div>
@@ -82,7 +82,7 @@
<!-- Class Sections -->
{% macro ClassSections(class_info, class_courses, class_students, published_courses) %}
{% macro ClassSections(class_info, class_courses, class_students) %}
<div class="mt-4">
{% if is_moderator %}
@@ -92,14 +92,17 @@
{% endif %}
<ul class="nav lms-nav" id="classes-tab">
{% if is_student %}
<li class="nav-item">
<a class="nav-link active" data-toggle="tab" href="#dashboard">
<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" data-toggle="tab" href="#courses">
<a class="nav-link {% if not is_student %} active {% endif %}" data-toggle="tab" href="#courses">
{{ _("Courses") }}
<span class="course-list-count">
{{ class_courses | length }}
@@ -128,6 +131,12 @@
{% 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") }}
@@ -143,12 +152,15 @@
<div class="border-bottom mb-4"></div>
<div class="tab-content">
<div class="tab-pane active" id="dashboard" role="tabpanel" aria-labelledby="dashboard">
{{ Dashboard(class_info) }}
</div>
<div class="tab-pane" id="courses" role="tabpanel" aria-labelledby="courses">
{{ CoursesSection(class_info, class_courses, published_courses) }}
{% 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>
<div class="tab-pane" id="students" role="tabpanel" aria-labelledby="students">
@@ -162,6 +174,10 @@
{% endif %}
{% if class_students | length and (is_moderator or is_student) %}
<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>
@@ -171,12 +187,53 @@
</div>
{% endmacro %}
{% macro Dashboard(class_info) %}
{% 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>
<!-- <div class="class-dashboard">
{% for course in is_student.courses %}
{% set value = is_student.courses[course] %}
{% set degree = value / 100 * 360 %}
<div class="progress mx-auto" data-value="{{ value }}">
<span class="progress-left">
<span class="progress-bar border-primary"
{% if value > 50 %} style="transform: rotate({{ degree }}deg);" {% endif %}></span>
</span>
<span class="progress-right">
<span class="progress-bar border-primary"
{% if value > 0 and value <= 50 %} style="transform: rotate({{ degree }}deg);" {% endif %}></span>
</span>
<div class="progress-value w-100 h-100 rounded-circle flex align-center justify-center">
<div class="bold-heading">
{{ value }} <sup class="small">%</sup>
</div>
</div>
</div>
{% endfor %}
</div> -->
{% endmacro %}
{% macro Discussions(class_info) %}
<article class="class-discussion">
{% set condition = is_moderator or is_student %}
{% set doctype, docname = _("LMS Class"), class_info.name %}
{% set single_thread = True %}
{% set title = "Announcements" %}
{% set title = "Discussions" %}
{% set cta_title = "Post" %}
{% set button_name = _("Start Learning") %}
{% set redirect_to = "/classes/" + class_info.name %}
@@ -186,7 +243,7 @@
</article>
{% endmacro %}
{% macro CoursesSection(class_info, class_courses, published_courses) %}
{% macro CoursesSection(class_info, class_courses) %}
<article>
<header class="mb-5">
<div class="edit-header">
@@ -488,6 +545,15 @@
let class_info = {{ class_info | json }};
</script>
{% else %}
<script>
frappe.boot.user = {
"can_create": [],
"can_select": ["LMS Course"],
"can_read": ["LMS Course"]
};
let courses = {{ course_list | json }};
</script>
{% endif %}
{{ include_script('controls.bundle.js') }}

View File

@@ -42,6 +42,14 @@ frappe.ready(() => {
$(".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) => {
@@ -544,3 +552,121 @@ const remove_assessment = (e) => {
});
});
};
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 day = moment(this.eval_form.get_value("date")).format("dddd");
slots.forEach((slot) => {
if (slot.day == day) {
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>`;
}
});
if (!slot_html) {
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"));
}
this.eval_form.hide();
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) => {
frappe.show_alert({
message: __("Evaluation scheduled successfully"),
indicator: "green",
});
setTimeout(() => {
window.location.reload();
}, 1000);
},
});
};

View File

@@ -1,13 +1,14 @@
from frappe import _
import frappe
from frappe.utils import getdate
from frappe.utils import getdate, cint
from lms.www.utils import get_assessments
from lms.lms.utils import (
has_course_moderator_role,
has_course_evaluator_role,
get_course_progress,
get_upcoming_evals,
has_submitted_assessment,
has_graded_assessment,
get_membership,
)
@@ -39,14 +40,10 @@ def get_context(context):
context.reference_doctype = "LMS Class"
context.reference_name = class_name
context.published_courses = frappe.get_all(
"LMS Course", {"published": 1}, ["name", "title"]
)
class_courses = frappe.get_all(
"Class Course",
{"parent": class_name},
["name", "course"],
["name", "course", "title"],
order_by="creation desc",
)
@@ -65,6 +62,7 @@ def get_context(context):
)
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_list(
"LMS Course", fields=["name", "title"], limit_page_length=0
)
@@ -74,6 +72,10 @@ def get_context(context):
class_students, class_courses, context.assessments
)
context.is_student = is_student(class_students)
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)
@@ -139,36 +141,47 @@ def get_class_student_details(class_students, class_courses, assessments):
)
)
student.update(frappe.db.get_value("User", student.student, "last_active", as_dict=1))
courses_completed = 0
for course in class_courses:
if get_course_progress(course.course, student.student) == 100:
courses_completed += 1
student["courses_completed"] = courses_completed
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
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 = []
@@ -188,3 +201,25 @@ def sort_students(class_students):
def is_student(class_students):
students = [student.student for student in class_students]
return frappe.session.user in students
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

View File

@@ -64,112 +64,13 @@
{% macro UpcomingEvals(upcoming_evals) %}
<div class="mb-8">
<div class="bold-heading mb-2">
{{ _("Upcoming Evaluations") }}
</div>
{% if upcoming_evals | length %}
<article class="cards-parent">
{% for eval in upcoming_evals %}
<div class="common-card-style column-card">
<div class="flex align-center justify-between">
<div class="bold-heading">
{{ eval.course_title }}
</div>
{% if eval.google_meet_link %}
<a class="btn btn-default btn-sm pull-right" href="{{ eval.google_meet_link }}">
{{ _("Join") }}
</a>
{% endif %}
</div>
<div class="vertically-center">
<svg class="icon icon-sm mr-1">
<use href="#icon-calendar"></use>
</svg>
<span>
{{ frappe.utils.format_date(eval.date, "medium") }}&nbsp;-&nbsp;
</span>
<span>
{{ frappe.utils.format_time(eval.start_time, "hh:mm a") }}
</span>
</div>
<div>
<span class="field-label">
{{ _("Evaluator") }}:
</span>
<span>
{{ eval.evaluator_name }}
</span>
</div>
</div>
{% endfor %}
</article>
{% else %}
<p class="text-muted"> {{ _("No Upcoming Evaluations") }} </p>
{% endif %}
{% include "lms/templates/upcoming_evals.html" %}
</div>
{% endmacro %}
{% macro Assessments(class_info, student) %}
<div class="mb-8">
<div class="bold-heading mb-2">
{{ _("Assessments") }}
</div>
{% if assessments | length %}
<article class="form-grid">
<div class="grid-heading-row">
<div class="grid-row">
<div class="data-row row">
<div class="col grid-static-col">
{{ _("Assessment") }}
</div>
<div class="col grid-static-col col-xs-2">
{{ _("Type") }}
</div>
<div class="col grid-static-col col-xs-2">
{{ _("Status/Score") }}
</div>
</div>
</div>
</div>
{% for assessment in assessments %}
{% set has_access = is_moderator and assessment.submission or frappe.session.user == student.name %}
<div class="grid-row">
<div class="data-row row">
<a class="col grid-static-col {% if has_access %} clickable {% endif %}" {% if has_access %} href="{{ assessment.url }}" {% endif %}>
{{ assessment.title }}
</a>
<div class="col grid-static-col col-xs-2">
{{ (assessment.assessment_type).split("LMS ")[1] }}
</div>
<div class="col grid-static-col col-xs-2 mb-2">
{% if assessment.submission %}
{% if assessment.assessment_type == "LMS Assignment" %}
{% set status = assessment.submission.status %}
{% set color = "green" if status == "Pass" else "red" if status == "Fail" else "orange" %}
<div class="indicator-pill {{ color }}">
{{ status }}
</div>
{% else %}
<div>
{{ assessment.submission.score }}
</div>
{% endif %}
{% else %}
<div class="indicator-pill red">
{{ _("Not Attempted") }}
</div>
{% endif %}
</div>
</div>
</div>
{% endfor %}
</article>
{% else %}
<p class="text-muted"> {{ _("No Assessments") }} </p>
{% endif %}
{% include "lms/templates/assessments.html" %}
</div>
{% endmacro %}

View File

@@ -2,130 +2,4 @@ frappe.ready(() => {
$(".clickable-row").click((e) => {
window.location.href = $(e.currentTarget).data("href");
});
$(".btn-schedule-eval").click((e) => {
open_evaluation_form(e);
});
$(document).on("click", ".slot", (e) => {
mark_active_slot(e);
});
});
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_name,
},
callback: (r) => {
if (r.message) {
display_slots(r.message);
}
},
});
};
const display_slots = (slots) => {
let slot_html = "";
let day = moment(this.eval_form.get_value("date")).format("dddd");
slots.forEach((slot) => {
if (slot.day == day) {
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>`;
}
});
if (!slot_html) {
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"));
}
this.eval_form.hide();
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_name,
},
callback: (r) => {
frappe.show_alert({
message: __("Evaluation scheduled successfully"),
indicator: "green",
});
setTimeout(() => {
window.location.reload();
}, 1000);
},
});
};

View File

@@ -1,5 +1,9 @@
import frappe
from lms.lms.utils import has_course_moderator_role, has_course_evaluator_role
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
@@ -34,20 +38,4 @@ def get_context(context):
)
context.assessments = get_assessments(class_name, context.student.name)
upcoming_evals = frappe.get_all(
"LMS Certificate Request",
{
"member": context.student.name,
"course": ["in", context.courses],
"date": [">=", frappe.utils.nowdate()],
},
["date", "start_time", "course", "evaluator", "google_meet_link"],
order_by="date",
)
for evals in upcoming_evals:
evals.course_title = frappe.db.get_value("LMS Course", evals.course, "title")
evals.evaluator_name = frappe.db.get_value("User", evals.evaluator, "full_name")
context.upcoming_evals = upcoming_evals
context.upcoming_evals = get_upcoming_evals(context.student.name, context.courses)