mirror of
https://github.com/frappe/lms.git
synced 2026-05-02 13:39:31 +03:00
chore: resolved conflicts
This commit is contained in:
@@ -70,7 +70,7 @@ const props = withDefaults(
|
||||
modelValue: string
|
||||
label?: string
|
||||
description?: string
|
||||
type: 'image' | 'video'
|
||||
type?: 'image' | 'video'
|
||||
required?: boolean
|
||||
}>(),
|
||||
{
|
||||
|
||||
@@ -285,9 +285,6 @@ const certificate = createResource({
|
||||
}&format=${encodeURIComponent(data.template)}`,
|
||||
'_blank'
|
||||
)
|
||||
capture('certificate_issued', {
|
||||
course: props.course.data.name,
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -52,14 +52,12 @@ import {
|
||||
} from 'frappe-ui'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import SettingFields from '@/components/Settings/SettingFields.vue'
|
||||
import { useTelemetry } from 'frappe-ui/frappe'
|
||||
|
||||
const show = defineModel<boolean>({ required: true, default: false })
|
||||
const paymentGateways = defineModel<any>('paymentGateways')
|
||||
const newGateway = ref(null)
|
||||
const newGatewayFields = ref([])
|
||||
const newGatewayData = ref<Record<string, any>>({})
|
||||
const { capture } = useTelemetry()
|
||||
|
||||
const props = defineProps<{
|
||||
gatewayID: string | null
|
||||
@@ -160,7 +158,6 @@ const saveNewGateway = (close: () => void) => {
|
||||
...newGatewayData.value,
|
||||
},
|
||||
}).then((data: any) => {
|
||||
capture('payment_gateway_configured')
|
||||
paymentGateways.value.reload()
|
||||
close()
|
||||
})
|
||||
|
||||
@@ -4,15 +4,30 @@
|
||||
<div class="text-lg text-ink-gray-9 font-semibold">
|
||||
{{ __('Upcoming Evaluations') }}
|
||||
</div>
|
||||
<Button
|
||||
v-if="
|
||||
upcoming_evals.data?.length != evaluationCourses.length && !forHome
|
||||
"
|
||||
@click="openEvalModal"
|
||||
>
|
||||
<Button v-if="canScheduleEvals" @click="openEvalModal">
|
||||
{{ __('Schedule Evaluation') }}
|
||||
</Button>
|
||||
</div>
|
||||
<div
|
||||
v-if="endDate && !endDateHasPassed"
|
||||
class="text-sm leading-5 bg-surface-amber-1 text-ink-amber-3 p-2 rounded-md mb-4"
|
||||
>
|
||||
{{ __('The last day to schedule your evaluations is ') }}
|
||||
<span class="font-medium">
|
||||
{{ dayjs(endDate).format('DD MMMM YYYY') }} </span
|
||||
>.
|
||||
{{ __('Please make sure to schedule your evaluation before this date.') }}
|
||||
</div>
|
||||
<div
|
||||
v-else-if="endDateHasPassed"
|
||||
class="text-sm leading-5 bg-surface-red-1 text-ink-red-3 p-2 rounded-md mb-4"
|
||||
>
|
||||
{{
|
||||
__(
|
||||
'The deadline to schedule evaluations has passed. Please contact the Instructor for assistance.'
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
<div v-if="upcoming_evals.data?.length">
|
||||
<div
|
||||
class="grid gap-4"
|
||||
@@ -99,7 +114,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-ink-gray-5">
|
||||
<div v-else-if="!endDateHasPassed" class="text-ink-gray-5">
|
||||
{{ __('Schedule an evaluation to get certified.') }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -173,6 +188,18 @@ const evaluationCourses = computed(() => {
|
||||
})
|
||||
})
|
||||
|
||||
const canScheduleEvals = computed(() => {
|
||||
return (
|
||||
upcoming_evals.data?.length != evaluationCourses.length &&
|
||||
!props.forHome &&
|
||||
!endDateHasPassed.value
|
||||
)
|
||||
})
|
||||
|
||||
const endDateHasPassed = computed(() => {
|
||||
return dayjs().isSameOrAfter(dayjs(props.endDate))
|
||||
})
|
||||
|
||||
const cancelEvaluation = (evl) => {
|
||||
$dialog({
|
||||
title: __('Cancel this evaluation?'),
|
||||
|
||||
@@ -144,20 +144,7 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="batch.data.evaluation_end_date && isStudent"
|
||||
class="text-sm leading-5 bg-surface-amber-1 text-ink-amber-3 p-2 rounded-md mb-10"
|
||||
>
|
||||
{{ __('The last day to schedule your evaluations is ') }}
|
||||
<span class="font-medium">
|
||||
{{
|
||||
dayjs(batch.data.evaluation_end_date).format('DD MMMM YYYY')
|
||||
}} </span
|
||||
>.
|
||||
{{
|
||||
__('Please make sure to schedule your evaluation before this date.')
|
||||
}}
|
||||
</div>
|
||||
|
||||
<div v-if="dayjs().isSameOrAfter(dayjs(batch.data.start_date))">
|
||||
<div class="text-ink-gray-7 font-semibold mb-2">
|
||||
{{ __('Feedback') }}
|
||||
|
||||
@@ -281,21 +281,13 @@ import {
|
||||
import {
|
||||
Breadcrumbs,
|
||||
FormControl,
|
||||
FileUploader,
|
||||
Button,
|
||||
TextEditor,
|
||||
createResource,
|
||||
usePageMeta,
|
||||
toast,
|
||||
call,
|
||||
Toast,
|
||||
} from 'frappe-ui'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { Image, Trash2 } from 'lucide-vue-next'
|
||||
import { useOnboarding, useTelemetry } from 'frappe-ui/frappe'
|
||||
import { sessionStore } from '../stores/session'
|
||||
import MultiSelect from '@/components/Controls/MultiSelect.vue'
|
||||
import Link from '@/components/Controls/Link.vue'
|
||||
import {
|
||||
escapeHTML,
|
||||
getMetaInfo,
|
||||
@@ -303,7 +295,13 @@ import {
|
||||
sanitizeHTML,
|
||||
updateMetaInfo,
|
||||
} from '@/utils'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { Trash2 } from 'lucide-vue-next'
|
||||
import { useOnboarding, useTelemetry } from 'frappe-ui/frappe'
|
||||
import { sessionStore } from '../stores/session'
|
||||
import Uploader from '@/components/Controls/Uploader.vue'
|
||||
import MultiSelect from '@/components/Controls/MultiSelect.vue'
|
||||
import Link from '@/components/Controls/Link.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const user = inject('$user')
|
||||
@@ -466,6 +464,7 @@ const validateFields = () => {
|
||||
!['description', 'batch_details'].includes(key) &&
|
||||
typeof batch[key] === 'string'
|
||||
) {
|
||||
console.log(key)
|
||||
batch[key] = escapeHTML(batch[key])
|
||||
}
|
||||
})
|
||||
|
||||
@@ -75,58 +75,11 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-5">
|
||||
<div class="mb-4">
|
||||
<div class="text-xs text-ink-gray-5 mb-2">
|
||||
{{ __('Course Image') }}
|
||||
</div>
|
||||
<FileUploader
|
||||
v-if="!course.course_image"
|
||||
:fileTypes="['image/*']"
|
||||
:validateFile="validateFile"
|
||||
@success="(file) => saveImage(file)"
|
||||
>
|
||||
<template
|
||||
v-slot="{ file, progress, uploading, openFileSelector }"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<div
|
||||
class="border rounded-md w-fit py-5 px-20 cursor-pointer"
|
||||
@click="openFileSelector"
|
||||
>
|
||||
<Image class="size-5 stroke-1 text-ink-gray-7" />
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<Button @click="openFileSelector">
|
||||
{{ __('Upload') }}
|
||||
</Button>
|
||||
<div class="mt-1 text-ink-gray-5 text-sm leading-5">
|
||||
{{
|
||||
__('Appears on the course card in the course list')
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</FileUploader>
|
||||
<div v-else class="mb-4">
|
||||
<div class="flex items-center">
|
||||
<img
|
||||
:src="course.course_image.file_url"
|
||||
class="border rounded-md w-40"
|
||||
/>
|
||||
<div class="ml-4">
|
||||
<Button @click="removeImage()">
|
||||
{{ __('Remove') }}
|
||||
</Button>
|
||||
<div class="mt-2 text-ink-gray-5 text-sm">
|
||||
{{
|
||||
__('Appears on the course card in the course list')
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Uploader
|
||||
v-model="course.image"
|
||||
:label="__('Course Image')"
|
||||
:required="false"
|
||||
/>
|
||||
|
||||
<ColorSwatches
|
||||
v-model="course.card_gradient"
|
||||
@@ -333,7 +286,6 @@ import {
|
||||
Button,
|
||||
createResource,
|
||||
FormControl,
|
||||
FileUploader,
|
||||
usePageMeta,
|
||||
toast,
|
||||
} from 'frappe-ui'
|
||||
@@ -347,22 +299,22 @@ import {
|
||||
watch,
|
||||
getCurrentInstance,
|
||||
} from 'vue'
|
||||
import { Image, Trash2, X } from 'lucide-vue-next'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useOnboarding, useTelemetry } from 'frappe-ui/frappe'
|
||||
import { sessionStore } from '../stores/session'
|
||||
import {
|
||||
escapeHTML,
|
||||
getMetaInfo,
|
||||
openSettings,
|
||||
sanitizeHTML,
|
||||
updateMetaInfo,
|
||||
validateFile,
|
||||
} from '@/utils'
|
||||
import { Trash2, X } from 'lucide-vue-next'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useOnboarding, useTelemetry } from 'frappe-ui/frappe'
|
||||
import { sessionStore } from '../stores/session'
|
||||
import Link from '@/components/Controls/Link.vue'
|
||||
import CourseOutline from '@/components/CourseOutline.vue'
|
||||
import MultiSelect from '@/components/Controls/MultiSelect.vue'
|
||||
import ColorSwatches from '@/components/Controls/ColorSwatches.vue'
|
||||
import Uploader from '@/components/Controls/Uploader.vue'
|
||||
|
||||
const user = inject('$user')
|
||||
const newTag = ref('')
|
||||
@@ -386,7 +338,7 @@ const course = reactive({
|
||||
short_introduction: '',
|
||||
description: '',
|
||||
video_link: '',
|
||||
course_image: null,
|
||||
image: null,
|
||||
card_gradient: '',
|
||||
tags: '',
|
||||
category: '',
|
||||
@@ -448,7 +400,7 @@ const courseCreationResource = createResource({
|
||||
return {
|
||||
doc: {
|
||||
doctype: 'LMS Course',
|
||||
image: course.course_image?.file_url || '',
|
||||
image: course.image,
|
||||
instructors: instructors.value.map((instructor) => ({
|
||||
instructor: instructor,
|
||||
})),
|
||||
@@ -469,7 +421,7 @@ const courseEditResource = createResource({
|
||||
doctype: 'LMS Course',
|
||||
name: values.course,
|
||||
fieldname: {
|
||||
image: course.course_image?.file_url || '',
|
||||
image: course.image,
|
||||
instructors: instructors.value.map((instructor) => ({
|
||||
instructor: instructor,
|
||||
})),
|
||||
@@ -519,24 +471,10 @@ const courseResource = createResource({
|
||||
course[key] = course[key] ? true : false
|
||||
}
|
||||
|
||||
if (data.image) imageResource.reload({ image: data.image })
|
||||
check_permission()
|
||||
},
|
||||
})
|
||||
|
||||
const imageResource = createResource({
|
||||
url: 'lms.lms.api.get_file_info',
|
||||
makeParams(values) {
|
||||
return {
|
||||
file_url: values.image,
|
||||
}
|
||||
},
|
||||
auto: false,
|
||||
onSuccess(data) {
|
||||
course.course_image = data
|
||||
},
|
||||
})
|
||||
|
||||
const validateFields = () => {
|
||||
course.description = sanitizeHTML(course.description)
|
||||
|
||||
@@ -580,16 +518,12 @@ const createCourse = () => {
|
||||
}
|
||||
|
||||
const editCourse = () => {
|
||||
let was_published = courseResource.data.published
|
||||
courseEditResource.submit(
|
||||
{
|
||||
course: courseResource.data.name,
|
||||
},
|
||||
{
|
||||
onSuccess() {
|
||||
if (!was_published && course.published) {
|
||||
capture('publish_course')
|
||||
}
|
||||
updateMetaInfo('courses', props.courseName, meta)
|
||||
toast.success(__('Course updated successfully'))
|
||||
},
|
||||
@@ -657,14 +591,6 @@ const removeTag = (tag) => {
|
||||
newTag.value = ''
|
||||
}
|
||||
|
||||
const saveImage = (file) => {
|
||||
course.course_image = file
|
||||
}
|
||||
|
||||
const removeImage = () => {
|
||||
course.course_image = null
|
||||
}
|
||||
|
||||
const check_permission = () => {
|
||||
let user_is_instructor = false
|
||||
if (user.data?.is_moderator) return
|
||||
|
||||
@@ -83,47 +83,11 @@
|
||||
class="mb-4"
|
||||
:required="true"
|
||||
/>
|
||||
<label class="block text-ink-gray-5 text-xs mb-1 mt-4">
|
||||
{{ __('Company Logo') }}
|
||||
<span class="text-ink-red-3">*</span>
|
||||
</label>
|
||||
<FileUploader
|
||||
v-if="!job.image"
|
||||
:fileTypes="['image/*']"
|
||||
:validateFile="validateFile"
|
||||
@success="(file) => saveImage(file)"
|
||||
>
|
||||
<template
|
||||
v-slot="{ file, progress, uploading, openFileSelector }"
|
||||
>
|
||||
<div class="mb-4">
|
||||
<Button @click="openFileSelector" :loading="uploading">
|
||||
{{
|
||||
uploading ? `Uploading ${progress}%` : 'Upload an image'
|
||||
}}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</FileUploader>
|
||||
<div v-else class="">
|
||||
<div class="flex items-center">
|
||||
<div class="border rounded-md p-2 mr-2">
|
||||
<FileText class="h-5 w-5 stroke-1.5 text-ink-gray-7" />
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span>
|
||||
{{ job.image.file_name }}
|
||||
</span>
|
||||
<span class="text-sm text-ink-gray-4 mt-1">
|
||||
{{ getFileSize(job.image.file_size) }}
|
||||
</span>
|
||||
</div>
|
||||
<X
|
||||
@click="removeImage()"
|
||||
class="bg-surface-gray-3 rounded-md cursor-pointer stroke-1.5 w-5 h-5 p-1 ml-4"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Uploader
|
||||
v-model="job.company_logo"
|
||||
:label="__('Company Logo')"
|
||||
:required="false"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -150,15 +114,14 @@ import {
|
||||
createResource,
|
||||
Button,
|
||||
TextEditor,
|
||||
FileUploader,
|
||||
usePageMeta,
|
||||
toast,
|
||||
} from 'frappe-ui'
|
||||
import { computed, onMounted, reactive, inject } from 'vue'
|
||||
import { FileText, X } from 'lucide-vue-next'
|
||||
import { sessionStore } from '@/stores/session'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { escapeHTML, getFileSize, sanitizeHTML, validateFile } from '@/utils'
|
||||
import { escapeHTML, sanitizeHTML } from '@/utils'
|
||||
import Uploader from '@/components/Controls/Uploader.vue'
|
||||
|
||||
const user = inject('$user')
|
||||
const router = useRouter()
|
||||
@@ -177,7 +140,7 @@ const newJob = createResource({
|
||||
return {
|
||||
doc: {
|
||||
doctype: 'Job Opportunity',
|
||||
company_logo: job.image?.file_url,
|
||||
company_logo: job.company_logo,
|
||||
...job,
|
||||
},
|
||||
}
|
||||
@@ -191,7 +154,7 @@ const updateJob = createResource({
|
||||
doctype: 'Job Opportunity',
|
||||
name: props.jobName,
|
||||
fieldname: {
|
||||
company_logo: job.image.file_url,
|
||||
company_logo: job.company_logo,
|
||||
...job,
|
||||
},
|
||||
}
|
||||
@@ -215,20 +178,6 @@ const jobDetail = createResource({
|
||||
Object.keys(data).forEach((key) => {
|
||||
if (Object.hasOwn(job, key)) job[key] = data[key]
|
||||
})
|
||||
if (data.company_logo) imageResource.reload({ image: data.company_logo })
|
||||
},
|
||||
})
|
||||
|
||||
const imageResource = createResource({
|
||||
url: 'lms.lms.api.get_file_info',
|
||||
makeParams(values) {
|
||||
return {
|
||||
file_url: values.image,
|
||||
}
|
||||
},
|
||||
auto: false,
|
||||
onSuccess(data) {
|
||||
job.image = data
|
||||
},
|
||||
})
|
||||
|
||||
@@ -241,7 +190,7 @@ const job = reactive({
|
||||
status: 'Open',
|
||||
company_name: '',
|
||||
company_website: '',
|
||||
image: null,
|
||||
company_logo: null,
|
||||
description: '',
|
||||
company_email_address: '',
|
||||
})
|
||||
@@ -322,14 +271,6 @@ const validateJobFields = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const saveImage = (file) => {
|
||||
job.image = file
|
||||
}
|
||||
|
||||
const removeImage = () => {
|
||||
job.image = null
|
||||
}
|
||||
|
||||
const jobTypes = computed(() => {
|
||||
return [
|
||||
{ label: 'Full Time', value: 'Full Time' },
|
||||
|
||||
@@ -665,6 +665,15 @@ iframe {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.codex-editor--narrow .ce-toolbox .ce-popover {
|
||||
right: unset;
|
||||
left: initial;
|
||||
}
|
||||
|
||||
.ce-popover {
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.cdx-search-field {
|
||||
border: none;
|
||||
}
|
||||
|
||||
@@ -233,7 +233,6 @@ import { ClipboardList, ListChecks, Plus, Trash2 } from 'lucide-vue-next'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { escapeHTML } from '@/utils'
|
||||
import Question from '@/components/Modals/Question.vue'
|
||||
import { useTelemetry } from 'frappe-ui/frappe'
|
||||
|
||||
const { brand } = sessionStore()
|
||||
const showQuestionModal = ref(false)
|
||||
@@ -242,7 +241,6 @@ const currentQuestion = reactive({
|
||||
marks: 0,
|
||||
name: '',
|
||||
})
|
||||
const { capture } = useTelemetry()
|
||||
const user = inject('$user')
|
||||
const router = useRouter()
|
||||
const readOnlyMode = window.read_only_mode
|
||||
@@ -260,9 +258,7 @@ onMounted(() => {
|
||||
if (!user.data?.is_moderator && !user.data?.is_instructor) {
|
||||
router.push({ name: 'Courses' })
|
||||
}
|
||||
if (props.quizID !== 'new') {
|
||||
quizDetails.reload()
|
||||
}
|
||||
quizDetails.reload()
|
||||
window.addEventListener('keydown', keyboardShortcut)
|
||||
})
|
||||
|
||||
@@ -310,9 +306,6 @@ const submitQuiz = () => {
|
||||
},
|
||||
{
|
||||
onSuccess(data) {
|
||||
if (props.quizID === 'new') {
|
||||
capture('quiz_created')
|
||||
}
|
||||
quizDetails.doc.total_marks = data.total_marks
|
||||
toast.success(__('Quiz updated successfully'))
|
||||
},
|
||||
@@ -408,7 +401,7 @@ const breadcrumbs = computed(() => {
|
||||
]
|
||||
|
||||
crumbs.push({
|
||||
label: props.quizID == 'new' ? __('New Quiz') : quizDetails.doc?.title,
|
||||
label: quizDetails.doc?.title,
|
||||
route: { name: 'QuizForm', params: { quizID: props.quizID } },
|
||||
})
|
||||
return crumbs
|
||||
@@ -416,7 +409,7 @@ const breadcrumbs = computed(() => {
|
||||
|
||||
usePageMeta(() => {
|
||||
return {
|
||||
title: props.quizID == 'new' ? __('New Quiz') : quizDetails.doc?.title,
|
||||
title: quizDetails.doc?.title,
|
||||
icon: brand.favicon,
|
||||
}
|
||||
})
|
||||
|
||||
+3
-3
@@ -1896,9 +1896,9 @@
|
||||
integrity sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==
|
||||
|
||||
"@types/node@*":
|
||||
version "25.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-25.0.7.tgz#afd2f0d34358631a287c6726dabe0c845abc2bae"
|
||||
integrity sha512-C/er7DlIZgRJO7WtTdYovjIFzGsz0I95UlMyR9anTb4aCpBSRWe5Jc1/RvLKUfzmOxHPGjSE5+63HgLtndxU4w==
|
||||
version "25.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-25.0.8.tgz#e54e00f94fe1db2497b3e42d292b8376a2678c8d"
|
||||
integrity sha512-powIePYMmC3ibL0UJ2i2s0WIbq6cg6UyVFQxSCpaPxxzAaziRfimGivjdF943sSGV6RADVbk0Nvlm5P/FB44Zg==
|
||||
dependencies:
|
||||
undici-types "~7.16.0"
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2025-12-04 12:53:38.246250",
|
||||
"modified": "2026-01-14 08:53:16.672825",
|
||||
"modified_by": "sayali@frappe.io",
|
||||
"module": "LMS",
|
||||
"name": "LMS Batch Enrollment",
|
||||
@@ -108,6 +108,18 @@
|
||||
"if_owner": 1,
|
||||
"read": 1,
|
||||
"role": "LMS Student"
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Batch Evaluator",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"row_format": "Dynamic",
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
"grid_page_length": 50,
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2025-05-21 15:58:51.667270",
|
||||
"modified": "2026-01-14 08:53:38.088168",
|
||||
"modified_by": "sayali@frappe.io",
|
||||
"module": "LMS",
|
||||
"name": "LMS Batch Feedback",
|
||||
@@ -105,6 +105,30 @@
|
||||
"role": "LMS Student",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Moderator",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Batch Evaluator",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"row_format": "Dynamic",
|
||||
|
||||
@@ -177,7 +177,7 @@
|
||||
"link_fieldname": "live_class"
|
||||
}
|
||||
],
|
||||
"modified": "2025-05-27 14:44:35.679712",
|
||||
"modified": "2026-01-14 08:54:07.684781",
|
||||
"modified_by": "sayali@frappe.io",
|
||||
"module": "LMS",
|
||||
"name": "LMS Live Class",
|
||||
@@ -215,6 +215,18 @@
|
||||
"report": 1,
|
||||
"role": "LMS Student",
|
||||
"share": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Batch Evaluator",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"row_format": "Dynamic",
|
||||
|
||||
@@ -155,11 +155,17 @@ def create_attendance(live_class, data):
|
||||
doc = frappe.new_doc("LMS Live Class Participant")
|
||||
doc.live_class = live_class.name
|
||||
doc.member = participant.get("user_email")
|
||||
doc.joined_at = participant.get("join_time")
|
||||
doc.left_at = participant.get("leave_time")
|
||||
doc.duration = participant.get("duration")
|
||||
doc.joined_at = get_datetime(participant.get("join_time"))
|
||||
doc.left_at = get_datetime(participant.get("leave_time"))
|
||||
doc.duration = get_minutes(participant.get("duration"))
|
||||
doc.insert()
|
||||
|
||||
|
||||
def update_attendees_count(live_class, data):
|
||||
frappe.db.set_value("LMS Live Class", live_class.name, "attendees", len(data))
|
||||
|
||||
|
||||
def get_minutes(duration_in_seconds):
|
||||
if duration_in_seconds:
|
||||
return int(duration_in_seconds) // 60
|
||||
return 0
|
||||
|
||||
@@ -89,8 +89,8 @@
|
||||
"grid_page_length": 50,
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2025-05-27 22:32:24.196643",
|
||||
"modified_by": "Administrator",
|
||||
"modified": "2026-01-14 08:54:21.421321",
|
||||
"modified_by": "sayali@frappe.io",
|
||||
"module": "LMS",
|
||||
"name": "LMS Live Class Participant",
|
||||
"owner": "Administrator",
|
||||
@@ -106,6 +106,30 @@
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Moderator",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Batch Evaluator",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"row_format": "Dynamic",
|
||||
|
||||
+1
-1
@@ -1293,7 +1293,7 @@ def get_assignment_details(assessment, member):
|
||||
|
||||
assessment.edit_url = f"/assignments/{assessment.assessment_name}"
|
||||
submission_name = existing_submission if existing_submission else "new-submission"
|
||||
assessment.url = f"/assignment-submission/{assessment.assessment_name}/{submission_name}"
|
||||
assessment.url = f"/lms/assignment-submission/{assessment.assessment_name}/{submission_name}"
|
||||
|
||||
return assessment
|
||||
|
||||
|
||||
+1
-1
@@ -66,4 +66,4 @@ indent-style = "tab"
|
||||
docstring-code-format = true
|
||||
|
||||
[tool.bench.frappe-dependencies]
|
||||
frappe = ">=15.0.0,<17.0.0"
|
||||
frappe = ">=15.0.0,<=17.0.0-dev"
|
||||
Reference in New Issue
Block a user