chore: linters

This commit is contained in:
Jannat Patel
2025-07-03 13:09:45 +05:30
parent 85da4f6d85
commit 991ebe09a2
3 changed files with 245 additions and 180 deletions

View File

@@ -88,10 +88,15 @@
</template> </template>
{{ __('Get Certificate') }} {{ __('Get Certificate') }}
</Button> </Button>
<Button v-if="user.data?.is_moderator || is_instructor()" class="w-full mt-2" size="md" @click="showProgressSummary"> <Button
v-if="user.data?.is_moderator || is_instructor()"
class="w-full mt-2"
size="md"
@click="showProgressSummary"
>
<template #prefix> <template #prefix>
<TrendingUp class="size-4 stroke-1.5" /> <TrendingUp class="size-4 stroke-1.5" />
{{ __("Progress Summary") }} {{ __('Progress Summary') }}
</template> </template>
</Button> </Button>
<router-link <router-link
@@ -170,7 +175,16 @@
/> />
</template> </template>
<script setup> <script setup>
import { BookOpen, BookText, CreditCard, GraduationCap, Pencil, Star, TrendingUp, Users } from 'lucide-vue-next' import {
BookOpen,
BookText,
CreditCard,
GraduationCap,
Pencil,
Star,
TrendingUp,
Users,
} from 'lucide-vue-next'
import { computed, inject, ref } from 'vue' import { computed, inject, ref } from 'vue'
import { Badge, Button, call, createResource, toast } from 'frappe-ui' import { Badge, Button, call, createResource, toast } from 'frappe-ui'
import { formatAmount } from '@/utils/' import { formatAmount } from '@/utils/'

View File

@@ -8,11 +8,10 @@
> >
<template #body-content> <template #body-content>
<div class="flex justify-between space-x-10 text-base"> <div class="flex justify-between space-x-10 text-base">
<div class="w-full">
<div class="w-full ">
<div class="flex items-center justify-between space-x-5 mb-4"> <div class="flex items-center justify-between space-x-5 mb-4">
<div class="text-xl font-semibold text-ink-gray-6"> <div class="text-xl font-semibold text-ink-gray-6">
{{ __("{0} Members").format(memberCount) }} {{ __('{0} Members').format(memberCount) }}
</div> </div>
<FormControl <FormControl
v-model="searchFilter" v-model="searchFilter"
@@ -41,18 +40,26 @@
:key="item.key" :key="item.key"
> >
<template #prefix="{ item }"> <template #prefix="{ item }">
<FeatherIcon :name="item.icon?.toString()" class="h-4 w-4" /> <FeatherIcon
:name="item.icon?.toString()"
class="h-4 w-4"
/>
</template> </template>
</ListHeaderItem> </ListHeaderItem>
</ListHeader> </ListHeader>
<ListRows v-for="row in progressList.data"> <ListRows v-for="row in progressList.data">
<router-link :to="{ <router-link
:to="{
name: 'Profile', name: 'Profile',
params: { username: row.member_username }, params: { username: row.member_username },
}"> }"
>
<ListRow :row="row"> <ListRow :row="row">
<template #default="{ column, item }"> <template #default="{ column, item }">
<ListRowItem :item="row[column.key]" :align="column.align"> <ListRowItem
:item="row[column.key]"
:align="column.align"
>
<template #prefix> <template #prefix>
<div v-if="column.key == 'member_name'"> <div v-if="column.key == 'member_name'">
<Avatar <Avatar
@@ -96,7 +103,13 @@
title: __('Progress Distribution'), title: __('Progress Distribution'),
categoryColumn: 'category', categoryColumn: 'category',
valueColumn: 'count', valueColumn: 'count',
colors: [theme.colors.red['400'], theme.colors.amber['400'], theme.colors.pink['400'], theme.colors.blue['400'], theme.colors.green['400']], colors: [
theme.colors.red['400'],
theme.colors.amber['400'],
theme.colors.pink['400'],
theme.colors.blue['400'],
theme.colors.green['400'],
],
}" }"
/> />
</div> </div>
@@ -105,22 +118,38 @@
</Dialog> </Dialog>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Avatar, Button, createListResource, createResource, Dialog, DonutChart, FeatherIcon, FormControl, ListView, ListHeader, ListHeaderItem, ListRows, ListRow, ListRowItem, NumberChart } from 'frappe-ui'; import {
import { computed, ref, watch } from 'vue'; Avatar,
Button,
createListResource,
createResource,
Dialog,
DonutChart,
FeatherIcon,
FormControl,
ListView,
ListHeader,
ListHeaderItem,
ListRows,
ListRow,
ListRowItem,
NumberChart,
} from 'frappe-ui'
import { computed, ref, watch } from 'vue'
import { theme } from '@/utils/theme' import { theme } from '@/utils/theme'
const show = defineModel<boolean | undefined>() const show = defineModel<boolean | undefined>()
const searchFilter = ref<string | null>(null); const searchFilter = ref<string | null>(null)
const props = defineProps<{ const props = defineProps<{
courseName?: string; courseName?: string
enrollments?: number; enrollments?: number
}>(); }>()
const memberCount = ref<number>(props.enrollments || 0); const memberCount = ref<number>(props.enrollments || 0)
const chartDetails = createResource({ const chartDetails = createResource({
url: "lms.lms.api.get_course_progress_distribution", url: 'lms.lms.api.get_course_progress_distribution',
params: { params: {
course: props.courseName, course: props.courseName,
}, },
@@ -128,39 +157,49 @@ const chartDetails = createResource({
}) })
const progressList = createListResource({ const progressList = createListResource({
doctype: "LMS Enrollment", doctype: 'LMS Enrollment',
filters: { filters: {
course: props.courseName, course: props.courseName,
}, },
fields: ["name", "member", "member_name", "member_image", "member_username", "progress"], fields: [
'name',
'member',
'member_name',
'member_image',
'member_username',
'progress',
],
pageLength: 50, pageLength: 50,
auto: true, auto: true,
}) })
watch([searchFilter], () => { watch([searchFilter], () => {
let filterApplied = false; let filterApplied = false
type Filters = { type Filters = {
course: string | undefined; course: string | undefined
member_name?: string[]; member_name?: string[]
} }
let filters: Filters = { let filters: Filters = {
course: props.courseName, course: props.courseName,
} }
if (searchFilter.value) { if (searchFilter.value) {
filters.member_name = ["like", `%${searchFilter.value}%`]; filters.member_name = ['like', `%${searchFilter.value}%`]
filterApplied = true; filterApplied = true
} }
progressList.update({ progressList.update({
filters: filters, filters: filters,
}) })
progressList.reload({}, { progressList.reload(
{},
{
onSuccess(data: any[]) { onSuccess(data: any[]) {
memberCount.value = filterApplied ? data.length : props.enrollments || 0; memberCount.value = filterApplied ? data.length : props.enrollments || 0
},
} }
}); )
}); })
const progressColumns = computed(() => { const progressColumns = computed(() => {
return [ return [
@@ -176,7 +215,7 @@ const progressColumns = computed(() => {
width: '30%', width: '30%',
align: 'right', align: 'right',
icon: 'trending-up', icon: 'trending-up',
} },
] ]
}) })
</script> </script>

View File

@@ -1593,11 +1593,16 @@ def track_new_watch_time(lesson, video):
doc.member = frappe.session.user doc.member = frappe.session.user
doc.save() doc.save()
@frappe.whitelist() @frappe.whitelist()
def get_course_progress_distribution(course): def get_course_progress_distribution(course):
all_progress = frappe.get_all("LMS Enrollment", { all_progress = frappe.get_all(
"LMS Enrollment",
{
"course": course, "course": course,
}, pluck="progress") },
pluck="progress",
)
average_progress = get_average_course_progress(all_progress) average_progress = get_average_course_progress(all_progress)
progress_distribution = get_progress_distribution(all_progress) progress_distribution = get_progress_distribution(all_progress)
@@ -1614,22 +1619,29 @@ def get_average_course_progress(progress_list):
average_progress = sum(progress_list) / len(progress_list) average_progress = sum(progress_list) / len(progress_list)
return flt(average_progress, frappe.get_system_settings("float_precision") or 3) return flt(average_progress, frappe.get_system_settings("float_precision") or 3)
def get_progress_distribution(progressList): def get_progress_distribution(progressList):
distribution = [{ distribution = [
{
"category": "0-20%", "category": "0-20%",
"count": len([p for p in progressList if 0 <= p < 20]), "count": len([p for p in progressList if 0 <= p < 20]),
}, { },
{
"category": "20-40%", "category": "20-40%",
"count": len([p for p in progressList if 20 <= p < 40]), "count": len([p for p in progressList if 20 <= p < 40]),
}, { },
{
"category": "40-60%", "category": "40-60%",
"count": len([p for p in progressList if 40 <= p < 60]), "count": len([p for p in progressList if 40 <= p < 60]),
}, { },
{
"category": "60-80%", "category": "60-80%",
"count": len([p for p in progressList if 60 <= p < 80]), "count": len([p for p in progressList if 60 <= p < 80]),
}, { },
{
"category": "80-100%", "category": "80-100%",
"count": len([p for p in progressList if 80 <= p <= 100]), "count": len([p for p in progressList if 80 <= p <= 100]),
}] },
]
return distribution return distribution