fix: evaluator info and other styles

This commit is contained in:
Jannat Patel
2024-09-11 19:51:14 +05:30
parent 7da608ed44
commit 58369ba65e
10 changed files with 522 additions and 393 deletions

View File

@@ -4,10 +4,14 @@
{{ props.label }} {{ props.label }}
</label> </label>
<div class="flex text-center"> <div class="flex text-center">
<div v-for="index in 5" @mouseover="hoveredRating = index" @mouseleave="hoveredRating = 0"> <div
v-for="index in 5"
@mouseover="hoveredRating = index"
@mouseleave="hoveredRating = 0"
>
<Star <Star
class="h-6 w-6 fill-gray-400 text-gray-50 stroke-1 mr-1 cursor-pointer" class="fill-gray-400 text-gray-50 stroke-1 mr-1 cursor-pointer"
:class="{ 'fill-yellow-200': (index <= hoveredRating && index > rating), 'fill-yellow-500': index <= rating }" :class="iconClasses(index)"
@click="markRating(index)" @click="markRating(index)"
/> />
</div> </div>
@@ -17,7 +21,7 @@
<script setup> <script setup>
import { Star } from 'lucide-vue-next' import { Star } from 'lucide-vue-next'
import { ref, watch } from 'vue' import { computed, ref, watch } from 'vue'
const props = defineProps({ const props = defineProps({
id: { id: {
@@ -32,8 +36,29 @@ const props = defineProps({
type: String, type: String,
default: '', default: '',
}, },
size: {
type: String,
default: 'md',
},
}) })
const iconClasses = (index) => {
let classes = [
{
sm: 'size-4',
md: 'size-5',
lg: 'size-6',
xl: 'size-7',
}[props.size],
]
if (index <= hoveredRating.value && index > rating.value) {
classes.push('fill-yellow-200')
} else if (index <= rating.value) {
classes.push('fill-yellow-500')
}
return classes.join(' ')
}
const emit = defineEmits(['update:modelValue']) const emit = defineEmits(['update:modelValue'])
const rating = ref(props.modelValue) const rating = ref(props.modelValue)
const hoveredRating = ref(0) const hoveredRating = ref(0)
@@ -47,7 +72,10 @@ function markRating(index) {
rating.value = index rating.value = index
} }
watch(() => props.modelValue, (newVal) => { watch(
rating.value = newVal () => props.modelValue,
}) (newVal) => {
rating.value = newVal
}
)
</script> </script>

View File

@@ -1,309 +1,378 @@
<template> <template>
<Dialog <Dialog
v-model="show" v-model="show"
:options="{ :options="{
size: '2xl', size: '2xl',
}"> }"
<template #body> >
<div class="flex text-base"> <template #body>
<div class="flex flex-col w-1/2 p-5"> <div class="flex text-base">
<div class="text-lg font-semibold mb-4"> <div class="flex flex-col w-1/2 p-5">
{{ event.title }} <div class="text-lg font-semibold mb-4">
</div> {{ event.title }}
</div>
<div class="flex flex-col space-y-4 text-sm text-gray-800"> <div class="flex flex-col space-y-4 text-sm text-gray-800">
<Tooltip :text="__('Email ID')"> <Tooltip :text="__('Email ID')">
<div class="flex items-center space-x-2 w-fit"> <div class="flex items-center space-x-2 w-fit">
<User class="h-4 w-4 stroke-1.5" /> <User class="h-4 w-4 stroke-1.5" />
<span> <span>
{{ event.member }} {{ event.member }}
</span> </span>
</div> </div>
</Tooltip> </Tooltip>
<Tooltip :text="__('Course')"> <Tooltip :text="__('Course')">
<div class="flex items-center space-x-2 w-fit"> <div class="flex items-center space-x-2 w-fit">
<BookOpen class="h-4 w-4 stroke-1.5" /> <BookOpen class="h-4 w-4 stroke-1.5" />
<span> <span>
{{ event.course_title }} {{ event.course_title }}
</span> </span>
</div> </div>
</Tooltip> </Tooltip>
<Tooltip v-if="event.batch_title" :text="__('Batch')"> <Tooltip v-if="event.batch_title" :text="__('Batch')">
<div class="flex items-center space-x-2 w-fit"> <div class="flex items-center space-x-2 w-fit">
<Users class="h-4 w-4 stroke-1.5" /> <Users class="h-4 w-4 stroke-1.5" />
<span> <span>
{{ event.batch_title }} {{ event.batch_title }}
</span> </span>
</div> </div>
</Tooltip> </Tooltip>
<Tooltip :text="__('Date')"> <Tooltip :text="__('Date')">
<div class="flex items-center space-x-2 w-fit"> <div class="flex items-center space-x-2 w-fit">
<Calendar class="h-4 w-4 stroke-1.5" /> <Calendar class="h-4 w-4 stroke-1.5" />
<span> <span>
{{ dayjs(event.date).format("DD MMM YYYY") }} {{ dayjs(event.date).format('DD MMM YYYY') }}
</span> </span>
</div> </div>
</Tooltip> </Tooltip>
<Tooltip :text="__('Time')"> <Tooltip :text="__('Time')">
<div class="flex items-center space-x-2 w-fit"> <div class="flex items-center space-x-2 w-fit">
<Clock class="h-4 w-4 stroke-1.5" /> <Clock class="h-4 w-4 stroke-1.5" />
<span> <span>
{{ formatTime(event.start_time) }} - {{ formatTime(event.end_time) }} {{ formatTime(event.start_time) }} -
</span> {{ formatTime(event.end_time) }}
</div> </span>
</Tooltip> </div>
</div> </Tooltip>
<div class="flex items-center space-x-2 mt-auto"> </div>
<Button v-if="certificate.name" @click="openCertificate(certificate)" class="w-full"> <div class="flex items-center space-x-2 mt-auto">
<template #prefix> <Button
<FileText class="h-4 w-4 stroke-1.5" /> v-if="certificate.name"
</template> @click="openCertificate(certificate)"
{{ __("View Certificate") }} class="w-full"
</Button> >
<Button v-else @click="openCallLink(event.venue)" class="w-full"> <template #prefix>
<template #prefix> <FileText class="h-4 w-4 stroke-1.5" />
<Video class="h-4 w-4 stroke-1.5" /> </template>
</template> {{ __('View Certificate') }}
<span> </Button>
{{ __("Join Meeting") }} <Button v-else @click="openCallLink(event.venue)" class="w-full">
</span> <template #prefix>
</Button> <Video class="h-4 w-4 stroke-1.5" />
</div> </template>
</div> <span>
<Tabs :tabs='tabs' v-model="tabIndex" class="border-l w-1/2"> {{ __('Join Meeting') }}
<template #default="{ tab }"> </span>
<div v-if="tab.label == 'Evaluation'" class="flex flex-col space-y-4 p-5"> </Button>
<Rating v-model="evaluation.rating" :label="__('Rating')"/> </div>
<FormControl type="select" :options='[{ </div>
value: "Pending", <Tabs :tabs="tabs" v-model="tabIndex" class="border-l w-1/2">
label: __("Pending") <template #default="{ tab }">
}, { <div
value: "In Progress", v-if="tab.label == 'Evaluation'"
label: __("In Progress") class="flex flex-col space-y-4 p-5"
}, { >
value: "Pass", <div class="flex items-center justify-between">
label: __("Pass") <Rating v-model="evaluation.rating" :label="__('Rating')" />
}, { <FormControl
value: "Fail", type="select"
label: __("Fail") :options="statusOptions"
}]' v-model="evaluation.status"
v-model="evaluation.status" :label="__('Status')" /> :label="__('Status')"
<FormControl type="textarea" v-model="evaluation.summary" :label="__('Summary')" /> class="w-1/2"
<Button variant="solid" @click="saveEvaluation()"> />
{{ __("Save") }} </div>
</Button> <Textarea
</div> v-model="evaluation.summary"
<div v-else class="flex flex-col space-y-4 p-5"> :label="__('Summary')"
:rows="7"
<FormControl type="checkbox" v-model="certificate.published" :label="__('Published')" /> />
<Link v-model="certificate.template" :label="__('Template')" doctype="Print Format" :filters='{ <Button variant="solid" @click="saveEvaluation()">
"doc_type": "LMS Certificate" {{ __('Save') }}
}' /> </Button>
<FormControl type="date" v-model="certificate.issue_date" :label="__('Issue Date')" /> </div>
<FormControl type="date" v-model="certificate.expiry_date" :label="__('Expiry Date')" /> <div v-else class="flex flex-col space-y-4 p-5">
<Button variant="solid" @click="saveCertificate()"> <FormControl
{{ __("Save") }} type="checkbox"
</Button> v-model="certificate.published"
</div> :label="__('Published')"
</template> />
</Tabs> <Link
</div> v-model="certificate.template"
</template> :label="__('Template')"
</Dialog> doctype="Print Format"
:filters="{
doc_type: 'LMS Certificate',
}"
/>
<FormControl
type="date"
v-model="certificate.issue_date"
:label="__('Issue Date')"
/>
<FormControl
type="date"
v-model="certificate.expiry_date"
:label="__('Expiry Date')"
/>
<Button variant="solid" @click="saveCertificate()">
{{ __('Save') }}
</Button>
</div>
</template>
</Tabs>
</div>
</template>
</Dialog>
</template> </template>
<script setup> <script setup>
import { Dialog, Button, FormControl, createResource, Tabs, Tooltip } from 'frappe-ui'; import {
import { User, Calendar, Clock, Video, BookOpen, FileText, GraduationCap, Users, ClipboardList } from "lucide-vue-next" Dialog,
import { inject, reactive, watch, ref, computed } from "vue" Button,
import { formatTime, showToast } from "@/utils" FormControl,
import Rating from "@/components/Controls/Rating.vue" createResource,
import Link from "@/components/Controls/Link.vue" Tabs,
Tooltip,
Textarea,
} from 'frappe-ui'
import {
User,
Calendar,
Clock,
Video,
BookOpen,
FileText,
GraduationCap,
Users,
ClipboardList,
} from 'lucide-vue-next'
import { inject, reactive, watch, ref, computed } from 'vue'
import { formatTime, showToast } from '@/utils'
import Rating from '@/components/Controls/Rating.vue'
import Link from '@/components/Controls/Link.vue'
const show = defineModel() const show = defineModel()
const dayjs = inject("$dayjs") const dayjs = inject('$dayjs')
const tabIndex = ref(0) const tabIndex = ref(0)
const showCertification = ref(false) const showCertification = ref(false)
const props = defineProps({ const props = defineProps({
event: { event: {
type: [Object, null], type: [Object, null],
required: true, required: true,
}, },
}); })
const evaluation = reactive({}) const evaluation = reactive({})
const certificate = reactive({}) const certificate = reactive({})
const defaultTemplate = createResource({ const defaultTemplate = createResource({
url: "frappe.client.get_value", url: 'frappe.client.get_value',
makeParams(values) { makeParams(values) {
return { return {
doctype: "Property Setter", doctype: 'Property Setter',
fieldname: "value", fieldname: 'value',
filters: { filters: {
doc_type: "LMS Certificate", doc_type: 'LMS Certificate',
property: "default_print_format" property: 'default_print_format',
} },
} }
}, },
auto: true, auto: true,
onSuccess(data) { onSuccess(data) {
certificate.template = data.value certificate.template = data.value
} },
}) })
const openCallLink = (link) => { const openCallLink = (link) => {
window.open(link, "_blank") window.open(link, '_blank')
} }
const evaluationResource = createResource({ const evaluationResource = createResource({
url: "lms.lms.api.save_evaluation_details", url: 'lms.lms.api.save_evaluation_details',
makeParams(values) { makeParams(values) {
return { return {
member: props.event.member, member: props.event.member,
course: props.event.course, course: props.event.course,
batch_name: props.event.batch_name, batch_name: props.event.batch_name,
date: props.event.date, date: props.event.date,
start_time: props.event.start_time, start_time: props.event.start_time,
end_time: props.event.end_time, end_time: props.event.end_time,
status: evaluation.status, status: evaluation.status,
rating: evaluation.rating, rating: evaluation.rating,
summary: evaluation.summary, summary: evaluation.summary,
} evaluator: props.event.evaluator,
}, }
auto: false, },
onSuccess(data) { auto: false,
evaluation.name = data.name onSuccess(data) {
} evaluation.name = data.name
},
}) })
const evaluationDetails = createResource({ const evaluationDetails = createResource({
url: "frappe.client.get", url: 'frappe.client.get',
makeParams(values) { makeParams(values) {
return { return {
doctype: "LMS Certificate Evaluation", doctype: 'LMS Certificate Evaluation',
filters: { filters: {
member: props.event.member, member: props.event.member,
course: props.event.course, course: props.event.course,
} },
} }
}, },
onSuccess(data) { onSuccess(data) {
for (const key in data) { for (const key in data) {
if (key in evaluation) if (key in evaluation) evaluation[key] = data[key]
evaluation[key] = data[key] if (key == 'rating') evaluation.rating = data.rating * 5
if (key == "rating") if (evaluation.status == 'Pass') showCertification.value = true
evaluation.rating = data.rating * 5 }
if (evaluation.status == "Pass") },
showCertification.value = true auto: false,
}
},
auto: false
}) })
const saveEvaluation = () => { const saveEvaluation = () => {
evaluationResource.submit({}, { evaluationResource.submit(
onSuccess: () => { {},
if (evaluation.status == "Pass") { {
showCertification.value = true onSuccess: () => {
} else { if (evaluation.status == 'Pass') {
show.value = false showCertification.value = true
} } else {
showToast( __("Success"), __("Evaluation saved successfully"), "check") show.value = false
} }
}) showToast(__('Success'), __('Evaluation saved successfully'), 'check')
},
}
)
} }
const certificateResource = createResource({ const certificateResource = createResource({
url: "lms.lms.api.save_certificate_details", url: 'lms.lms.api.save_certificate_details',
makeParams(values) { makeParams(values) {
return { return {
member: props.event.member, member: props.event.member,
course: props.event.course, course: props.event.course,
batch_name: props.event.batch_name, batch_name: props.event.batch_name,
published: certificate.published, published: certificate.published,
issue_date: certificate.issue_date, issue_date: certificate.issue_date,
expiry_date: certificate.expiry_date, expiry_date: certificate.expiry_date,
template: certificate.template, template: certificate.template,
} evaluator: props.event.evaluator,
}, }
auto: false, },
onSuccess(data) { auto: false,
certificate.name = data onSuccess(data) {
} certificate.name = data
},
}) })
const certificateDetails = createResource({ const certificateDetails = createResource({
url: "frappe.client.get", url: 'frappe.client.get',
makeParams(values) { makeParams(values) {
return { return {
doctype: "LMS Certificate", doctype: 'LMS Certificate',
filters: { filters: {
member: props.event.member, member: props.event.member,
course: props.event.course, course: props.event.course,
} },
} }
}, },
onSuccess(data) { onSuccess(data) {
for (const key in data) { for (const key in data) {
if (key in certificate) if (key in certificate) certificate[key] = data[key]
certificate[key] = data[key] certificate.name = data.name
certificate.name = data.name showCertification.value = true
showCertification.value = true }
} },
}, onError(err) {
onError(err) { certificate.template = defaultTemplate.data.value
certificate.template = defaultTemplate.data.value },
}, auto: false,
auto: false
}) })
const saveCertificate = () => { const saveCertificate = () => {
certificateResource.submit({}, { certificateResource.submit(
onSuccess: () => { {},
showToast( __("Success"), __("Certificate saved successfully"), "check") {
} onSuccess: () => {
}) showToast(__('Success'), __('Certificate saved successfully'), 'check')
},
}
)
} }
watch(show, () => { watch(show, () => {
if (show.value) { if (show.value) {
evaluation.rating = 0 evaluation.rating = 0
evaluation.status = "Pending" evaluation.status = 'Pending'
evaluation.summary = "" evaluation.summary = ''
evaluationDetails.reload() evaluationDetails.reload()
certificate.published = true certificate.published = true
certificate.issue_date = dayjs().format("YYYY-MM-DD") certificate.issue_date = dayjs().format('YYYY-MM-DD')
certificate.expiry_date = null certificate.expiry_date = null
certificate.template = null certificate.template = null
certificate.name = null certificate.name = null
certificateDetails.reload() certificateDetails.reload()
} }
}) })
const openCertificate = (certificate) => { const openCertificate = (certificate) => {
window.open( window.open(
`/api/method/frappe.utils.print_format.download_pdf?doctype=LMS+Certificate&name=${certificate.name}&format=${encodeURIComponent(certificate.template)}` `/api/method/frappe.utils.print_format.download_pdf?doctype=LMS+Certificate&name=${
) certificate.name
}&format=${encodeURIComponent(certificate.template)}`
)
} }
const statusOptions = computed(() => {
return [
{
value: 'Pending',
label: __('Pending'),
},
{
value: 'In Progress',
label: __('In Progress'),
},
{
value: 'Pass',
label: __('Pass'),
},
{
value: 'Fail',
label: __('Fail'),
},
]
})
const tabs = computed(() => { const tabs = computed(() => {
const tabsArray = [ const tabsArray = [
{ {
label: __("Evaluation"), label: __('Evaluation'),
icon: ClipboardList, icon: ClipboardList,
}, },
] ]
if (showCertification.value) { if (showCertification.value) {
tabsArray.push({ tabsArray.push({
label: __("Certificate"), label: __('Certificate'),
icon: GraduationCap, icon: GraduationCap,
}) })
} }
return tabsArray return tabsArray
}) })
</script> </script>

View File

@@ -158,11 +158,11 @@ const setActiveTab = () => {
watchEffect(() => { watchEffect(() => {
if (activeTab.value) { if (activeTab.value) {
let route = { let route = {
"About": { name: 'ProfileAbout' }, About: { name: 'ProfileAbout' },
"Certificates": { name: 'ProfileCertificates' }, Certificates: { name: 'ProfileCertificates' },
"Roles": { name: 'ProfileRoles' }, Roles: { name: 'ProfileRoles' },
"Slots": { name: 'ProfileEvaluator' }, Slots: { name: 'ProfileEvaluator' },
"Schedule": { name: 'ProfileEvaluationSchedule' }, Schedule: { name: 'ProfileEvaluationSchedule' },
}[activeTab.value] }[activeTab.value]
router.push(route) router.push(route)
} }
@@ -186,7 +186,10 @@ const isSessionUser = () => {
const getTabButtons = () => { const getTabButtons = () => {
let buttons = [{ label: 'About' }, { label: 'Certificates' }] let buttons = [{ label: 'About' }, { label: 'Certificates' }]
if ($user.data?.is_moderator) buttons.push({ label: 'Roles' }) if ($user.data?.is_moderator) buttons.push({ label: 'Roles' })
if (isSessionUser() && ($user.data?.is_evaluator || $user.data?.is_moderator)) { if (
isSessionUser() &&
($user.data?.is_evaluator || $user.data?.is_moderator)
) {
buttons.push({ label: 'Slots' }) buttons.push({ label: 'Slots' })
buttons.push({ label: 'Schedule' }) buttons.push({ label: 'Schedule' })
} }

View File

@@ -1,52 +1,49 @@
<template> <template>
<div class="mt-7 mb-20"> <div class="mt-7 mb-20">
<div class="flex h-screen flex-col overflow-hidden"> <div class="flex h-screen flex-col overflow-hidden">
<Calendar <Calendar
v-if="evaluations.data?.length" v-if="evaluations.data?.length"
:config="{ :config="{
defaultMode: 'Month', defaultMode: 'Month',
disableModes: ['Day', 'Week'], disableModes: ['Day', 'Week'],
redundantCellHeight: 100, redundantCellHeight: 100,
enableShortcuts: false, enableShortcuts: false,
}" }"
:events="evaluations.data" :events="evaluations.data"
@click="(event) => openEvent(event)" @click="(event) => openEvent(event)"
> >
<template #header="{currentMonthYear, <template #header="{ currentMonthYear, decrement, increment }">
decrement, <div class="mb-2 flex justify-between">
increment, <span class="text-lg font-semibold">
}"> {{ currentMonthYear }}
<div class="mb-2 flex justify-between"> </span>
<span class="text-lg font-semibold"> <div class="flex gap-x-1">
{{ currentMonthYear }} <Button
</span> @click="decrement()"
<div class="flex gap-x-1"> variant="ghost"
<Button class="h-4 w-4"
@click="decrement()" icon="chevron-left"
variant="ghost" />
class="h-4 w-4" <Button
icon="chevron-left" @click="increment()"
/> variant="ghost"
<Button class="h-4 w-4"
@click="increment()" icon="chevron-right"
variant="ghost" />
class="h-4 w-4" </div>
icon="chevron-right" </div>
/> </template>
</div> </Calendar>
</div> </div>
</template> </div>
</Calendar> <Event v-model="showEvent" :event="currentEvent" />
</div>
</div>
<Event v-model="showEvent" :event="currentEvent"/>
</template> </template>
<script setup> <script setup>
import { Calendar, createListResource, Button } from "frappe-ui" import { Calendar, createListResource, Button } from 'frappe-ui'
import { inject, ref } from "vue" import { inject, ref } from 'vue'
import Event from "@/components/Modals/Event.vue" import Event from '@/components/Modals/Event.vue'
const user = inject("$user") const user = inject('$user')
const currentEvent = ref(null) const currentEvent = ref(null)
const showEvent = ref(false) const showEvent = ref(false)
@@ -58,37 +55,48 @@ const props = defineProps({
}) })
const evaluations = createListResource({ const evaluations = createListResource({
doctype: "LMS Certificate Request", doctype: 'LMS Certificate Request',
filters: { filters: {
"evaluator": user.data?.name evaluator: user.data?.name,
}, },
fields: ["name", "member_name", "member", "course", "course_title", "batch_name", "batch_title", "date", "start_time", "end_time", "google_meet_link"], fields: [
auto: true, 'name',
cache: ["schedule", user.data?.name], 'member_name',
transform(data) { 'member',
return data.map((d) => { 'course',
return { 'course_title',
title: `${d.member_name}'s Evaluation`, 'batch_name',
participant: d.member_name, 'batch_title',
id: d.name, 'evaluator',
venue: d.google_meet_link, 'evaluator_name',
fromDate: `${d.date} ${d.start_time}`, 'date',
toDate: `${d.date} ${d.end_time}`, 'start_time',
color: "green", 'end_time',
start_time: d.start_time, 'google_meet_link',
end_time: d.end_time, ],
course: d.course, auto: true,
course_title: d.course_title, orderBy: 'creation desc',
member: d.member, limit: 100,
batch_name: d.batch_name, cache: ['schedule', user.data?.name],
batch_title: d.batch_title transform(data) {
} return data.map((d) => {
}) let mappedData = Object.assign({}, d)
},
mappedData.title = `${d.member_name}'s Evaluation`
mappedData.participant = d.member_name
mappedData.id = d.name
mappedData.venue = d.google_meet_link
mappedData.fromDate = `${d.date} ${d.start_time}`
mappedData.toDate = `${d.date} ${d.end_time}`
mappedData.color = 'green'
return mappedData
})
},
}) })
const openEvent = (event) => { const openEvent = (event) => {
currentEvent.value = event.calendarEvent currentEvent.value = event.calendarEvent
showEvent.value = true showEvent.value = true
} }
</script> </script>

View File

@@ -85,8 +85,9 @@ const routes = [
{ {
name: 'ProfileEvaluationSchedule', name: 'ProfileEvaluationSchedule',
path: 'schedule', path: 'schedule',
component: () => import('@/pages/ProfileEvaluationSchedule.vue'), component: () =>
} import('@/pages/ProfileEvaluationSchedule.vue'),
},
], ],
}, },
{ {

View File

@@ -9,6 +9,7 @@ from frappe.query_builder.functions import Count
from frappe.utils import time_diff, now_datetime, get_datetime from frappe.utils import time_diff, now_datetime, get_datetime
from typing import Optional from typing import Optional
@frappe.whitelist() @frappe.whitelist()
def autosave_section(section, code): def autosave_section(section, code):
"""Saves the code edited in one of the sections.""" """Saves the code edited in one of the sections."""
@@ -614,22 +615,23 @@ def check_app_permission():
@frappe.whitelist() @frappe.whitelist()
def save_evaluation_details( def save_evaluation_details(
member: str, member,
course: str, course,
batch_name: str, batch_name,
date: str, evaluator,
start_time: str, date,
end_time: str, start_time,
status: str, end_time,
rating: int, status,
summary: str) -> None: rating,
summary,
):
""" """
Save evaluation details for a member against a course. Save evaluation details for a member against a course.
""" """
evaluation = frappe.db.exists("LMS Certificate Evaluation", { evaluation = frappe.db.exists(
"member": member, "LMS Certificate Evaluation", {"member": member, "course": course}
"course": course )
})
details = { details = {
"date": date, "date": date,
@@ -638,7 +640,7 @@ def save_evaluation_details(
"status": status, "status": status,
"rating": rating / 5, "rating": rating / 5,
"summary": summary, "summary": summary,
"batch_name": batch_name "batch_name": batch_name,
} }
if evaluation: if evaluation:
@@ -646,10 +648,13 @@ def save_evaluation_details(
return evaluation return evaluation
else: else:
doc = frappe.new_doc("LMS Certificate Evaluation") doc = frappe.new_doc("LMS Certificate Evaluation")
details.update({ details.update(
"member": member, {
"course": course, "member": member,
}) "course": course,
"evaluator": evaluator,
}
)
doc.update(details) doc.update(details)
doc.insert() doc.insert()
return doc.name return doc.name
@@ -657,28 +662,26 @@ def save_evaluation_details(
@frappe.whitelist() @frappe.whitelist()
def save_certificate_details( def save_certificate_details(
member: str, member,
course: str, course,
batch_name: str, batch_name,
evaluator,
issue_date, issue_date,
expiry_date, expiry_date,
template, template,
published=True, published=True,
) -> None: ):
""" """
Save certificate details for a member against a course. Save certificate details for a member against a course.
""" """
certificate = frappe.db.exists("LMS Certificate", { certificate = frappe.db.exists("LMS Certificate", {"member": member, "course": course})
"member": member,
"course": course
})
details = { details = {
"published": published, "published": published,
"issue_date": issue_date, "issue_date": issue_date,
"expiry_date": expiry_date, "expiry_date": expiry_date,
"template": template, "template": template,
"batch_name": batch_name "batch_name": batch_name,
} }
if certificate: if certificate:
@@ -686,10 +689,13 @@ def save_certificate_details(
return certificate return certificate
else: else:
doc = frappe.new_doc("LMS Certificate") doc = frappe.new_doc("LMS Certificate")
details.update({ details.update(
"member": member, {
"course": course, "member": member,
}) "course": course,
"evaluator": evaluator,
}
)
doc.update(details) doc.update(details)
doc.insert() doc.insert()
return doc.name return doc.name

View File

@@ -15,8 +15,10 @@
"template", "template",
"published", "published",
"section_break_scyf", "section_break_scyf",
"expiry_date", "evaluator",
"evaluator_name",
"column_break_slaw", "column_break_slaw",
"expiry_date",
"batch_name" "batch_name"
], ],
"fields": [ "fields": [
@@ -95,11 +97,24 @@
{ {
"fieldname": "column_break_slaw", "fieldname": "column_break_slaw",
"fieldtype": "Column Break" "fieldtype": "Column Break"
},
{
"fieldname": "evaluator",
"fieldtype": "Link",
"label": "Evaluator",
"options": "User"
},
{
"fetch_from": "evaluator.full_name",
"fieldname": "evaluator_name",
"fieldtype": "Data",
"label": "Evaluator Name",
"read_only": 1
} }
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"links": [], "links": [],
"modified": "2024-07-16 15:29:19.708888", "modified": "2024-09-11 11:37:20.419955",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "LMS", "module": "LMS",
"name": "LMS Certificate", "name": "LMS Certificate",

View File

@@ -119,9 +119,9 @@
{ {
"fieldname": "evaluator", "fieldname": "evaluator",
"fieldtype": "Link", "fieldtype": "Link",
"in_standard_filter": 1,
"label": "Evaluator", "label": "Evaluator",
"options": "User", "options": "User"
"reqd": 1
}, },
{ {
"fetch_from": "evaluator.full_name", "fetch_from": "evaluator.full_name",
@@ -133,7 +133,7 @@
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"links": [], "links": [],
"modified": "2024-09-10 20:17:49.908093", "modified": "2024-09-11 11:20:06.233491",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "LMS", "module": "LMS",
"name": "LMS Certificate Evaluation", "name": "LMS Certificate Evaluation",

View File

@@ -48,8 +48,7 @@
"fieldtype": "Link", "fieldtype": "Link",
"in_standard_filter": 1, "in_standard_filter": 1,
"label": "Evaluator", "label": "Evaluator",
"options": "User", "options": "User"
"read_only": 1
}, },
{ {
"fieldname": "date", "fieldname": "date",
@@ -149,7 +148,7 @@
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"links": [], "links": [],
"modified": "2024-09-10 13:13:48.282623", "modified": "2024-09-11 11:19:44.669132",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "LMS", "module": "LMS",
"name": "LMS Certificate Request", "name": "LMS Certificate Request",

View File

@@ -2,7 +2,7 @@
"name": "frappe_lms", "name": "frappe_lms",
"version": "1.0.0", "version": "1.0.0",
"description": "Easy to use, open-source, Learning Management System", "description": "Easy to use, open-source, Learning Management System",
"workspaces": [ "workspaces1": [
"frappe-ui", "frappe-ui",
"frontend" "frontend"
], ],