feat: show student course and assessment progress on batch page
This commit is contained in:
@@ -110,6 +110,7 @@ const openCourseModal = () => {
|
||||
}
|
||||
|
||||
const getCoursesColumns = () => {
|
||||
console.log(courses.data)
|
||||
return [
|
||||
{
|
||||
label: 'Title',
|
||||
@@ -118,13 +119,13 @@ const getCoursesColumns = () => {
|
||||
},
|
||||
{
|
||||
label: 'Lessons',
|
||||
key: 'lesson_count',
|
||||
key: 'lessons',
|
||||
align: 'right',
|
||||
},
|
||||
{
|
||||
label: 'Enrollments',
|
||||
align: 'right',
|
||||
key: 'enrollment_count',
|
||||
key: 'enrollments',
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
@@ -20,10 +20,10 @@
|
||||
>
|
||||
<ListHeaderItem :item="item" v-for="item in getStudentColumns()">
|
||||
<template #prefix="{ item }">
|
||||
<component
|
||||
<FeatherIcon
|
||||
v-if="item.icon"
|
||||
:is="item.icon"
|
||||
class="h-4 w-4 stroke-1.5 ml-4"
|
||||
:name="item.icon"
|
||||
class="h-4 w-4 stroke-1.5"
|
||||
/>
|
||||
</template>
|
||||
</ListHeaderItem>
|
||||
@@ -42,9 +42,19 @@
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<div v-if="column.key == 'courses'">
|
||||
{{ row[column.key] }}
|
||||
</div>
|
||||
<div v-else-if="column.icon == 'book-open'">
|
||||
{{ Math.ceil(row.courses[column.key]) }}
|
||||
</div>
|
||||
<Badge
|
||||
v-else-if="column.icon == 'help-circle'"
|
||||
:theme="getStatusTheme(row.assessments[column.key])"
|
||||
class="text-xs"
|
||||
>
|
||||
{{ row.assessments[column.key] }}
|
||||
</Badge>
|
||||
</ListRowItem>
|
||||
</template>
|
||||
</ListRow>
|
||||
@@ -74,7 +84,11 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import {
|
||||
Avatar,
|
||||
Badge,
|
||||
Button,
|
||||
createResource,
|
||||
FeatherIcon,
|
||||
ListHeader,
|
||||
ListHeaderItem,
|
||||
ListSelectBanner,
|
||||
@@ -82,8 +96,6 @@ import {
|
||||
ListRows,
|
||||
ListView,
|
||||
ListRowItem,
|
||||
Avatar,
|
||||
Button,
|
||||
} from 'frappe-ui'
|
||||
import { Trash2, Plus } from 'lucide-vue-next'
|
||||
import { ref } from 'vue'
|
||||
@@ -109,27 +121,38 @@ const students = createResource({
|
||||
})
|
||||
|
||||
const getStudentColumns = () => {
|
||||
return [
|
||||
let columns = [
|
||||
{
|
||||
label: 'Full Name',
|
||||
key: 'full_name',
|
||||
width: 2,
|
||||
},
|
||||
{
|
||||
label: 'Courses Done',
|
||||
key: 'courses_completed',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
label: 'Assessments Done',
|
||||
key: 'assessments_completed',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
label: 'Last Active',
|
||||
key: 'last_active',
|
||||
width: 1,
|
||||
},
|
||||
]
|
||||
|
||||
if (students.data?.[0].courses) {
|
||||
Object.keys(students.data?.[0].courses).forEach((course) => {
|
||||
columns.push({
|
||||
label: `${course} (%)`,
|
||||
key: course,
|
||||
width: 1,
|
||||
icon: 'book-open',
|
||||
align: 'center',
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if (students.data?.[0].assessments) {
|
||||
Object.keys(students.data?.[0].assessments).forEach((assessment) => {
|
||||
columns.push({
|
||||
label: assessment,
|
||||
key: assessment,
|
||||
width: 1,
|
||||
icon: 'help-circle',
|
||||
align: 'left',
|
||||
})
|
||||
})
|
||||
}
|
||||
return columns
|
||||
}
|
||||
|
||||
const openStudentModal = () => {
|
||||
@@ -160,4 +183,14 @@ const removeStudents = (selections, unselectAll) => {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const getStatusTheme = (status) => {
|
||||
if (status === 'Pass') {
|
||||
return 'green'
|
||||
} else if (status == 'Not Graded') {
|
||||
return 'orange'
|
||||
} else {
|
||||
return 'red'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
import { Dialog, createResource } from 'frappe-ui'
|
||||
import { ref } from 'vue'
|
||||
import Link from '@/components/Controls/Link.vue'
|
||||
import { showToast } from '@/utils'
|
||||
|
||||
const students = defineModel('reloadStudents')
|
||||
const student = ref()
|
||||
@@ -61,8 +62,11 @@ const addStudent = (close) => {
|
||||
{
|
||||
onSuccess() {
|
||||
students.value.reload()
|
||||
close()
|
||||
student.value = null
|
||||
close()
|
||||
},
|
||||
onError(err) {
|
||||
showToast(__('Error'), __(err.messages?.[0] || err), 'x')
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -874,26 +874,6 @@ def is_onboarding_complete():
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
def get_evaluator(course, batch):
|
||||
evaluator = None
|
||||
evaluator = frappe.db.get_value(
|
||||
@@ -1459,13 +1439,11 @@ def get_quiz_details(assessment, member):
|
||||
@frappe.whitelist()
|
||||
def get_batch_students(batch):
|
||||
students = []
|
||||
|
||||
students_list = frappe.get_all(
|
||||
"Batch Student", filters={"parent": batch}, fields=["student", "name"]
|
||||
)
|
||||
|
||||
batch_courses = frappe.get_all("Batch Course", {"parent": batch}, pluck="course")
|
||||
|
||||
batch_courses = frappe.get_all("Batch Course", {"parent": batch}, ["course", "title"])
|
||||
assessments = frappe.get_all(
|
||||
"LMS Assessment",
|
||||
filters={"parent": batch},
|
||||
@@ -1483,29 +1461,62 @@ def get_batch_students(batch):
|
||||
)
|
||||
detail.last_active = format_datetime(detail.last_active, "dd MMM YY")
|
||||
detail.name = student.name
|
||||
students.append(detail)
|
||||
detail.courses = frappe._dict()
|
||||
detail.assessments = frappe._dict()
|
||||
|
||||
""" Iterate through courses and track their progress """
|
||||
for course in batch_courses:
|
||||
progress = frappe.db.get_value(
|
||||
"LMS Enrollment", {"course": course, "member": student.student}, "progress"
|
||||
"LMS Enrollment", {"course": course.course, "member": student.student}, "progress"
|
||||
)
|
||||
|
||||
detail.courses[course.title] = progress
|
||||
if progress == 100:
|
||||
courses_completed += 1
|
||||
|
||||
detail.courses_completed = courses_completed
|
||||
|
||||
""" Iterate through assessments and track their progress """
|
||||
for assessment in assessments:
|
||||
if has_submitted_assessment(
|
||||
title = frappe.db.get_value("LMS Assignment", assessment.assessment_name, "title")
|
||||
status = has_submitted_assessment(
|
||||
assessment.assessment_name, assessment.assessment_type, student.student
|
||||
):
|
||||
)
|
||||
detail.assessments[title] = status
|
||||
if status not in ["Not Attempted", 0]:
|
||||
assessments_completed += 1
|
||||
|
||||
detail.courses_completed = courses_completed
|
||||
detail.assessments_completed = assessments_completed
|
||||
students.append(detail)
|
||||
|
||||
return students
|
||||
|
||||
|
||||
def has_submitted_assessment(assessment, assessment_type, member=None):
|
||||
if not member:
|
||||
member = frappe.session.user
|
||||
|
||||
if assessment_type == "LMS Assignment":
|
||||
doctype = "LMS Assignment Submission"
|
||||
docfield = "assignment"
|
||||
fields = ["status"]
|
||||
not_attempted = "Not Attempted"
|
||||
elif assessment_type == "LMS Quiz":
|
||||
doctype = "LMS Quiz Submission"
|
||||
docfield = "quiz"
|
||||
fields = ["score"]
|
||||
not_attempted = 0
|
||||
|
||||
filters = {}
|
||||
filters[docfield] = assessment
|
||||
filters["member"] = member
|
||||
|
||||
attempt = frappe.db.exists(doctype, filters)
|
||||
if attempt:
|
||||
attempt_details = frappe.db.get_value(doctype, filters, fields)
|
||||
return attempt_details
|
||||
else:
|
||||
return not_attempted
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_discussion_topics(doctype, docname, single_thread):
|
||||
if single_thread:
|
||||
|
||||
Reference in New Issue
Block a user