fix: class improvements

This commit is contained in:
Jannat Patel
2023-06-23 20:16:48 +05:30
parent 3d8237008f
commit 99f1a8dfc3
15 changed files with 463 additions and 200 deletions

View File

@@ -46,7 +46,7 @@
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"links": [], "links": [],
"modified": "2023-05-29 14:50:55.259990", "modified": "2023-06-23 12:34:21.314971",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "LMS", "module": "LMS",
"name": "LMS Assignment", "name": "LMS Assignment",
@@ -64,9 +64,22 @@
"role": "System Manager", "role": "System Manager",
"share": 1, "share": 1,
"write": 1 "write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Moderator",
"share": 1,
"write": 1
} }
], ],
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"states": [] "states": [],
"title_field": "title"
} }

View File

@@ -50,7 +50,8 @@
{ {
"fieldname": "description", "fieldname": "description",
"fieldtype": "Small Text", "fieldtype": "Small Text",
"label": "Description" "label": "Description",
"reqd": 1
}, },
{ {
"fieldname": "section_break_6", "fieldname": "section_break_6",
@@ -137,7 +138,7 @@
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"links": [], "links": [],
"modified": "2023-06-15 12:30:26.929156", "modified": "2023-06-22 15:57:25.190084",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "LMS", "module": "LMS",
"name": "LMS Class", "name": "LMS Class",

View File

@@ -8,6 +8,7 @@ from frappe.utils import cint, format_date, format_datetime
import requests import requests
import base64 import base64
import json import json
from lms.lms.utils import has_course_moderator_role
class LMSClass(Document): class LMSClass(Document):
@@ -188,6 +189,9 @@ def create_class(
@frappe.whitelist() @frappe.whitelist()
def update_assessment(type, name, value, class_name): def update_assessment(type, name, value, class_name):
if not has_course_moderator_role():
return
value = cint(value) value = cint(value)
filters = { filters = {
"assessment_type": type, "assessment_type": type,

View File

@@ -70,7 +70,7 @@
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"links": [], "links": [],
"modified": "2023-06-21 09:13:01.322701", "modified": "2023-06-23 12:35:25.204131",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "LMS", "module": "LMS",
"name": "LMS Quiz", "name": "LMS Quiz",
@@ -87,6 +87,18 @@
"role": "System Manager", "role": "System Manager",
"share": 1, "share": 1,
"write": 1 "write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Moderator",
"share": 1,
"write": 1
} }
], ],
"show_title_field_in_link": 1, "show_title_field_in_link": 1,

View File

@@ -733,3 +733,23 @@ def is_onboarding_complete():
if course_created if course_created
else None, else None,
} }
def has_submitted_assessment(assessment, type, member=None):
if not member:
member = frappe.session.user
doctype = (
"LMS Assignment Submission" if type == "LMS Assignment" else "LMS Quiz Submission"
)
docfield = "assignment" if type == "LMS Assignment" else "quiz"
filters = {}
filters[docfield] = assessment
filters["member"] = member
return frappe.db.exists(doctype, filters)
def has_graded_assessment(submission):
status = frappe.db.get_value("LMS Assignment Submission", submission, "status")
return False if status == "Not Graded" else True

View File

@@ -72,6 +72,9 @@
{% endif %} {% endif %}
</div> </div>
<div class="course-card-title">{{ course.title }}</div> <div class="course-card-title">{{ course.title }}</div>
<div class="short-introduction">
{{ course.short_introduction }}
</div>
{% if membership and not is_instructor(course.name) and not read_only %} {% if membership and not is_instructor(course.name) and not read_only %}
<div class="progress"> <div class="progress">

View File

@@ -374,10 +374,18 @@ input[type=checkbox] {
.course-card-title { .course-card-title {
font-weight: 600; font-weight: 600;
color: var(--gray-900); color: var(--gray-900);
margin-bottom: 1.25rem;
font-size: 1.125rem; font-size: 1.125rem;
} }
.short-introduction {
text-overflow: ellipsis;
width: 100%;
overflow: hidden;
white-space: nowrap;
margin-bottom: 1.25rem;
}
.card-divider { .card-divider {
border-top: 1px solid var(--gray-300); border-top: 1px solid var(--gray-300);
margin-bottom: 1rem; margin-bottom: 1rem;
@@ -1385,6 +1393,10 @@ pre {
margin: 0 1rem; margin: 0 1rem;
} }
.seperator::after {
content: "\00B7";
}
.course-overlay-card { .course-overlay-card {
background-color: white; background-color: white;
border-radius: var(--border-radius-lg); border-radius: var(--border-radius-lg);
@@ -1890,6 +1902,14 @@ li {
padding: 0 1rem !important; padding: 0 1rem !important;
} }
.modal-dialog-scrollable .modal-body {
overflow-y: unset;
}
.modal-dialog-scrollable .modal-content {
overflow: unset;
}
.modal-header, .modal-body { .modal-header, .modal-body {
margin-bottom: 0.5rem !important; margin-bottom: 0.5rem !important;
} }
@@ -2168,3 +2188,9 @@ select {
.awesomplete ul li:last-child { .awesomplete ul li:last-child {
display: none; display: none;
} }
.students-parent {
display: grid;
grid-template-columns: repeat(auto-fill, 150px);
grid-gap: 1rem;
}

View File

@@ -326,6 +326,7 @@ const open_class_dialog = () => {
label: __("Description"), label: __("Description"),
fieldname: "description", fieldname: "description",
default: class_info && class_info.description, default: class_info && class_info.description,
reqd: 1,
}, },
], ],
primary_action_label: __("Save"), primary_action_label: __("Save"),

View File

@@ -34,7 +34,15 @@
<div class="page-title"> <div class="page-title">
{{ class_info.title }} {{ class_info.title }}
</div> </div>
<div class="mt-1">
{% if class_info.description %}
<div class="mb-4">
{{ class_info.description }}
</div>
{% endif %}
<div class="vertically-center">
<div class="">
<svg class="icon icon-sm"> <svg class="icon icon-sm">
<use href="#icon-calendar"></use> <use href="#icon-calendar"></use>
</svg> </svg>
@@ -45,11 +53,26 @@
{{ frappe.utils.format_date(class_info.end_date, "long") }} {{ frappe.utils.format_date(class_info.end_date, "long") }}
</span> </span>
</div> </div>
{% if class_info.description %}
<div class="mt-1"> <span class="seperator"></span>
{{ class_info.description }}
<div class="">
<svg class="icon icon-md">
<use href="#icon-education"></use>
</svg>
{{ class_courses | length }} {{ _("Courses") }}
</div> </div>
{% endif %}
<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 %} {% if class_info.custom_component %}
{{ class_info.custom_component }} {{ class_info.custom_component }}
@@ -155,20 +178,9 @@
</header> </header>
{% if class_courses | length %} {% if class_courses | length %}
<div> <div class="cards-parent">
{% for course in class_courses %} {% for course in class_courses %}
<div class="list-row level"> {{ widgets.CourseCard(course=course, read_only=False) }}
<a class="clickable" href="/courses/{{ course.course }}">
{{ course.title }}
</a>
{% if is_moderator %}
<div type="button" class="btn-remove-course" data-course="{{ course.course }}">
<svg class="icon icon-sm">
<use href="#icon-delete"></use>
</svg>
</div>
{% endif %}
</div>
{% endfor %} {% endfor %}
</div> </div>
{% else %} {% else %}
@@ -177,7 +189,6 @@
</div> </div>
{% endif %} {% endif %}
</article> </article>
{% endmacro %} {% endmacro %}
@@ -198,19 +209,56 @@
</header> </header>
{% if class_students | length %} {% if class_students | length %}
<div> <div class="form-grid">
{% for student in class_students %} <div class="grid-heading-row">
{% set last_active = frappe.db.get_value("User", student.student, "last_active") %} <div class="grid-row">
{% set allow_progress = is_moderator or student.student == frappe.session.user %} <div class="data-row row">
<div class="col grid-static-col">
<div class="list-row level"> {{ _("Full Name") }}
<div> </div>
<a {% if allow_progress %} class="clickable" href="/classes/{{ class_info.name }}/students/{{ student.username }}" {% endif %}> <div class="col grid-static-col col-xs-2 text-right">
{{ student.student_name }} {{ _("Courses Completed") }}
</a> </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> </div>
{% if is_moderator %} {% if is_moderator %}
<div type="button" class="btn-remove-student" data-student="{{ student.student }}"> <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 student.student == frappe.session.user %}
<div class="grid-row">
<div class="data-row row">
<a class="col grid-static-col {% 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.format_datetime(student.last_active, "medium") }}
</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"> <svg class="icon icon-sm">
<use href="#icon-delete"></use> <use href="#icon-delete"></use>
</svg> </svg>
@@ -218,6 +266,16 @@
{% endif %} {% endif %}
</div> </div>
<!-- <div class="progress w-25">
<div class="progress-bar" role="progressbar" aria-valuenow="{{ student.course_completion }}"
aria-valuemin="0" aria-valuemax="100" style="width:{{ student.course_completion }}%">
<span class="sr-only"> {{ student.course_completion }} {{ _("Complete") }} </span>
</div>
</div> -->
</div>
{% endfor %} {% endfor %}
</div> </div>
{% else %} {% else %}
@@ -239,13 +297,13 @@
</button> </button>
{% endif %} {% endif %}
</header> </header>
{{ CreateAssessment() }} <!-- {{ ManageAssessments() }} -->
{{ AssessmentList(assessments) }} {{ AssessmentList(assessments) }}
</article> </article>
{% endmacro %} {% endmacro %}
{% macro CreateAssessment() %} {% macro ManageAssessments() %}
{% if is_moderator %} {% if is_moderator %}
<div class="modal fade assessment-modal" id="assessment-modal" tabindex="-1" role="dialog"> <div class="modal fade assessment-modal" id="assessment-modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
@@ -279,12 +337,16 @@
<p class="field-description"> <p class="field-description">
{{ _("Select the assessments you wish to include for this class. Your selections will be automatically saved upon clicking. If you decide to remove an item from the list, simply uncheck it.") }} {{ _("Select the assessments you wish to include for this class. Your selections will be automatically saved upon clicking. If you decide to remove an item from the list, simply uncheck it.") }}
</p> </p>
<div class="flex justify-content-between"> <div class="">
{% if all_assignments | length %} {% if all_assignments | length %}
<div> <div>
<div class="field-label mb-2"> <div class="clickable flex align-center field-label mb-2" data-toggle="collapse" data-target="#assignments-list">
{{ _("Assignments") }} {{ _("Assignments") }}
<svg class="icon icon-sm ml-2">
<use class="mb-1" href="#icon-down"></use>
</svg>
</div> </div>
<div id="assignments-list" class="collapse">
{% for assignment in all_assignments %} {% for assignment in all_assignments %}
<div> <div>
<label class="vertically-center"> <label class="vertically-center">
@@ -294,12 +356,20 @@
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
<hr>
</div>
{% endif %} {% endif %}
{% if all_quizzes | length %} {% if all_quizzes | length %}
<div> <div>
<div class="field-label mb-2"> <div class="clickable flex align-center field-label mb-2" data-toggle="collapse" data-target="#quiz-list">
<div>
{{ _("Quizzes") }} {{ _("Quizzes") }}
</div> </div>
<svg class="icon icon-sm ml-2">
<use class="mb-1" href="#icon-down"></use>
</svg>
</div>
<div id="quiz-list" class="collapse">
{% for quiz in all_quizzes %} {% for quiz in all_quizzes %}
<div> <div>
<label class="vertically-center"> <label class="vertically-center">
@@ -309,6 +379,7 @@
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
</div>
{% endif %} {% endif %}
</div> </div>
@@ -317,7 +388,7 @@
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button class="btn btn-primary btn-sm btn-assessment-close" data-dismiss="modal" aria-label="Close"> <button class="btn btn-primary btn-sm btn-close" data-dismiss="modal" aria-label="Close">
{{ _("Done") }} {{ _("Done") }}
</button> </button>
</div> </div>
@@ -329,28 +400,37 @@
{% macro AssessmentList(assessments) %} {% macro AssessmentList(assessments) %}
{% if assessments | length %} {% if assessments | length %}
<div> <div class="form-grid">
<div class="list-row level level-left small"> <div class="grid-heading-row">
<div class="w-25"> <div class="grid-row">
<div class="row data-row">
<div class="col grid-static-col">
{{ _("Title") }} {{ _("Title") }}
</div> </div>
<div class=""> <div class="col grid-static-col">
{{ _("Type") }} {{ _("Type") }}
</div> </div>
</div> </div>
</div>
</div>
<div class="grid-body">
<div class="rows">
{% for assessment in assessments %} {% for assessment in assessments %}
<div class="list-row level level-left"> <div class="grid-row">
<div class="w-25"> <div class="row data-row">
<a class="clickable" href="{{ assessment.edit_url }}"> <a class="col grid-static-col clickable" href="{{ assessment.edit_url }}">
{{ assessment.title }} {{ assessment.title }}
</a> </a>
</div> <div class="col grid-static-col">
<div class="">
{{ assessment.assessment_type.split("LMS ")[1] }} {{ assessment.assessment_type.split("LMS ")[1] }}
</div> </div>
</div> </div>
</div>
{% endfor %} {% endfor %}
</div> </div>
</div>
</div>
{% else %} {% else %}
<p class="text-muted mt-3"> {{ _("No Assessments") }} </p> <p class="text-muted mt-3"> {{ _("No Assessments") }} </p>
{% endif %} {% endif %}
@@ -376,7 +456,6 @@
{% macro CreateLiveClass(class_info) %} {% macro CreateLiveClass(class_info) %}
{% if is_moderator %} {% if is_moderator %}
<div class="modal fade live-class-modal" id="live-class-modal" tabindex="-1" role="dialog"> <div class="modal fade live-class-modal" id="live-class-modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
@@ -404,7 +483,6 @@
</div> </div>
</div> </div>
{% endif %} {% endif %}
{% endmacro %} {% endmacro %}
@@ -412,7 +490,7 @@
<div class="lms-card-parent mt-5"> <div class="lms-card-parent mt-5">
{% if live_classes | length %} {% if live_classes | length %}
{% for class in live_classes %} {% for class in live_classes %}
<div class="lms-card"> <div class="common-card-style column-card">
<div class="mb-0"> <div class="mb-0">
<div class="dropdown pull-right"> <div class="dropdown pull-right">
@@ -430,10 +508,9 @@
<a class="dropdown-item small" href="{{ class.join_url }}"> {{ _("Join") }} </a> <a class="dropdown-item small" href="{{ class.join_url }}"> {{ _("Join") }} </a>
</li> </li>
{% endif %} {% endif %}
</ul> </ul>
</div> </div>
<div class="lms-card-title mb-4"> <div class="bold-heading mb-4">
{{ class.title }} {{ class.title }}
</div> </div>
</div> </div>
@@ -468,10 +545,12 @@
<script> <script>
frappe.boot.user = { frappe.boot.user = {
"can_create": [], "can_create": [],
"can_select": ["User", "LMS Category"], "can_select": ["User", "LMS Category", "LMS Assignment", "LMS Quiz"],
"can_read": ["User", "LMS Category"] "can_read": ["User", "LMS Category", "LMS Assignment", "LMS Quiz"]
}; };
frappe.boot.single_types = []
frappe.router = { frappe.router = {
slug (name) { slug (name) {
return name.toLowerCase().replace(/ /g, "-"); return name.toLowerCase().replace(/ /g, "-");

View File

@@ -32,14 +32,15 @@ frappe.ready(() => {
$("#open-assessment-modal").click((e) => { $("#open-assessment-modal").click((e) => {
e.preventDefault(); e.preventDefault();
$("#assessment-modal").modal("show"); show_assessment_modal();
/* $("#assessment-modal").modal("show"); */
}); });
$(".assessment-item").click((e) => { $(".assessment-item").click((e) => {
update_assessment(e); update_assessment(e);
}); });
$(".btn-assessment-close").click((e) => { $(".btn-close").click((e) => {
window.location.reload(); window.location.reload();
}); });
}); });
@@ -349,12 +350,6 @@ const show_course_modal = () => {
fieldname: "course", fieldname: "course",
reqd: 1, reqd: 1,
}, },
{
fieldtype: "HTML",
fieldname: "instructions",
label: __("Instructions"),
options: __("Select a course to add to this class."),
},
], ],
primary_action_label: __("Add"), primary_action_label: __("Add"),
primary_action(values) { primary_action(values) {
@@ -384,10 +379,6 @@ const show_course_modal = () => {
}, },
}); });
course_modal.show(); course_modal.show();
setTimeout(() => {
$(".modal-body").css("min-height", "200px");
$(".modal-body input").focus();
}, 1000);
}; };
const remove_course = (e) => { const remove_course = (e) => {
@@ -426,14 +417,6 @@ const show_student_modal = () => {
ignore_user_type: 1, ignore_user_type: 1,
}, },
}, },
{
fieldtype: "HTML",
fieldname: "instructions",
label: __("Instructions"),
options: __(
"Please ensure a user account exists for the student before adding them to the class. Only users can be enrolled as students."
),
},
], ],
primary_action_label: __("Add"), primary_action_label: __("Add"),
primary_action(values) { primary_action(values) {
@@ -463,20 +446,61 @@ const show_student_modal = () => {
}, },
}); });
student_modal.show(); student_modal.show();
setTimeout(() => {
$(".modal-body").css("min-height", "200px");
$(".modal-body input").focus();
}, 1000);
}; };
const update_assessment = (e) => { 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,
get_query: () => {
return {
filters: {
name: ["in", ["LMS Assignment", "LMS Quiz"]],
},
};
},
},
{
fieldtype: "Dynamic Link",
options: "assessment_type",
label: __("Assessment"),
fieldname: "assessment_name",
reqd: 1,
},
],
primary_action_label: __("Add"),
primary_action(values) {
frappe.call({ frappe.call({
method: "lms.lms.doctype.lms_class.lms_class.update_assessment", method: "frappe.client.insert",
args: { args: {
type: $(e.currentTarget).data("type"), doc: {
name: $(e.currentTarget).data("name"), doctype: "LMS Assessment",
value: $(e.currentTarget).prop("checked") ? 1 : 0, assessment_type: values.assessment_type,
class_name: $(".class-details").data("class"), assessment_name: values.assessment_name,
parenttype: "LMS Class",
parentfield: "assessments",
parent: $(".class-details").data("class"),
},
},
callback(r) {
frappe.show_alert(
{
message: __("Assessment Added"),
indicator: "green",
},
3
);
window.location.reload();
}, },
}); });
assessment_modal.hide();
},
});
assessment_modal.show();
}; };

View File

@@ -1,15 +1,18 @@
import frappe
from lms.lms.utils import has_course_moderator_role
from frappe import _ from frappe import _
import frappe
from frappe.utils import getdate from frappe.utils import getdate
from lms.www.utils import get_assessments from lms.www.utils import get_assessments
from lms.lms.utils import (
has_course_moderator_role,
get_course_progress,
has_submitted_assessment,
has_graded_assessment,
)
def get_context(context): def get_context(context):
context.no_cache = 1 context.no_cache = 1
class_name = frappe.form_dict["classname"] class_name = frappe.form_dict["classname"]
session_user = []
remaining_students = []
context.is_moderator = has_course_moderator_role() context.is_moderator = has_course_moderator_role()
context.class_info = frappe.db.get_value( context.class_info = frappe.db.get_value(
@@ -21,12 +24,12 @@ def get_context(context):
"start_date", "start_date",
"end_date", "end_date",
"description", "description",
"medium",
"custom_component", "custom_component",
"seat_count", "seat_count",
"start_time", "start_time",
"end_time", "end_time",
"category", "category",
"medium",
], ],
as_dict=True, as_dict=True,
) )
@@ -35,10 +38,10 @@ def get_context(context):
"LMS Course", {"published": 1}, ["name", "title"] "LMS Course", {"published": 1}, ["name", "title"]
) )
context.class_courses = frappe.get_all( class_courses = frappe.get_all(
"Class Course", "Class Course",
{"parent": class_name}, {"parent": class_name},
["name", "course", "title"], ["name", "course"],
order_by="creation desc", order_by="creation desc",
) )
@@ -49,20 +52,6 @@ def get_context(context):
order_by="creation desc", order_by="creation desc",
) )
for student in class_students:
if student.student == frappe.session.user:
session_user.append(student)
else:
remaining_students.append(student)
if len(session_user):
context.class_students = session_user + remaining_students
else:
context.class_students = class_students
students = [student.student for student in class_students]
context.is_student = frappe.session.user in students
context.live_classes = frappe.get_all( context.live_classes = frappe.get_all(
"LMS Live Class", "LMS Live Class",
{"class_name": class_name, "date": [">=", getdate()]}, {"class_name": class_name, "date": [">=", getdate()]},
@@ -70,7 +59,16 @@ def get_context(context):
order_by="date", order_by="date",
) )
context.class_courses = get_class_course_details(class_courses)
context.all_courses = frappe.get_list(
"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.assessments = get_assessments(class_name)
context.class_students = get_class_student_details(
class_students, class_courses, context.assessments
)
context.is_student = is_student(class_students)
context.all_assignments = get_all_assignments(class_name) context.all_assignments = get_all_assignments(class_name)
context.all_quizzes = get_all_quizzes(class_name) context.all_quizzes = get_all_quizzes(class_name)
@@ -103,3 +101,85 @@ def get_all_assignments(class_name):
} }
) )
return all_assignments 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_certificate",
"price_certificate",
"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))
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
return sort_students(class_students)
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 is_student(class_students):
students = [student.student for student in class_students]
return frappe.session.user in students

View File

@@ -93,38 +93,38 @@
{% set course_count = frappe.db.count("Class Course", {"parent": class.name}) %} {% set course_count = frappe.db.count("Class Course", {"parent": class.name}) %}
{% set student_count = frappe.db.count("Class Student", {"parent": class.name}) %} {% set student_count = frappe.db.count("Class Student", {"parent": class.name}) %}
<div class="lms-card"> <div class="common-card-style column-card">
<div class="lms-card-title mb-4"> <div class="bold-heading mb-2">
{{ class.title }} {{ class.title }}
</div> </div>
<!-- <div class="text-muted small mb-4"> <div class="mb-1">
{% if course_count %}
<span class="mr-3">
{{ course_count }} {{ _("Courses") }}
</span>
{% endif %}
{% if student_count %}
<span>
{{ student_count }} {{ _("Students") }}
</span>
{% endif %}
</div> -->
<div class="">
<svg class="icon icon-sm"> <svg class="icon icon-sm">
<use href="#icon-calendar"></use> <use href="#icon-calendar"></use>
</svg> </svg>
<span> <span>
{{ frappe.utils.format_date(class.start_date, "long") }} - {{ frappe.utils.format_date(class.start_date, "medium") }} -
</span> </span>
<span> <span>
{{ frappe.utils.format_date(class.end_date, "long") }} {{ frappe.utils.format_date(class.end_date, "medium") }}
</span> </span>
</div> </div>
<div class="mb-1">
<svg class="icon icon-md">
<use href="#icon-education"></use>
</svg>
{{ course_count }} {{ _("Courses") }}
</div>
<div class="mb-1">
<svg class="icon icon-md">
<use href="#icon-users"></use>
</svg>
{{ student_count }} {{ _("Students") }}
</div>
<a class="stretched-link" href="/classes/{{ class.name }}"></a> <a class="stretched-link" href="/classes/{{ class.name }}"></a>
</div> </div>
{% endfor %} {% endfor %}

View File

@@ -68,7 +68,6 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% for assessment in assessments %} {% for assessment in assessments %}
{% set has_access = is_moderator and assessment.submission or frappe.session.user == student.name %} {% set has_access = is_moderator and assessment.submission or frappe.session.user == student.name %}

View File

@@ -43,6 +43,7 @@ def get_courses():
"name", "name",
"upcoming", "upcoming",
"title", "title",
"short_introduction",
"image", "image",
"enable_certification", "enable_certification",
"paid_certificate", "paid_certificate",

View File

@@ -1,6 +1,6 @@
{% extends "lms/templates/lms_base.html" %} {% extends "lms/templates/lms_base.html" %}
{% block title %} {% block title %}
{{ _('Community') }} {{ _('People') }}
{% endblock %} {% endblock %}