Merge pull request #1151 from frappe/develop
chore: merge 'develop' into 'main'
This commit is contained in:
@@ -99,6 +99,7 @@ import { getSidebarLinks } from '../utils'
|
||||
import { usersStore } from '@/stores/user'
|
||||
import { sessionStore } from '@/stores/session'
|
||||
import { useSidebar } from '@/stores/sidebar'
|
||||
import { useSettings } from '@/stores/settings'
|
||||
import { ChevronRight, Plus } from 'lucide-vue-next'
|
||||
import { createResource, Button } from 'frappe-ui'
|
||||
import PageModal from '@/components/Modals/PageModal.vue'
|
||||
@@ -114,6 +115,7 @@ const isModerator = ref(false)
|
||||
const isInstructor = ref(false)
|
||||
const pageToEdit = ref(null)
|
||||
const showWebPages = ref(false)
|
||||
const settingsStore = useSettings()
|
||||
|
||||
onMounted(() => {
|
||||
socket.on('publish_lms_notifications', (data) => {
|
||||
@@ -183,6 +185,28 @@ const addQuizzes = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const addPrograms = () => {
|
||||
if (settingsStore.learningPaths.data) {
|
||||
let activeFor = ['Programs', 'ProgramForm']
|
||||
let index = 1
|
||||
if (!isInstructor.value && !isModerator.value) {
|
||||
sidebarLinks.value = sidebarLinks.value.filter(
|
||||
(link) => link.label !== 'Courses'
|
||||
)
|
||||
activeFor.push('CourseDetail')
|
||||
activeFor.push('Lesson')
|
||||
index = 0
|
||||
}
|
||||
|
||||
sidebarLinks.value.splice(index, 0, {
|
||||
label: 'Programs',
|
||||
icon: 'Route',
|
||||
to: 'Programs',
|
||||
activeFor: activeFor,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const openPageModal = (link) => {
|
||||
showPageModal.value = true
|
||||
pageToEdit.value = link
|
||||
@@ -215,6 +239,7 @@ watch(userResource, () => {
|
||||
isModerator.value = userResource.data.is_moderator
|
||||
isInstructor.value = userResource.data.is_instructor
|
||||
addQuizzes()
|
||||
addPrograms()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
{{ option.label }}
|
||||
</div>
|
||||
<div
|
||||
v-if="option.label != option.description"
|
||||
v-if="option.description"
|
||||
class="text-xs text-gray-700"
|
||||
v-html="option.description"
|
||||
></div>
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</Autocomplete>
|
||||
<p v-if="description" class="text-sm text-gray-600">{{ description }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -67,6 +68,10 @@ const props = defineProps({
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'change'])
|
||||
@@ -118,7 +123,7 @@ const options = createResource({
|
||||
transform: (data) => {
|
||||
return data.map((option) => {
|
||||
return {
|
||||
label: option.value,
|
||||
label: option.label || option.value,
|
||||
value: option.value,
|
||||
description: option.description,
|
||||
}
|
||||
|
||||
@@ -303,9 +303,9 @@ const trashChapter = (chapterName) => {
|
||||
}
|
||||
|
||||
const redirectToChapter = (chapter) => {
|
||||
if (!chapter.is_scorm_package) return
|
||||
event.preventDefault()
|
||||
if (props.allowEdit) return
|
||||
if (!chapter.is_scorm_package) return
|
||||
if (!user.data) {
|
||||
showToast(
|
||||
__('You are not enrolled'),
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<AppSidebar />
|
||||
</div>
|
||||
<div class="w-full overflow-auto" id="scrollContainer">
|
||||
<OnboardingBanner />
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
@@ -16,4 +17,5 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import AppSidebar from './AppSidebar.vue'
|
||||
import OnboardingBanner from '@/components/OnboardingBanner.vue'
|
||||
</script>
|
||||
|
||||
@@ -17,10 +17,15 @@
|
||||
<template #body-content>
|
||||
<div class="space-y-4 text-base">
|
||||
<FormControl label="Title" v-model="chapter.title" :required="true" />
|
||||
<FormControl
|
||||
:label="__('Is SCORM Package')"
|
||||
<Switch
|
||||
size="sm"
|
||||
:label="__('SCORM Package')"
|
||||
:description="
|
||||
__(
|
||||
'Enable this only if you want to upload a SCORM package as a chapter.'
|
||||
)
|
||||
"
|
||||
v-model="chapter.is_scorm_package"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div v-if="chapter.is_scorm_package">
|
||||
<FileUploader
|
||||
@@ -70,14 +75,17 @@ import {
|
||||
Dialog,
|
||||
FileUploader,
|
||||
FormControl,
|
||||
Switch,
|
||||
} from 'frappe-ui'
|
||||
import { defineModel, reactive, watch, ref } from 'vue'
|
||||
import { defineModel, reactive, watch } from 'vue'
|
||||
import { showToast, getFileSize } from '@/utils/'
|
||||
import { capture } from '@/telemetry'
|
||||
import { FileText, X } from 'lucide-vue-next'
|
||||
import { useSettings } from '@/stores/settings'
|
||||
|
||||
const show = defineModel()
|
||||
const outline = defineModel('outline')
|
||||
const settingsStore = useSettings()
|
||||
|
||||
const props = defineProps({
|
||||
course: {
|
||||
@@ -137,6 +145,9 @@ const addChapter = async (close) => {
|
||||
{
|
||||
onSuccess(data) {
|
||||
cleanChapter()
|
||||
if (!settingsStore.onboardingDetails.data?.is_onboarded) {
|
||||
settingsStore.onboardingDetails.reload()
|
||||
}
|
||||
outline.value.reload()
|
||||
showToast(
|
||||
__('Success'),
|
||||
|
||||
@@ -108,9 +108,31 @@ const tabsStructure = computed(() => {
|
||||
hideLabel: true,
|
||||
items: [
|
||||
{
|
||||
label: 'Members',
|
||||
description: 'Manage the members of your learning system',
|
||||
icon: 'UserRoundPlus',
|
||||
label: 'General',
|
||||
icon: 'Wrench',
|
||||
fields: [
|
||||
{
|
||||
label: 'Enable Learning Paths',
|
||||
name: 'enable_learning_paths',
|
||||
description:
|
||||
'This will enforce students to go through programs assigned to them in the correct order.',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
label: 'Send calendar invite for evaluations',
|
||||
name: 'send_calendar_invite_for_evaluations',
|
||||
description:
|
||||
'If enabled, it sends google calendar invite to the student for evaluations.',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
label: 'Unsplash Access Key',
|
||||
name: 'unsplash_access_key',
|
||||
description:
|
||||
'Optional. If this is set, students can pick a cover image from the unsplash library for their profile page. https://unsplash.com/documentation#getting-started.',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -156,9 +178,14 @@ const tabsStructure = computed(() => {
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Settings',
|
||||
hideLabel: true,
|
||||
label: 'Lists',
|
||||
hideLabel: false,
|
||||
items: [
|
||||
{
|
||||
label: 'Members',
|
||||
description: 'Manage the members of your learning system',
|
||||
icon: 'UserRoundPlus',
|
||||
},
|
||||
{
|
||||
label: 'Categories',
|
||||
description: 'Manage the members of your learning system',
|
||||
|
||||
151
frontend/src/components/OnboardingBanner.vue
Normal file
151
frontend/src/components/OnboardingBanner.vue
Normal file
@@ -0,0 +1,151 @@
|
||||
<template>
|
||||
<div v-if="showOnboardingBanner && onboardingDetails.data">
|
||||
<Tooltip :text="__('Skip Onboarding')" placement="left">
|
||||
<X
|
||||
class="w-4 h-4 stroke-1 absolute top-2 right-2 cursor-pointer mr-1"
|
||||
@click="skipOnboarding.reload()"
|
||||
/>
|
||||
</Tooltip>
|
||||
<div class="flex items-center justify-evenly bg-gray-100 p-10">
|
||||
<div
|
||||
@click="redirectToCourseForm()"
|
||||
class="flex items-center space-x-2"
|
||||
:class="{
|
||||
'cursor-pointer': !onboardingDetails.data.course_created.length,
|
||||
}"
|
||||
>
|
||||
<span
|
||||
v-if="onboardingDetails.data.course_created.length"
|
||||
class="py-1 px-1 bg-white rounded-full"
|
||||
>
|
||||
<Check class="h-4 w-4 stroke-2 text-green-600" />
|
||||
</span>
|
||||
<span v-else class="font-semibold bg-white px-2 py-1 rounded-full">
|
||||
1
|
||||
</span>
|
||||
<span class="text-lg font-semibold">
|
||||
{{ __('Create a course') }}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
@click="redirectToChapterForm()"
|
||||
class="flex items-center space-x-2"
|
||||
:class="{
|
||||
'cursor-pointer':
|
||||
onboardingDetails.data.course_created.length &&
|
||||
!onboardingDetails.data.chapter_created.length,
|
||||
'text-gray-400': !onboardingDetails.data.course_created.length,
|
||||
}"
|
||||
>
|
||||
<span
|
||||
v-if="onboardingDetails.data.chapter_created.length"
|
||||
class="py-1 px-1 bg-white rounded-full"
|
||||
>
|
||||
<Check class="h-4 w-4 stroke-2 text-green-600" />
|
||||
</span>
|
||||
<span v-else class="font-semibold bg-white px-2 py-1 rounded-full">
|
||||
2
|
||||
</span>
|
||||
<span class="text-lg font-semibold">
|
||||
{{ __('Add a chapter') }}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
@click="redirectToLessonForm()"
|
||||
class="flex items-center space-x-2"
|
||||
:class="{
|
||||
'cursor-pointer':
|
||||
onboardingDetails.data.course_created.length &&
|
||||
onboardingDetails.data.chapter_created.length,
|
||||
'text-gray-400':
|
||||
!onboardingDetails.data.course_created.length ||
|
||||
!onboardingDetails.data.chapter_created.length,
|
||||
}"
|
||||
>
|
||||
<span
|
||||
v-if="onboardingDetails.data.lesson_created.length"
|
||||
class="py-1 px-1 bg-white rounded-full"
|
||||
>
|
||||
<Check class="h-4 w-4 stroke-2 text-green-600" />
|
||||
</span>
|
||||
<span class="font-semibold bg-white px-2 py-1 rounded-full"> 3 </span>
|
||||
<span class="text-lg font-semibold">
|
||||
{{ __('Add a lesson') }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import { Check, X } from 'lucide-vue-next'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useSettings } from '@/stores/settings'
|
||||
import { createResource, Tooltip } from 'frappe-ui'
|
||||
|
||||
const showOnboardingBanner = ref(false)
|
||||
const settings = useSettings()
|
||||
const onboardingDetails = settings.onboardingDetails
|
||||
const router = useRouter()
|
||||
|
||||
watch(onboardingDetails, () => {
|
||||
if (!onboardingDetails.data?.is_onboarded) {
|
||||
showOnboardingBanner.value = true
|
||||
} else {
|
||||
showOnboardingBanner.value = false
|
||||
}
|
||||
})
|
||||
|
||||
const redirectToCourseForm = () => {
|
||||
if (onboardingDetails.data?.course_created.length) {
|
||||
return
|
||||
} else {
|
||||
router.push({ name: 'CourseForm', params: { courseName: 'new' } })
|
||||
}
|
||||
}
|
||||
|
||||
const redirectToChapterForm = () => {
|
||||
if (!onboardingDetails.data?.course_created.length) {
|
||||
return
|
||||
} else {
|
||||
router.push({
|
||||
name: 'CourseForm',
|
||||
params: {
|
||||
courseName: onboardingDetails.data?.first_course,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const redirectToLessonForm = () => {
|
||||
if (!onboardingDetails.data?.course_created.length) {
|
||||
return
|
||||
} else if (!onboardingDetails.data?.chapter_created.length) {
|
||||
return
|
||||
} else {
|
||||
router.push({
|
||||
name: 'LessonForm',
|
||||
params: {
|
||||
courseName: onboardingDetails.data?.first_course,
|
||||
chapterNumber: 1,
|
||||
lessonNumber: 1,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const skipOnboarding = createResource({
|
||||
url: 'frappe.client.set_value',
|
||||
makeParams() {
|
||||
return {
|
||||
doctype: 'LMS Settings',
|
||||
name: 'LMS Settings',
|
||||
fieldname: 'is_onboarding_complete',
|
||||
value: 1,
|
||||
}
|
||||
},
|
||||
onSuccess(data) {
|
||||
onboardingDetails.reload()
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -397,6 +397,9 @@ const attempts = createResource({
|
||||
watch(
|
||||
() => quiz.data,
|
||||
() => {
|
||||
if (quiz.data) {
|
||||
populateQuestions()
|
||||
}
|
||||
if (quiz.data && quiz.data.max_attempts) {
|
||||
attempts.reload()
|
||||
resetQuiz()
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
<script setup>
|
||||
import { Button, Badge } from 'frappe-ui'
|
||||
import SettingFields from '@/components/SettingFields.vue'
|
||||
import { showToast } from '@/utils'
|
||||
|
||||
const props = defineProps({
|
||||
fields: {
|
||||
@@ -54,7 +55,14 @@ const update = () => {
|
||||
props.data.doc[f.name] = f.value
|
||||
}
|
||||
})
|
||||
props.data.save.submit()
|
||||
props.data.save.submit(
|
||||
{},
|
||||
{
|
||||
onError(err) {
|
||||
showToast('Error', err.messages?.[0] || err, 'x')
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -90,6 +90,7 @@
|
||||
:type="field.type"
|
||||
:rows="field.rows"
|
||||
:options="field.options"
|
||||
:description="field.description"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -100,7 +101,7 @@
|
||||
import { FormControl, FileUploader, Button, Switch } from 'frappe-ui'
|
||||
import { computed } from 'vue'
|
||||
import { getFileSize, validateFile } from '@/utils'
|
||||
import { X, FileText } from 'lucide-vue-next'
|
||||
import { X } from 'lucide-vue-next'
|
||||
import Link from '@/components/Controls/Link.vue'
|
||||
import CodeEditor from '@/components/Controls/CodeEditor.vue'
|
||||
|
||||
|
||||
@@ -434,6 +434,9 @@ const submitCourse = () => {
|
||||
onSuccess(data) {
|
||||
capture('course_created')
|
||||
showToast('Success', 'Course created successfully', 'check')
|
||||
if (!settingsStore.onboardingDetails.data?.is_onboarded) {
|
||||
settingsStore.onboardingDetails.reload()
|
||||
}
|
||||
router.push({
|
||||
name: 'CourseForm',
|
||||
params: { courseName: data.name },
|
||||
|
||||
@@ -160,30 +160,45 @@
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
Breadcrumbs,
|
||||
Tabs,
|
||||
Badge,
|
||||
Breadcrumbs,
|
||||
Button,
|
||||
FormControl,
|
||||
call,
|
||||
createResource,
|
||||
FormControl,
|
||||
Tabs,
|
||||
} from 'frappe-ui'
|
||||
import CourseCard from '@/components/CourseCard.vue'
|
||||
import { BookOpen, Plus, Search } from 'lucide-vue-next'
|
||||
import { ref, computed, inject, onMounted, watch } from 'vue'
|
||||
import { updateDocumentTitle } from '@/utils'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useSettings } from '@/stores/settings'
|
||||
|
||||
const user = inject('$user')
|
||||
const searchQuery = ref('')
|
||||
const currentCategory = ref(null)
|
||||
const hasCourses = ref(false)
|
||||
const router = useRouter()
|
||||
const settings = useSettings()
|
||||
|
||||
onMounted(() => {
|
||||
checkLearningPath()
|
||||
let queries = new URLSearchParams(location.search)
|
||||
if (queries.has('category')) {
|
||||
currentCategory.value = queries.get('category')
|
||||
}
|
||||
})
|
||||
|
||||
const checkLearningPath = () => {
|
||||
if (
|
||||
settings.learningPaths.data &&
|
||||
(!user.data?.is_moderator || !user.data?.is_instructor)
|
||||
) {
|
||||
router.push({ name: 'Programs' })
|
||||
}
|
||||
}
|
||||
|
||||
const courses = createResource({
|
||||
url: 'lms.lms.utils.get_courses',
|
||||
cache: ['courses', user.data?.email],
|
||||
|
||||
@@ -92,11 +92,13 @@ import LessonHelp from '@/components/LessonHelp.vue'
|
||||
import { ChevronRight } from 'lucide-vue-next'
|
||||
import { updateDocumentTitle, createToast, getEditorTools } from '@/utils'
|
||||
import { capture } from '@/telemetry'
|
||||
import { useSettings } from '@/stores/settings'
|
||||
|
||||
const editor = ref(null)
|
||||
const instructorEditor = ref(null)
|
||||
const user = inject('$user')
|
||||
const openInstructorEditor = ref(false)
|
||||
const settingsStore = useSettings()
|
||||
let autoSaveInterval
|
||||
let showSuccessMessage = false
|
||||
|
||||
@@ -393,6 +395,9 @@ const createNewLesson = () => {
|
||||
onSuccess() {
|
||||
capture('lesson_created')
|
||||
showToast('Success', 'Lesson created successfully', 'check')
|
||||
if (!settingsStore.onboardingDetails.data?.is_onboarded) {
|
||||
settingsStore.onboardingDetails.reload()
|
||||
}
|
||||
lessonDetails.reload()
|
||||
},
|
||||
}
|
||||
|
||||
354
frontend/src/pages/ProgramForm.vue
Normal file
354
frontend/src/pages/ProgramForm.vue
Normal file
@@ -0,0 +1,354 @@
|
||||
<template>
|
||||
<header
|
||||
class="sticky top-0 z-10 flex flex-col md:flex-row md:items-center justify-between border-b bg-white px-3 py-2.5 sm:px-5"
|
||||
>
|
||||
<Breadcrumbs :items="breadbrumbs" />
|
||||
<Button variant="solid">
|
||||
{{ __('Save') }}
|
||||
</Button>
|
||||
</header>
|
||||
<div v-if="program.doc" class="pt-5 px-5 w-3/4 mx-auto space-y-10">
|
||||
<FormControl v-model="program.doc.title" :label="__('Title')" />
|
||||
|
||||
<!-- Courses -->
|
||||
<div>
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div class="text-lg font-semibold">
|
||||
{{ __('Program Courses') }}
|
||||
</div>
|
||||
<Button
|
||||
@click="
|
||||
() => {
|
||||
currentForm = 'course'
|
||||
showDialog = true
|
||||
}
|
||||
"
|
||||
>
|
||||
<template #prefix>
|
||||
<Plus class="w-4 h-4" />
|
||||
</template>
|
||||
{{ __('Add') }}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<ListView
|
||||
:columns="courseColumns"
|
||||
:rows="program.doc.program_courses"
|
||||
row-key="name"
|
||||
:options="{
|
||||
showTooltip: false,
|
||||
}"
|
||||
>
|
||||
<ListHeader
|
||||
class="mb-2 grid items-center space-x-4 rounded bg-gray-100 p-2"
|
||||
>
|
||||
<ListHeaderItem :item="item" v-for="item in courseColumns" />
|
||||
</ListHeader>
|
||||
<ListRows>
|
||||
<Draggable
|
||||
:list="program.doc.program_courses"
|
||||
item-key="name"
|
||||
group="items"
|
||||
@end="updateOrder"
|
||||
>
|
||||
<template #item="{ element: row }">
|
||||
<ListRow :row="row" />
|
||||
</template>
|
||||
</Draggable>
|
||||
</ListRows>
|
||||
<ListSelectBanner>
|
||||
<template #actions="{ unselectAll, selections }">
|
||||
<div class="flex gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
@click="remove(selections, unselectAll, 'program_courses')"
|
||||
>
|
||||
<Trash2 class="h-4 w-4 stroke-1.5" />
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</ListSelectBanner>
|
||||
</ListView>
|
||||
</div>
|
||||
|
||||
<!-- Members -->
|
||||
<div>
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div class="text-lg font-semibold">
|
||||
{{ __('Program Members') }}
|
||||
</div>
|
||||
<Button
|
||||
@click="
|
||||
() => {
|
||||
currentForm = 'member'
|
||||
showDialog = true
|
||||
}
|
||||
"
|
||||
>
|
||||
<template #prefix>
|
||||
<Plus class="w-4 h-4" />
|
||||
</template>
|
||||
{{ __('Add') }}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<ListView
|
||||
:columns="memberColumns"
|
||||
:rows="program.doc.program_members"
|
||||
row-key="name"
|
||||
:options="{
|
||||
showTooltip: false,
|
||||
}"
|
||||
>
|
||||
<ListHeader
|
||||
class="mb-2 grid items-center space-x-4 rounded bg-gray-100 p-2"
|
||||
>
|
||||
<ListHeaderItem :item="item" v-for="item in memberColumns" />
|
||||
</ListHeader>
|
||||
<ListRows>
|
||||
<ListRow :row="row" v-for="row in program.doc.program_members" />
|
||||
</ListRows>
|
||||
<ListSelectBanner>
|
||||
<template #actions="{ unselectAll, selections }">
|
||||
<div class="flex gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
@click="remove(selections, unselectAll, 'program_members')"
|
||||
>
|
||||
<Trash2 class="h-4 w-4 stroke-1.5" />
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</ListSelectBanner>
|
||||
</ListView>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Dialog
|
||||
v-model="showDialog"
|
||||
:options="{
|
||||
title:
|
||||
currentForm == 'course'
|
||||
? __('New Program Course')
|
||||
: __('New Program Member'),
|
||||
actions: [
|
||||
{
|
||||
label: __('Add'),
|
||||
variant: 'solid',
|
||||
onClick: () =>
|
||||
currentForm == 'course'
|
||||
? addProgramCourse(close)
|
||||
: addProgramMember(close),
|
||||
},
|
||||
],
|
||||
}"
|
||||
>
|
||||
<template #body-content>
|
||||
<Link
|
||||
v-if="currentForm == 'course'"
|
||||
v-model="course"
|
||||
doctype="LMS Course"
|
||||
:filters="{
|
||||
disable_self_learning: 1,
|
||||
}"
|
||||
:label="__('Program Course')"
|
||||
:description="
|
||||
__(
|
||||
'Only courses for which self learning is disabled can be added to program.'
|
||||
)
|
||||
"
|
||||
/>
|
||||
|
||||
<Link
|
||||
v-if="currentForm == 'member'"
|
||||
v-model="member"
|
||||
doctype="User"
|
||||
:filters="{
|
||||
ignore_user_type: 1,
|
||||
}"
|
||||
:label="__('Program Member')"
|
||||
/>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup>
|
||||
import {
|
||||
Breadcrumbs,
|
||||
Button,
|
||||
call,
|
||||
createDocumentResource,
|
||||
Dialog,
|
||||
FormControl,
|
||||
ListView,
|
||||
ListRows,
|
||||
ListRow,
|
||||
ListHeader,
|
||||
ListHeaderItem,
|
||||
ListSelectBanner,
|
||||
} from 'frappe-ui'
|
||||
import { computed, ref } from 'vue'
|
||||
import { Plus, Trash2 } from 'lucide-vue-next'
|
||||
import Link from '@/components/Controls/Link.vue'
|
||||
import { showToast } from '@/utils/'
|
||||
import Draggable from 'vuedraggable'
|
||||
|
||||
const showDialog = ref(false)
|
||||
const currentForm = ref(null)
|
||||
const course = ref(null)
|
||||
const member = ref(null)
|
||||
|
||||
const props = defineProps({
|
||||
programName: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const program = createDocumentResource({
|
||||
doctype: 'LMS Program',
|
||||
name: props.programName,
|
||||
auto: true,
|
||||
cache: ['program', props.programName],
|
||||
})
|
||||
|
||||
const addProgramCourse = () => {
|
||||
program.setValue.submit(
|
||||
{
|
||||
program_courses: [
|
||||
...program.doc.program_courses,
|
||||
{ course: course.value },
|
||||
],
|
||||
},
|
||||
{
|
||||
onSuccess(data) {
|
||||
showDialog.value = false
|
||||
course.value = null
|
||||
showToast(__('Success'), __('Course added to program'), 'check')
|
||||
program.reload()
|
||||
},
|
||||
onError(err) {
|
||||
showToast('Error', err.messages?.[0] || err, 'x')
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const addProgramMember = () => {
|
||||
program.setValue.submit(
|
||||
{
|
||||
program_members: [
|
||||
...program.doc.program_members,
|
||||
{ member: member.value },
|
||||
],
|
||||
},
|
||||
{
|
||||
onSuccess(data) {
|
||||
showDialog.value = false
|
||||
member.value = null
|
||||
showToast(__('Success'), __('Member added to program'), 'check')
|
||||
program.reload()
|
||||
},
|
||||
onError(err) {
|
||||
showToast('Error', err.messages?.[0] || err, 'x')
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const remove = (selections, unselectAll, doctype) => {
|
||||
selections = Array.from(selections)
|
||||
program.setValue.submit(
|
||||
{
|
||||
[doctype]: program.doc[doctype].filter(
|
||||
(row) => !selections.includes(row.name)
|
||||
),
|
||||
},
|
||||
{
|
||||
onSuccess(data) {
|
||||
unselectAll()
|
||||
showToast(__('Success'), __('Items removed successfully'), 'check')
|
||||
program.reload()
|
||||
},
|
||||
onError(err) {
|
||||
showToast('Error', err.messages?.[0] || err, 'x')
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const updateOrder = (e) => {
|
||||
let sourceIdx = e.from.dataset.idx
|
||||
let targetIdx = e.to.dataset.idx
|
||||
let courses = program.doc.program_courses
|
||||
courses.splice(targetIdx, 0, courses.splice(sourceIdx, 1)[0])
|
||||
|
||||
courses.forEach((course, index) => {
|
||||
course.idx = index + 1
|
||||
})
|
||||
|
||||
program.setValue.submit(
|
||||
{
|
||||
program_courses: courses,
|
||||
},
|
||||
{
|
||||
onSuccess(data) {
|
||||
showToast(__('Success'), __('Course moved successfully'), 'check')
|
||||
program.reload()
|
||||
},
|
||||
onError(err) {
|
||||
showToast('Error', err.messages?.[0] || err, 'x')
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const courseColumns = computed(() => {
|
||||
return [
|
||||
{
|
||||
label: 'Title',
|
||||
key: 'course_title',
|
||||
width: 3,
|
||||
},
|
||||
{
|
||||
label: 'ID',
|
||||
key: 'course',
|
||||
width: 3,
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
const memberColumns = computed(() => {
|
||||
return [
|
||||
{
|
||||
label: 'Member',
|
||||
key: 'member',
|
||||
width: 3,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
label: 'Full Name',
|
||||
key: 'full_name',
|
||||
width: 3,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
label: 'Progress',
|
||||
key: 'progress',
|
||||
width: 3,
|
||||
align: 'left',
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
const breadbrumbs = computed(() => {
|
||||
return [
|
||||
{
|
||||
label: 'Programs',
|
||||
route: { name: 'Programs' },
|
||||
},
|
||||
{
|
||||
label: props.programName === 'new' ? 'New Program' : props.programName,
|
||||
},
|
||||
]
|
||||
})
|
||||
</script>
|
||||
185
frontend/src/pages/Programs.vue
Normal file
185
frontend/src/pages/Programs.vue
Normal file
@@ -0,0 +1,185 @@
|
||||
<template>
|
||||
<header
|
||||
class="sticky top-0 z-10 flex items-center justify-between border-b bg-white px-3 py-2.5 sm:px-5"
|
||||
>
|
||||
<Breadcrumbs :items="breadbrumbs" />
|
||||
<Button
|
||||
v-if="user.data?.is_moderator || user.data?.is_instructor"
|
||||
@click="showDialog = true"
|
||||
variant="solid"
|
||||
>
|
||||
<template #prefix>
|
||||
<Plus class="h-4 w-4 stroke-1.5" />
|
||||
</template>
|
||||
{{ __('New') }}
|
||||
</Button>
|
||||
</header>
|
||||
<div v-if="programs.data?.length" class="pt-5 px-5">
|
||||
<div v-for="program in programs.data" class="mb-20">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="text-xl font-semibold">
|
||||
{{ program.name }}
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<Badge
|
||||
v-if="program.members"
|
||||
variant="subtle"
|
||||
theme="green"
|
||||
size="lg"
|
||||
>
|
||||
{{ program.members }}
|
||||
{{
|
||||
program.members == 1 ? __(singularize('members')) : __('members')
|
||||
}}
|
||||
</Badge>
|
||||
<Badge
|
||||
v-if="program.progress"
|
||||
variant="subtle"
|
||||
theme="blue"
|
||||
size="lg"
|
||||
>
|
||||
{{ program.progress }}{{ __('% completed') }}
|
||||
</Badge>
|
||||
|
||||
<router-link
|
||||
v-if="user.data?.is_moderator || user.data?.is_instructor"
|
||||
:to="{
|
||||
name: 'ProgramForm',
|
||||
params: { programName: program.name },
|
||||
}"
|
||||
>
|
||||
<Button>
|
||||
<template #prefix>
|
||||
<Edit class="h-4 w-4 stroke-1.5" />
|
||||
</template>
|
||||
{{ __('Edit') }}
|
||||
</Button>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="program.courses?.length"
|
||||
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5 mt-5"
|
||||
>
|
||||
<CourseCard
|
||||
v-for="course in program.courses"
|
||||
:course="course"
|
||||
@click="enrollMember(program.name, course.name)"
|
||||
class="cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="text-sm italic text-gray-600 mt-4">
|
||||
{{ __('No courses in this program') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="text-center p-5 text-gray-600 mt-52 w-3/4 md:w-1/2 mx-auto space-y-2"
|
||||
>
|
||||
<BookOpen class="size-10 mx-auto stroke-1 text-gray-500" />
|
||||
<div class="text-xl font-medium">
|
||||
{{ __('No programs found') }}
|
||||
</div>
|
||||
<div class="leading-5">
|
||||
{{
|
||||
__(
|
||||
'There are no programs available at the moment. Keep an eye out, fresh learning experiences are on the way soon!'
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Dialog
|
||||
v-model="showDialog"
|
||||
:options="{
|
||||
title: __('New Program'),
|
||||
actions: [
|
||||
{
|
||||
label: __('Create'),
|
||||
variant: 'solid',
|
||||
onClick: () => createProgram(close),
|
||||
},
|
||||
],
|
||||
}"
|
||||
>
|
||||
<template #body-content>
|
||||
<FormControl :label="__('Title')" v-model="title" />
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup>
|
||||
import {
|
||||
Badge,
|
||||
Breadcrumbs,
|
||||
Button,
|
||||
call,
|
||||
createResource,
|
||||
Dialog,
|
||||
FormControl,
|
||||
} from 'frappe-ui'
|
||||
import { computed, inject, ref } from 'vue'
|
||||
import { BookOpen, Edit, Plus } from 'lucide-vue-next'
|
||||
import CourseCard from '@/components/CourseCard.vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { showToast, singularize } from '@/utils'
|
||||
|
||||
const user = inject('$user')
|
||||
const showDialog = ref(false)
|
||||
const router = useRouter()
|
||||
const title = ref('')
|
||||
|
||||
const programs = createResource({
|
||||
url: 'lms.lms.utils.get_programs',
|
||||
auto: true,
|
||||
cache: 'programs',
|
||||
})
|
||||
|
||||
const createProgram = (close) => {
|
||||
call('frappe.client.insert', {
|
||||
doc: {
|
||||
doctype: 'LMS Program',
|
||||
title: title.value,
|
||||
},
|
||||
}).then((res) => {
|
||||
router.push({ name: 'ProgramForm', params: { programName: res.name } })
|
||||
})
|
||||
}
|
||||
|
||||
const enrollMember = (program, course) => {
|
||||
call('lms.lms.utils.enroll_in_program_course', {
|
||||
program: program,
|
||||
course: course,
|
||||
})
|
||||
.then((data) => {
|
||||
if (data.current_lesson) {
|
||||
router.push({
|
||||
name: 'Lesson',
|
||||
params: {
|
||||
courseName: course,
|
||||
chapterNumber: data.current_lesson.split('-')[0],
|
||||
lessonNumber: data.current_lesson.split('-')[1],
|
||||
},
|
||||
})
|
||||
} else if (data) {
|
||||
router.push({
|
||||
name: 'Lesson',
|
||||
params: {
|
||||
courseName: course,
|
||||
chapterNumber: 1,
|
||||
lessonNumber: 1,
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
showToast('Error', err.messages?.[0] || err, 'x')
|
||||
})
|
||||
}
|
||||
|
||||
const breadbrumbs = computed(() => [
|
||||
{
|
||||
label: 'Programs',
|
||||
},
|
||||
])
|
||||
</script>
|
||||
@@ -182,6 +182,17 @@ const routes = [
|
||||
component: () => import('@/pages/QuizSubmission.vue'),
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: '/programs/:programName',
|
||||
name: 'ProgramForm',
|
||||
component: () => import('@/pages/ProgramForm.vue'),
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: '/programs',
|
||||
name: 'Programs',
|
||||
component: () => import('@/pages/Programs.vue'),
|
||||
},
|
||||
]
|
||||
|
||||
let router = createRouter({
|
||||
|
||||
@@ -1,12 +1,32 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import { createResource } from 'frappe-ui'
|
||||
|
||||
export const useSettings = defineStore('settings', () => {
|
||||
const isSettingsOpen = ref(false)
|
||||
const activeTab = ref(null)
|
||||
const learningPaths = createResource({
|
||||
url: 'frappe.client.get_single_value',
|
||||
makeParams(values) {
|
||||
return {
|
||||
doctype: 'LMS Settings',
|
||||
field: 'enable_learning_paths',
|
||||
}
|
||||
},
|
||||
auto: true,
|
||||
cache: ['learningPaths'],
|
||||
})
|
||||
|
||||
const onboardingDetails = createResource({
|
||||
url: 'lms.lms.utils.is_onboarding_complete',
|
||||
auto: true,
|
||||
cache: ['onboardingDetails'],
|
||||
})
|
||||
|
||||
return {
|
||||
isSettingsOpen,
|
||||
activeTab,
|
||||
learningPaths,
|
||||
onboardingDetails,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "2.12.0"
|
||||
__version__ = "2.13.0"
|
||||
|
||||
@@ -66,7 +66,9 @@ def delete_lms_roles():
|
||||
|
||||
|
||||
def create_course_creator_role():
|
||||
if not frappe.db.exists("Role", "Course Creator"):
|
||||
if frappe.db.exists("Role", "Course Creator"):
|
||||
frappe.db.set_value("Role", "Course Creator", "desk_access", 0)
|
||||
else:
|
||||
role = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Role",
|
||||
@@ -79,7 +81,9 @@ def create_course_creator_role():
|
||||
|
||||
|
||||
def create_moderator_role():
|
||||
if not frappe.db.exists("Role", "Moderator"):
|
||||
if frappe.db.exists("Role", "Moderator"):
|
||||
frappe.db.set_value("Role", "Moderator", "desk_access", 0)
|
||||
else:
|
||||
role = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Role",
|
||||
@@ -92,7 +96,9 @@ def create_moderator_role():
|
||||
|
||||
|
||||
def create_evaluator_role():
|
||||
if not frappe.db.exists("Role", "Batch Evaluator"):
|
||||
if frappe.db.exists("Role", "Batch Evaluator"):
|
||||
frappe.db.set_value("Role", "Batch Evaluator", "desk_access", 0)
|
||||
else:
|
||||
role = frappe.new_doc("Role")
|
||||
role.update(
|
||||
{
|
||||
@@ -105,7 +111,9 @@ def create_evaluator_role():
|
||||
|
||||
|
||||
def create_lms_student_role():
|
||||
if not frappe.db.exists("Role", "LMS Student"):
|
||||
if frappe.db.exists("Role", "LMS Student"):
|
||||
frappe.db.set_value("Role", "LMS Student", "desk_access", 0)
|
||||
else:
|
||||
role = frappe.new_doc("Role")
|
||||
role.update(
|
||||
{
|
||||
|
||||
@@ -93,15 +93,15 @@ def save_progress(lesson, course):
|
||||
|
||||
frappe.db.set_value("LMS Enrollment", membership, "current_lesson", lesson)
|
||||
|
||||
quiz_completed = get_quiz_progress(lesson)
|
||||
if not quiz_completed:
|
||||
return 0
|
||||
|
||||
if frappe.db.exists(
|
||||
"LMS Course Progress", {"lesson": lesson, "member": frappe.session.user}
|
||||
):
|
||||
return
|
||||
|
||||
quiz_completed = get_quiz_progress(lesson)
|
||||
if not quiz_completed:
|
||||
return 0
|
||||
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "LMS Course Progress",
|
||||
|
||||
@@ -53,7 +53,7 @@ class LMSCourse(Document):
|
||||
frappe.throw(_("Please install the Payments app to create a paid courses."))
|
||||
|
||||
def validate_amount_and_currency(self):
|
||||
if self.paid_course and (not self.amount and not self.currency):
|
||||
if self.paid_course and (not self.course_price and not self.currency):
|
||||
frappe.throw(_("Amount and currency are required for paid courses."))
|
||||
|
||||
def on_update(self):
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import ceil
|
||||
|
||||
|
||||
class LMSEnrollment(Document):
|
||||
@@ -11,6 +12,9 @@ class LMSEnrollment(Document):
|
||||
self.validate_membership_in_same_batch()
|
||||
self.validate_membership_in_different_batch_same_course()
|
||||
|
||||
def on_update(self):
|
||||
self.update_program_progress()
|
||||
|
||||
def validate_membership_in_same_batch(self):
|
||||
filters = {"member": self.member, "course": self.course, "name": ["!=", self.name]}
|
||||
if self.batch_old:
|
||||
@@ -55,6 +59,26 @@ class LMSEnrollment(Document):
|
||||
)
|
||||
)
|
||||
|
||||
def update_program_progress(self):
|
||||
programs = frappe.get_all(
|
||||
"LMS Program Member", {"member": self.member}, ["parent", "name"]
|
||||
)
|
||||
|
||||
for program in programs:
|
||||
total_progress = 0
|
||||
courses = frappe.get_all(
|
||||
"LMS Program Course", {"parent": program.parent}, pluck="course"
|
||||
)
|
||||
for course in courses:
|
||||
progress = frappe.db.get_value(
|
||||
"LMS Enrollment", {"course": course, "member": self.member}, "progress"
|
||||
)
|
||||
progress = progress or 0
|
||||
total_progress += progress
|
||||
|
||||
average_progress = ceil(total_progress / len(courses))
|
||||
frappe.db.set_value("LMS Program Member", program.name, "progress", average_progress)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def create_membership(
|
||||
|
||||
0
lms/lms/doctype/lms_program/__init__.py
Normal file
0
lms/lms/doctype/lms_program/__init__.py
Normal file
8
lms/lms/doctype/lms_program/lms_program.js
Normal file
8
lms/lms/doctype/lms_program/lms_program.js
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright (c) 2024, Frappe and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
// frappe.ui.form.on("LMS Program", {
|
||||
// refresh(frm) {
|
||||
|
||||
// },
|
||||
// });
|
||||
84
lms/lms/doctype/lms_program/lms_program.json
Normal file
84
lms/lms/doctype/lms_program/lms_program.json
Normal file
@@ -0,0 +1,84 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:title",
|
||||
"creation": "2024-11-18 12:27:13.283169",
|
||||
"doctype": "DocType",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"title",
|
||||
"program_courses",
|
||||
"program_members"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "program_courses",
|
||||
"fieldtype": "Table",
|
||||
"label": "Program Courses",
|
||||
"options": "LMS Program Course"
|
||||
},
|
||||
{
|
||||
"fieldname": "program_members",
|
||||
"fieldtype": "Table",
|
||||
"label": "Program Members",
|
||||
"options": "LMS Program Member"
|
||||
},
|
||||
{
|
||||
"fieldname": "title",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Title",
|
||||
"reqd": 1,
|
||||
"unique": 1
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2024-11-20 12:26:02.214628",
|
||||
"modified_by": "Administrator",
|
||||
"module": "LMS",
|
||||
"name": "LMS Program",
|
||||
"naming_rule": "By fieldname",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"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": "Course Creator",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
32
lms/lms/doctype/lms_program/lms_program.py
Normal file
32
lms/lms/doctype/lms_program/lms_program.py
Normal file
@@ -0,0 +1,32 @@
|
||||
# Copyright (c) 2024, Frappe and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class LMSProgram(Document):
|
||||
def validate(self):
|
||||
self.validate_program_courses()
|
||||
self.validate_program_members()
|
||||
|
||||
def validate_program_courses(self):
|
||||
courses = [row.course for row in self.program_courses]
|
||||
duplicates = {course for course in courses if courses.count(course) > 1}
|
||||
if len(duplicates):
|
||||
frappe.throw(
|
||||
_("Course {0} has already been added to this batch.").format(
|
||||
frappe.bold(next(iter(duplicates)))
|
||||
)
|
||||
)
|
||||
|
||||
def validate_program_members(self):
|
||||
members = [row.member for row in self.program_members]
|
||||
duplicates = {member for member in members if members.count(member) > 1}
|
||||
if len(duplicates):
|
||||
frappe.throw(
|
||||
_("Member {0} has already been added to this batch.").format(
|
||||
frappe.bold(next(iter(duplicates)))
|
||||
)
|
||||
)
|
||||
21
lms/lms/doctype/lms_program/test_lms_program.py
Normal file
21
lms/lms/doctype/lms_program/test_lms_program.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# Copyright (c) 2024, Frappe and Contributors
|
||||
# See license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.tests import IntegrationTestCase, UnitTestCase
|
||||
|
||||
|
||||
# On IntegrationTestCase, the doctype test records and all
|
||||
# link-field test record depdendencies are recursively loaded
|
||||
# Use these module variables to add/remove to/from that list
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
|
||||
IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
|
||||
|
||||
|
||||
class TestLMSProgram(UnitTestCase):
|
||||
"""
|
||||
Unit tests for LMSProgram.
|
||||
Use this class for testing individual functions and methods.
|
||||
"""
|
||||
|
||||
pass
|
||||
0
lms/lms/doctype/lms_program_course/__init__.py
Normal file
0
lms/lms/doctype/lms_program_course/__init__.py
Normal file
42
lms/lms/doctype/lms_program_course/lms_program_course.json
Normal file
42
lms/lms/doctype/lms_program_course/lms_program_course.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_rename": 1,
|
||||
"creation": "2024-11-18 12:27:37.030302",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"course",
|
||||
"course_title"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "course",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Course",
|
||||
"options": "LMS Course",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "course.title",
|
||||
"fieldname": "course_title",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Course Title",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-11-18 12:43:46.800199",
|
||||
"modified_by": "Administrator",
|
||||
"module": "LMS",
|
||||
"name": "LMS Program Course",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
9
lms/lms/doctype/lms_program_course/lms_program_course.py
Normal file
9
lms/lms/doctype/lms_program_course/lms_program_course.py
Normal file
@@ -0,0 +1,9 @@
|
||||
# Copyright (c) 2024, Frappe and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class LMSProgramCourse(Document):
|
||||
pass
|
||||
0
lms/lms/doctype/lms_program_member/__init__.py
Normal file
0
lms/lms/doctype/lms_program_member/__init__.py
Normal file
50
lms/lms/doctype/lms_program_member/lms_program_member.json
Normal file
50
lms/lms/doctype/lms_program_member/lms_program_member.json
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_rename": 1,
|
||||
"creation": "2024-11-18 12:29:13.615014",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"member",
|
||||
"full_name",
|
||||
"progress"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "member",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Member",
|
||||
"options": "User",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "member.full_name",
|
||||
"fieldname": "full_name",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Full Name",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "progress",
|
||||
"fieldtype": "Int",
|
||||
"in_list_view": 1,
|
||||
"label": "Progress"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-11-21 12:51:31.882576",
|
||||
"modified_by": "Administrator",
|
||||
"module": "LMS",
|
||||
"name": "LMS Program Member",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
9
lms/lms/doctype/lms_program_member/lms_program_member.py
Normal file
9
lms/lms/doctype/lms_program_member/lms_program_member.py
Normal file
@@ -0,0 +1,9 @@
|
||||
# Copyright (c) 2024, Frappe and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class LMSProgramMember(Document):
|
||||
pass
|
||||
@@ -5,13 +5,15 @@
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"general_tab",
|
||||
"default_home",
|
||||
"send_calendar_invite_for_evaluations",
|
||||
"is_onboarding_complete",
|
||||
"column_break_zdel",
|
||||
"enable_learning_paths",
|
||||
"unsplash_access_key",
|
||||
"livecode_url",
|
||||
"section_break_szgq",
|
||||
"send_calendar_invite_for_evaluations",
|
||||
"show_day_view",
|
||||
"column_break_2",
|
||||
"show_dashboard",
|
||||
@@ -80,6 +82,7 @@
|
||||
{
|
||||
"fieldname": "mentor_request_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 1,
|
||||
"label": "Mentor Request"
|
||||
},
|
||||
{
|
||||
@@ -127,6 +130,7 @@
|
||||
{
|
||||
"fieldname": "section_break_szgq",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 1,
|
||||
"label": "Batch Settings"
|
||||
},
|
||||
{
|
||||
@@ -336,12 +340,23 @@
|
||||
"fieldname": "payments_app_is_not_installed",
|
||||
"fieldtype": "HTML",
|
||||
"label": "Payments app is not installed"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "enable_learning_paths",
|
||||
"fieldtype": "Check",
|
||||
"label": "Enable Learning Paths"
|
||||
},
|
||||
{
|
||||
"fieldname": "general_tab",
|
||||
"fieldtype": "Tab Break",
|
||||
"label": "General"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2024-10-01 12:15:49.800242",
|
||||
"modified": "2024-11-20 11:55:05.358421",
|
||||
"modified_by": "Administrator",
|
||||
"module": "LMS",
|
||||
"name": "LMS Settings",
|
||||
@@ -356,6 +371,13 @@
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"role": "LMS Student",
|
||||
"share": 1
|
||||
}
|
||||
],
|
||||
"sort_field": "modified",
|
||||
|
||||
@@ -855,7 +855,10 @@ def get_telemetry_boot_info():
|
||||
}
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def is_onboarding_complete():
|
||||
if not has_course_moderator_role():
|
||||
return {"is_onboarded": False}
|
||||
course_created = frappe.db.a_row_exists("LMS Course")
|
||||
chapter_created = frappe.db.a_row_exists("Course Chapter")
|
||||
lesson_created = frappe.db.a_row_exists("Course Lesson")
|
||||
@@ -1751,3 +1754,81 @@ def enroll_in_batch(batch, payment_name=None):
|
||||
)
|
||||
|
||||
student.save(ignore_permissions=True)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_programs():
|
||||
if (
|
||||
has_course_moderator_role()
|
||||
or has_course_instructor_role()
|
||||
or has_course_evaluator_role()
|
||||
):
|
||||
programs = frappe.get_all("LMS Program", fields=["name"])
|
||||
else:
|
||||
programs = frappe.get_all(
|
||||
"LMS Program Member", {"member": frappe.session.user}, ["parent as name", "progress"]
|
||||
)
|
||||
|
||||
for program in programs:
|
||||
program_courses = frappe.get_all(
|
||||
"LMS Program Course", {"parent": program.name}, ["course"], order_by="idx"
|
||||
)
|
||||
program.courses = []
|
||||
for course in program_courses:
|
||||
program.courses.append(get_course_details(course.course))
|
||||
|
||||
program.members = frappe.db.count("LMS Program Member", {"parent": program.name})
|
||||
|
||||
return programs
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def enroll_in_program_course(program, course):
|
||||
enrollment = frappe.db.exists(
|
||||
"LMS Enrollment", {"member": frappe.session.user, "course": course}
|
||||
)
|
||||
|
||||
if enrollment:
|
||||
enrollment = frappe.db.get_value(
|
||||
"LMS Enrollment", enrollment, ["name", "current_lesson"], as_dict=1
|
||||
)
|
||||
enrollment.current_lesson = get_lesson_index(enrollment.current_lesson)
|
||||
return enrollment
|
||||
|
||||
program_courses = frappe.get_all(
|
||||
"LMS Program Course", {"parent": program}, ["course", "idx"], order_by="idx"
|
||||
)
|
||||
current_course_idx = [
|
||||
program_course.idx
|
||||
for program_course in program_courses
|
||||
if program_course.course == course
|
||||
][0]
|
||||
|
||||
for program_course in program_courses:
|
||||
if program_course.idx < current_course_idx:
|
||||
enrollment = frappe.db.get_value(
|
||||
"LMS Enrollment",
|
||||
{"member": frappe.session.user, "course": program_course.course},
|
||||
["name", "progress"],
|
||||
as_dict=1,
|
||||
)
|
||||
if enrollment and enrollment.progress != 100:
|
||||
frappe.throw(
|
||||
_("Please complete the previous courses in the program to enroll in this course.")
|
||||
)
|
||||
elif not enrollment:
|
||||
frappe.throw(
|
||||
_("Please complete the previous courses in the program to enroll in this course.")
|
||||
)
|
||||
else:
|
||||
continue
|
||||
|
||||
enrollment = frappe.new_doc("LMS Enrollment")
|
||||
enrollment.update(
|
||||
{
|
||||
"member": frappe.session.user,
|
||||
"course": course,
|
||||
}
|
||||
)
|
||||
enrollment.save()
|
||||
return enrollment
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"app": "lms",
|
||||
"charts": [
|
||||
{
|
||||
"chart_name": "New Signups",
|
||||
@@ -145,7 +146,7 @@
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2024-08-09 13:19:06.273056",
|
||||
"modified": "2024-11-21 12:16:25.886431",
|
||||
"modified_by": "Administrator",
|
||||
"module": "LMS",
|
||||
"name": "LMS",
|
||||
@@ -212,5 +213,6 @@
|
||||
"type": "DocType"
|
||||
}
|
||||
],
|
||||
"title": "LMS"
|
||||
}
|
||||
"title": "LMS",
|
||||
"type": "Workspace"
|
||||
}
|
||||
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: frappe\n"
|
||||
"Report-Msgid-Bugs-To: jannat@frappe.io\n"
|
||||
"POT-Creation-Date: 2024-11-15 16:04+0000\n"
|
||||
"PO-Revision-Date: 2024-11-18 17:32\n"
|
||||
"PO-Revision-Date: 2024-11-23 18:16\n"
|
||||
"Last-Translator: jannat@frappe.io\n"
|
||||
"Language-Team: Persian\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -183,7 +183,7 @@ msgstr "آدرس خط 2"
|
||||
#: lms/lms/doctype/cohort_web_page/cohort_web_page.json
|
||||
#: lms/lms/doctype/lms_enrollment/lms_enrollment.json
|
||||
msgid "Admin"
|
||||
msgstr "اَدمین"
|
||||
msgstr "ادمین"
|
||||
|
||||
#. Name of a role
|
||||
#: lms/lms/doctype/lms_badge/lms_badge.json
|
||||
@@ -729,11 +729,11 @@ msgstr ""
|
||||
|
||||
#: frontend/src/components/Modals/ChapterModal.vue:149
|
||||
msgid "Chapter added successfully"
|
||||
msgstr ""
|
||||
msgstr "فصل با موفقیت اضافه شد"
|
||||
|
||||
#: frontend/src/components/Modals/ChapterModal.vue:193
|
||||
msgid "Chapter updated successfully"
|
||||
msgstr ""
|
||||
msgstr "فصل با موفقیت به روز شد"
|
||||
|
||||
#. Label of the chapters (Table) field in DocType 'LMS Course'
|
||||
#. Group in LMS Course's connections
|
||||
@@ -1157,7 +1157,7 @@ msgstr "اسم دوره"
|
||||
|
||||
#: frontend/src/pages/CourseDetail.vue:74
|
||||
msgid "Course Outline"
|
||||
msgstr ""
|
||||
msgstr "طرح کلی دوره"
|
||||
|
||||
#. Label of the course_price (Currency) field in DocType 'LMS Course'
|
||||
#: frontend/src/pages/CourseForm.vue:219
|
||||
@@ -1197,7 +1197,7 @@ msgstr "دوره قبلاً به دسته اضافه شده است."
|
||||
|
||||
#: frontend/src/pages/CourseForm.vue:457
|
||||
msgid "Course deleted successfully"
|
||||
msgstr ""
|
||||
msgstr "دوره با موفقیت حذف شد"
|
||||
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:58
|
||||
msgid "Course {0} has already been added to this batch."
|
||||
@@ -1366,19 +1366,19 @@ msgstr "حذف"
|
||||
|
||||
#: frontend/src/components/CourseOutline.vue:52
|
||||
msgid "Delete Chapter"
|
||||
msgstr ""
|
||||
msgstr "حذف فصل"
|
||||
|
||||
#: frontend/src/pages/CourseForm.vue:464
|
||||
msgid "Delete Course"
|
||||
msgstr ""
|
||||
msgstr "حذف دوره"
|
||||
|
||||
#: frontend/src/components/CourseOutline.vue:287
|
||||
msgid "Delete this chapter?"
|
||||
msgstr ""
|
||||
msgstr "این فصل حذف شود؟"
|
||||
|
||||
#: frontend/src/components/CourseOutline.vue:229
|
||||
msgid "Delete this lesson?"
|
||||
msgstr ""
|
||||
msgstr "این درس حذف شود؟"
|
||||
|
||||
#: frontend/src/pages/CourseForm.vue:465
|
||||
msgid "Deleting the course will also delete all its chapters and lessons. Are you sure you want to delete this course?"
|
||||
@@ -1386,11 +1386,11 @@ msgstr ""
|
||||
|
||||
#: frontend/src/components/CourseOutline.vue:288
|
||||
msgid "Deleting this chapter will also delete all its lessons and permanently remove it from the course. This action cannot be undone. Are you sure you want to continue?"
|
||||
msgstr ""
|
||||
msgstr "با حذف این فصل، تمام دروس آن نیز حذف می شود و برای همیشه از دوره حذف می شود. این عمل قابل بازگشت نیست. آیا مطمئن هستید که می خواهید ادامه دهید؟"
|
||||
|
||||
#: frontend/src/components/CourseOutline.vue:230
|
||||
msgid "Deleting this lesson will permanently remove it from the course. This action cannot be undone. Are you sure you want to continue?"
|
||||
msgstr ""
|
||||
msgstr "حذف این درس آن را برای همیشه از دوره حذف می کند. این عمل قابل بازگشت نیست. آیا مطمئن هستید که می خواهید ادامه دهید؟"
|
||||
|
||||
#. Label of the description (Text Editor) field in DocType 'Job Opportunity'
|
||||
#. Label of the description (Small Text) field in DocType 'Certification'
|
||||
@@ -1763,7 +1763,7 @@ msgstr "رویداد"
|
||||
|
||||
#: frontend/src/pages/BatchForm.vue:156
|
||||
msgid "Example: IST (+5:30)"
|
||||
msgstr ""
|
||||
msgstr "مثال: IST (+5:30)"
|
||||
|
||||
#. Label of the exercise (Link) field in DocType 'Exercise Latest Submission'
|
||||
#. Label of the exercise (Link) field in DocType 'Exercise Submission'
|
||||
@@ -3287,7 +3287,7 @@ msgstr ""
|
||||
|
||||
#: frontend/src/components/Modals/ChapterModal.vue:223
|
||||
msgid "Only zip files are allowed"
|
||||
msgstr ""
|
||||
msgstr "فقط فایل های zip مجاز هستند"
|
||||
|
||||
#. Option for the 'Status' (Select) field in DocType 'Job Opportunity'
|
||||
#. Option for the 'Membership' (Select) field in DocType 'LMS Batch Old'
|
||||
@@ -3551,7 +3551,7 @@ msgstr ""
|
||||
|
||||
#: lms/overrides/user.py:240
|
||||
msgid "Please ask your administrator to verify your sign-up"
|
||||
msgstr "لطفاً از سرپرست خود بخواهید ثبت نام شما را تأیید کند"
|
||||
msgstr "لطفاً از ادمین خود بخواهید ثبت نام شما را تأیید کند"
|
||||
|
||||
#: lms/overrides/user.py:238
|
||||
msgid "Please check your email for verification"
|
||||
@@ -3567,7 +3567,7 @@ msgstr ""
|
||||
|
||||
#: frontend/src/components/CourseOutline.vue:312
|
||||
msgid "Please enroll for this course to view this lesson"
|
||||
msgstr ""
|
||||
msgstr "لطفا برای مشاهده این درس در این دوره ثبت نام کنید"
|
||||
|
||||
#: frontend/src/components/Quiz.vue:13
|
||||
msgid "Please ensure that you complete all the questions in {0} minutes."
|
||||
@@ -4778,7 +4778,7 @@ msgstr ""
|
||||
|
||||
#: frontend/src/pages/SCORMChapter.vue:198
|
||||
msgid "This is a chapter in the course {0}"
|
||||
msgstr ""
|
||||
msgstr "این یک فصل از دوره {0} است"
|
||||
|
||||
#: lms/lms/widgets/CourseOutline.html:62
|
||||
msgid "This lesson is not available for preview. As you are the Instructor of the course only you can see it."
|
||||
@@ -4898,7 +4898,7 @@ msgstr "عنوان"
|
||||
|
||||
#: frontend/src/components/Modals/ChapterModal.vue:169
|
||||
msgid "Title is required"
|
||||
msgstr ""
|
||||
msgstr "عنوان الزامی است"
|
||||
|
||||
#. Label of the unavailable_to (Date) field in DocType 'Course Evaluator'
|
||||
#: frontend/src/pages/ProfileEvaluator.vue:98
|
||||
@@ -5253,11 +5253,11 @@ msgstr ""
|
||||
|
||||
#: frontend/src/components/CourseOutline.vue:311
|
||||
msgid "You are not enrolled"
|
||||
msgstr ""
|
||||
msgstr "شما ثبت نام نکرده اید"
|
||||
|
||||
#: frontend/src/pages/SCORMChapter.vue:22
|
||||
msgid "You are not enrolled in this course. Please enroll to access this lesson."
|
||||
msgstr ""
|
||||
msgstr "شما در این دوره ثبت نام نکرده اید. لطفا برای دسترسی به این دوره ثبت نام کنید."
|
||||
|
||||
#: frontend/src/pages/Courses.vue:134
|
||||
msgid "You can add chapters and lessons to it."
|
||||
@@ -5489,7 +5489,7 @@ msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_certificate/lms_certificate.py:59
|
||||
msgid "{0} is already certified for the course {1}"
|
||||
msgstr ""
|
||||
msgstr "{0} قبلاً برای دوره {1} تایید شده است"
|
||||
|
||||
#: lms/lms/notification/certificate_request_reminder/certificate_request_reminder.html:5
|
||||
msgid "{0} is your evaluator"
|
||||
|
||||
@@ -7,8 +7,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Frappe LMS VERSION\n"
|
||||
"Report-Msgid-Bugs-To: jannat@frappe.io\n"
|
||||
"POT-Creation-Date: 2024-11-15 16:04+0000\n"
|
||||
"PO-Revision-Date: 2024-11-15 16:04+0000\n"
|
||||
"POT-Creation-Date: 2024-11-22 16:05+0000\n"
|
||||
"PO-Revision-Date: 2024-11-22 16:05+0000\n"
|
||||
"Last-Translator: jannat@frappe.io\n"
|
||||
"Language-Team: jannat@frappe.io\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -20,6 +20,11 @@ msgstr ""
|
||||
msgid " Please evaluate and grade it."
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/Programs.vue:41
|
||||
#, python-format
|
||||
msgid "% completed"
|
||||
msgstr ""
|
||||
|
||||
#. Paragraph text in the LMS Workspace
|
||||
#: lms/lms/workspace/lms/lms.json
|
||||
msgid "<a href=\"/app/lms-settings/LMS%20Settings\">LMS Settings</a>"
|
||||
@@ -97,7 +102,8 @@ msgstr ""
|
||||
#: frontend/src/components/BatchStudents.vue:6
|
||||
#: frontend/src/components/Categories.vue:26
|
||||
#: frontend/src/components/LiveClass.vue:11
|
||||
#: frontend/src/components/Members.vue:43
|
||||
#: frontend/src/components/Members.vue:43 frontend/src/pages/ProgramForm.vue:30
|
||||
#: frontend/src/pages/ProgramForm.vue:91 frontend/src/pages/ProgramForm.vue:136
|
||||
msgid "Add"
|
||||
msgstr ""
|
||||
|
||||
@@ -127,12 +133,20 @@ msgstr ""
|
||||
msgid "Add a Student"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/OnboardingBanner.vue:50
|
||||
msgid "Add a chapter"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/Modals/BatchCourseModal.vue:5
|
||||
msgid "Add a course"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/OnboardingBanner.vue:73
|
||||
msgid "Add a lesson"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/Modals/Question.vue:141
|
||||
#: frontend/src/pages/QuizForm.vue:181
|
||||
#: frontend/src/pages/QuizForm.vue:182
|
||||
msgid "Add a new question"
|
||||
msgstr ""
|
||||
|
||||
@@ -148,7 +162,7 @@ msgstr ""
|
||||
msgid "Add an existing question"
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_question/lms_question.py:60
|
||||
#: lms/lms/doctype/lms_question/lms_question.py:66
|
||||
msgid "Add at least one possible answer for this question: {0}"
|
||||
msgstr ""
|
||||
|
||||
@@ -251,6 +265,14 @@ msgstr ""
|
||||
msgid "Amount Field"
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:70
|
||||
msgid "Amount and currency are required for paid batches."
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_course/lms_course.py:57
|
||||
msgid "Amount and currency are required for paid courses."
|
||||
msgstr ""
|
||||
|
||||
#. Label of the amount_with_gst (Currency) field in DocType 'LMS Payment'
|
||||
#: lms/lms/doctype/lms_payment/lms_payment.json
|
||||
msgid "Amount with GST"
|
||||
@@ -344,7 +366,7 @@ msgstr ""
|
||||
msgid "Assessment added successfully"
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:75
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:80
|
||||
msgid "Assessment {0} has already been added to this batch."
|
||||
msgstr ""
|
||||
|
||||
@@ -395,7 +417,7 @@ msgstr ""
|
||||
msgid "Assignment will appear at the bottom of the lesson."
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_question/lms_question.py:42
|
||||
#: lms/lms/doctype/lms_question/lms_question.py:43
|
||||
msgid "At least one option must be correct for this question."
|
||||
msgstr ""
|
||||
|
||||
@@ -538,7 +560,7 @@ msgstr ""
|
||||
msgid "Batch Updated"
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:40
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:41
|
||||
msgid "Batch end date cannot be before the batch start date"
|
||||
msgstr ""
|
||||
|
||||
@@ -725,11 +747,11 @@ msgstr ""
|
||||
msgid "Chapter Reference"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/Modals/ChapterModal.vue:149
|
||||
#: frontend/src/components/Modals/ChapterModal.vue:154
|
||||
msgid "Chapter added successfully"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/Modals/ChapterModal.vue:193
|
||||
#: frontend/src/components/Modals/ChapterModal.vue:198
|
||||
msgid "Chapter updated successfully"
|
||||
msgstr ""
|
||||
|
||||
@@ -762,7 +784,7 @@ msgstr ""
|
||||
msgid "Choices"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/Quiz.vue:578 lms/templates/quiz/quiz.html:53
|
||||
#: frontend/src/components/Quiz.vue:581 lms/templates/quiz/quiz.html:53
|
||||
msgid "Choose all answers that apply"
|
||||
msgstr ""
|
||||
|
||||
@@ -770,7 +792,7 @@ msgstr ""
|
||||
msgid "Choose an icon"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/Quiz.vue:579 lms/templates/quiz/quiz.html:53
|
||||
#: frontend/src/components/Quiz.vue:582 lms/templates/quiz/quiz.html:53
|
||||
msgid "Choose one answer"
|
||||
msgstr ""
|
||||
|
||||
@@ -986,7 +1008,7 @@ msgid "Contact the Administrator to enroll for this course."
|
||||
msgstr ""
|
||||
|
||||
#. Label of the content (Text) field in DocType 'Course Lesson'
|
||||
#: frontend/src/pages/LessonForm.vue:58
|
||||
#: frontend/src/pages/LessonForm.vue:62
|
||||
#: lms/lms/doctype/course_lesson/course_lesson.json
|
||||
msgid "Content"
|
||||
msgstr ""
|
||||
@@ -1055,6 +1077,7 @@ msgstr ""
|
||||
#. Label of the course (Link) field in DocType 'LMS Enrollment'
|
||||
#. Label of the course (Link) field in DocType 'LMS Exercise'
|
||||
#. Label of the course (Link) field in DocType 'LMS Mentor Request'
|
||||
#. Label of the course (Link) field in DocType 'LMS Program Course'
|
||||
#. Label of the course (Link) field in DocType 'LMS Quiz'
|
||||
#. Label of the course (Link) field in DocType 'LMS Quiz Submission'
|
||||
#. Label of the course (Link) field in DocType 'Related Courses'
|
||||
@@ -1084,6 +1107,7 @@ msgstr ""
|
||||
#: lms/lms/doctype/lms_enrollment/lms_enrollment.json
|
||||
#: lms/lms/doctype/lms_exercise/lms_exercise.json
|
||||
#: lms/lms/doctype/lms_mentor_request/lms_mentor_request.json
|
||||
#: lms/lms/doctype/lms_program_course/lms_program_course.json
|
||||
#: lms/lms/doctype/lms_quiz/lms_quiz.json
|
||||
#: lms/lms/doctype/lms_quiz_submission/lms_quiz_submission.json
|
||||
#: lms/lms/doctype/related_courses/related_courses.json
|
||||
@@ -1112,6 +1136,7 @@ msgstr ""
|
||||
#. Name of a role
|
||||
#: frontend/src/pages/ProfileRoles.vue:16
|
||||
#: lms/lms/doctype/lms_course/lms_course.json
|
||||
#: lms/lms/doctype/lms_program/lms_program.json
|
||||
#: lms/lms/doctype/lms_question/lms_question.json
|
||||
#: lms/lms/doctype/lms_quiz/lms_quiz.json
|
||||
msgid "Course Creator"
|
||||
@@ -1182,22 +1207,33 @@ msgstr ""
|
||||
#. Label of the course_title (Data) field in DocType 'Course Chapter'
|
||||
#. Label of the course_title (Data) field in DocType 'LMS Certificate'
|
||||
#. Label of the course_title (Data) field in DocType 'LMS Certificate Request'
|
||||
#. Label of the course_title (Data) field in DocType 'LMS Program Course'
|
||||
#: lms/lms/doctype/batch_course/batch_course.json
|
||||
#: lms/lms/doctype/course_chapter/course_chapter.json
|
||||
#: lms/lms/doctype/lms_certificate/lms_certificate.json
|
||||
#: lms/lms/doctype/lms_certificate_request/lms_certificate_request.json
|
||||
#: lms/lms/doctype/lms_program_course/lms_program_course.json
|
||||
msgid "Course Title"
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:340
|
||||
#: frontend/src/pages/ProgramForm.vue:226
|
||||
msgid "Course added to program"
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:345
|
||||
msgid "Course already added to the batch."
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/CourseForm.vue:457
|
||||
#: frontend/src/pages/CourseForm.vue:460
|
||||
msgid "Course deleted successfully"
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:58
|
||||
#: frontend/src/pages/ProgramForm.vue:295
|
||||
msgid "Course moved successfully"
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:59
|
||||
#: lms/lms/doctype/lms_program/lms_program.py:19
|
||||
msgid "Course {0} has already been added to this batch."
|
||||
msgstr ""
|
||||
|
||||
@@ -1232,6 +1268,7 @@ msgid "Cover Image"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/Modals/ChapterModal.vue:9
|
||||
#: frontend/src/pages/Programs.vue:99
|
||||
msgid "Create"
|
||||
msgstr ""
|
||||
|
||||
@@ -1247,7 +1284,7 @@ msgstr ""
|
||||
msgid "Create a Batch"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/Courses.vue:131 lms/templates/onboarding_header.html:19
|
||||
#: frontend/src/pages/Courses.vue:132 lms/templates/onboarding_header.html:19
|
||||
msgid "Create a Course"
|
||||
msgstr ""
|
||||
|
||||
@@ -1255,6 +1292,10 @@ msgstr ""
|
||||
msgid "Create a Live Class"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/OnboardingBanner.vue:27
|
||||
msgid "Create a course"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/Modals/Question.vue:31
|
||||
msgid "Create a new question"
|
||||
msgstr ""
|
||||
@@ -1358,7 +1399,7 @@ msgstr ""
|
||||
|
||||
#: frontend/src/components/CourseOutline.vue:235
|
||||
#: frontend/src/components/CourseOutline.vue:293
|
||||
#: frontend/src/pages/CourseForm.vue:15 frontend/src/pages/CourseForm.vue:470
|
||||
#: frontend/src/pages/CourseForm.vue:15 frontend/src/pages/CourseForm.vue:473
|
||||
msgid "Delete"
|
||||
msgstr ""
|
||||
|
||||
@@ -1366,7 +1407,7 @@ msgstr ""
|
||||
msgid "Delete Chapter"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/CourseForm.vue:464
|
||||
#: frontend/src/pages/CourseForm.vue:467
|
||||
msgid "Delete Course"
|
||||
msgstr ""
|
||||
|
||||
@@ -1378,7 +1419,7 @@ msgstr ""
|
||||
msgid "Delete this lesson?"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/CourseForm.vue:465
|
||||
#: frontend/src/pages/CourseForm.vue:468
|
||||
msgid "Deleting the course will also delete all its chapters and lessons. Are you sure you want to delete this course?"
|
||||
msgstr ""
|
||||
|
||||
@@ -1468,7 +1509,7 @@ msgstr ""
|
||||
msgid "Dream Companies"
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_question/lms_question.py:32
|
||||
#: lms/lms/doctype/lms_question/lms_question.py:33
|
||||
msgid "Duplicate options found for this question."
|
||||
msgstr ""
|
||||
|
||||
@@ -1506,7 +1547,7 @@ msgstr ""
|
||||
#: frontend/src/components/CourseCardOverlay.vue:86
|
||||
#: frontend/src/components/Modals/ChapterModal.vue:9
|
||||
#: frontend/src/pages/JobDetail.vue:31 frontend/src/pages/Lesson.vue:65
|
||||
#: frontend/src/pages/Profile.vue:32
|
||||
#: frontend/src/pages/Profile.vue:32 frontend/src/pages/Programs.vue:55
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
|
||||
@@ -1519,7 +1560,7 @@ msgstr ""
|
||||
msgid "Edit Profile"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/QuizForm.vue:180
|
||||
#: frontend/src/pages/QuizForm.vue:181
|
||||
msgid "Edit the question"
|
||||
msgstr ""
|
||||
|
||||
@@ -1580,6 +1621,15 @@ msgstr ""
|
||||
msgid "Enable Google API in Google Settings to send calendar invites for evaluations."
|
||||
msgstr ""
|
||||
|
||||
#. Label of the enable_learning_paths (Check) field in DocType 'LMS Settings'
|
||||
#: lms/lms/doctype/lms_settings/lms_settings.json
|
||||
msgid "Enable Learning Paths"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/Modals/ChapterModal.vue:24
|
||||
msgid "Enable this only if you want to upload a SCORM package as a chapter."
|
||||
msgstr ""
|
||||
|
||||
#. Label of the enabled (Check) field in DocType 'LMS Badge'
|
||||
#: lms/lms/doctype/lms_badge/lms_badge.json
|
||||
msgid "Enabled"
|
||||
@@ -1632,7 +1682,7 @@ msgstr ""
|
||||
msgid "Enrolled successfully"
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:98
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:103
|
||||
msgid "Enrollment Confirmation for the Next Training Batch"
|
||||
msgstr ""
|
||||
|
||||
@@ -1641,7 +1691,7 @@ msgstr ""
|
||||
msgid "Enrollment Count"
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/utils.py:1702
|
||||
#: lms/lms/utils.py:1705
|
||||
msgid "Enrollment Failed"
|
||||
msgstr ""
|
||||
|
||||
@@ -1670,14 +1720,14 @@ msgid "Enter the correct answer"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/Modals/AnnouncementModal.vue:105
|
||||
#: frontend/src/components/Modals/ChapterModal.vue:154
|
||||
#: frontend/src/components/Modals/ChapterModal.vue:161
|
||||
#: frontend/src/components/Modals/ChapterModal.vue:197
|
||||
#: frontend/src/components/Modals/ChapterModal.vue:159
|
||||
#: frontend/src/components/Modals/ChapterModal.vue:166
|
||||
#: frontend/src/components/Modals/ChapterModal.vue:202
|
||||
#: frontend/src/components/Modals/Question.vue:246
|
||||
#: frontend/src/components/Modals/Question.vue:266
|
||||
#: frontend/src/components/Modals/Question.vue:323
|
||||
#: frontend/src/pages/Billing.vue:264 frontend/src/pages/QuizForm.vue:349
|
||||
#: frontend/src/pages/QuizForm.vue:364
|
||||
#: frontend/src/pages/Billing.vue:264 frontend/src/pages/QuizForm.vue:350
|
||||
#: frontend/src/pages/QuizForm.vue:365
|
||||
#: frontend/src/pages/QuizSubmission.vue:117
|
||||
msgid "Error"
|
||||
msgstr ""
|
||||
@@ -1706,7 +1756,7 @@ msgstr ""
|
||||
msgid "Evaluation Request"
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:82
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:87
|
||||
msgid "Evaluation end date cannot be less than the batch end date."
|
||||
msgstr ""
|
||||
|
||||
@@ -1872,7 +1922,7 @@ msgstr ""
|
||||
msgid "Flexible Time"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/QuizForm.vue:462
|
||||
#: frontend/src/pages/QuizForm.vue:463
|
||||
msgid "Form to create and edit quizzes"
|
||||
msgstr ""
|
||||
|
||||
@@ -1920,7 +1970,9 @@ msgid "From Date"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the full_name (Data) field in DocType 'Invite Request'
|
||||
#. Label of the full_name (Data) field in DocType 'LMS Program Member'
|
||||
#: lms/lms/doctype/invite_request/invite_request.json
|
||||
#: lms/lms/doctype/lms_program_member/lms_program_member.json
|
||||
#: lms/templates/signup-form.html:5
|
||||
msgid "Full Name"
|
||||
msgstr ""
|
||||
@@ -1952,6 +2004,11 @@ msgstr ""
|
||||
msgid "GSTIN"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the general_tab (Tab Break) field in DocType 'LMS Settings'
|
||||
#: lms/lms/doctype/lms_settings/lms_settings.json
|
||||
msgid "General"
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_certificate_request/lms_certificate_request.js:18
|
||||
msgid "Generate Google Meet Link"
|
||||
msgstr ""
|
||||
@@ -2083,7 +2140,7 @@ msgstr ""
|
||||
msgid "I am unavailable"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/QuizForm.vue:383
|
||||
#: frontend/src/pages/QuizForm.vue:384
|
||||
msgid "ID"
|
||||
msgstr ""
|
||||
|
||||
@@ -2224,7 +2281,7 @@ msgstr ""
|
||||
|
||||
#. Label of the instructor_notes (Markdown Editor) field in DocType 'Course
|
||||
#. Lesson'
|
||||
#: frontend/src/pages/Lesson.vue:128 frontend/src/pages/LessonForm.vue:38
|
||||
#: frontend/src/pages/Lesson.vue:128 frontend/src/pages/LessonForm.vue:42
|
||||
#: lms/lms/doctype/course_lesson/course_lesson.json
|
||||
msgid "Instructor Notes"
|
||||
msgstr ""
|
||||
@@ -2300,7 +2357,6 @@ msgstr ""
|
||||
|
||||
#. Label of the is_scorm_package (Check) field in DocType 'Course Chapter'
|
||||
#. Label of the is_scorm_package (Check) field in DocType 'Course Lesson'
|
||||
#: frontend/src/components/Modals/ChapterModal.vue:26
|
||||
#: lms/lms/doctype/course_chapter/course_chapter.json
|
||||
#: lms/lms/doctype/course_lesson/course_lesson.json
|
||||
msgid "Is SCORM Package"
|
||||
@@ -2330,6 +2386,10 @@ msgstr ""
|
||||
msgid "Items in Sidebar"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/ProgramForm.vue:269
|
||||
msgid "Items removed successfully"
|
||||
msgstr ""
|
||||
|
||||
#: lms/templates/signup-form.html:6
|
||||
msgid "Jane Doe"
|
||||
msgstr ""
|
||||
@@ -2529,6 +2589,21 @@ msgstr ""
|
||||
msgid "LMS Payment"
|
||||
msgstr ""
|
||||
|
||||
#. Name of a DocType
|
||||
#: lms/lms/doctype/lms_program/lms_program.json
|
||||
msgid "LMS Program"
|
||||
msgstr ""
|
||||
|
||||
#. Name of a DocType
|
||||
#: lms/lms/doctype/lms_program_course/lms_program_course.json
|
||||
msgid "LMS Program Course"
|
||||
msgstr ""
|
||||
|
||||
#. Name of a DocType
|
||||
#: lms/lms/doctype/lms_program_member/lms_program_member.json
|
||||
msgid "LMS Program Member"
|
||||
msgstr ""
|
||||
|
||||
#. Name of a DocType
|
||||
#: lms/lms/doctype/lms_question/lms_question.json
|
||||
msgid "LMS Question"
|
||||
@@ -2588,6 +2663,7 @@ msgstr ""
|
||||
#: lms/lms/doctype/lms_live_class/lms_live_class.json
|
||||
#: lms/lms/doctype/lms_quiz/lms_quiz.json
|
||||
#: lms/lms/doctype/lms_quiz_submission/lms_quiz_submission.json
|
||||
#: lms/lms/doctype/lms_settings/lms_settings.json
|
||||
#: lms/lms/doctype/lms_source/lms_source.json
|
||||
#: lms/lms/doctype/user_skill/user_skill.json
|
||||
msgid "LMS Student"
|
||||
@@ -2810,7 +2886,7 @@ msgstr ""
|
||||
#. Label of the marks (Int) field in DocType 'LMS Quiz Result'
|
||||
#: frontend/src/components/Modals/Question.vue:50
|
||||
#: frontend/src/components/Modals/Question.vue:96
|
||||
#: frontend/src/components/Quiz.vue:94 frontend/src/pages/QuizForm.vue:393
|
||||
#: frontend/src/components/Quiz.vue:94 frontend/src/pages/QuizForm.vue:394
|
||||
#: frontend/src/pages/QuizSubmission.vue:52
|
||||
#: lms/lms/doctype/lms_quiz_question/lms_quiz_question.json
|
||||
#: lms/lms/doctype/lms_quiz_result/lms_quiz_result.json
|
||||
@@ -2865,6 +2941,7 @@ msgstr ""
|
||||
#. Option for the 'Role' (Select) field in DocType 'LMS Enrollment'
|
||||
#. Label of the member (Link) field in DocType 'LMS Mentor Request'
|
||||
#. Label of the member (Link) field in DocType 'LMS Payment'
|
||||
#. Label of the member (Link) field in DocType 'LMS Program Member'
|
||||
#. Label of the member (Link) field in DocType 'LMS Quiz Submission'
|
||||
#: frontend/src/pages/QuizSubmission.vue:27
|
||||
#: frontend/src/pages/QuizSubmissionList.vue:77
|
||||
@@ -2879,6 +2956,7 @@ msgstr ""
|
||||
#: lms/lms/doctype/lms_enrollment/lms_enrollment.json
|
||||
#: lms/lms/doctype/lms_mentor_request/lms_mentor_request.json
|
||||
#: lms/lms/doctype/lms_payment/lms_payment.json
|
||||
#: lms/lms/doctype/lms_program_member/lms_program_member.json
|
||||
#: lms/lms/doctype/lms_quiz_submission/lms_quiz_submission.json
|
||||
#: lms/lms/report/course_progress_summary/course_progress_summary.py:64
|
||||
msgid "Member"
|
||||
@@ -2928,6 +3006,14 @@ msgstr ""
|
||||
msgid "Member Type"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/ProgramForm.vue:248
|
||||
msgid "Member added to program"
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_program/lms_program.py:29
|
||||
msgid "Member {0} has already been added to this batch."
|
||||
msgstr ""
|
||||
|
||||
#. Group in LMS Batch Old's connections
|
||||
#: lms/lms/doctype/lms_batch_old/lms_batch_old.json
|
||||
msgid "Members"
|
||||
@@ -3000,6 +3086,10 @@ msgstr ""
|
||||
msgid "Milestones"
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_question/lms_question.py:48
|
||||
msgid "Minimum two options are required for multiple choice questions."
|
||||
msgstr ""
|
||||
|
||||
#. Name of a role
|
||||
#: frontend/src/pages/ProfileRoles.vue:10
|
||||
#: lms/lms/doctype/course_evaluator/course_evaluator.json
|
||||
@@ -3011,6 +3101,7 @@ msgstr ""
|
||||
#: lms/lms/doctype/lms_certificate_request/lms_certificate_request.json
|
||||
#: lms/lms/doctype/lms_enrollment/lms_enrollment.json
|
||||
#: lms/lms/doctype/lms_live_class/lms_live_class.json
|
||||
#: lms/lms/doctype/lms_program/lms_program.json
|
||||
#: lms/lms/doctype/lms_question/lms_question.json
|
||||
#: lms/lms/doctype/lms_quiz/lms_quiz.json
|
||||
#: lms/lms/doctype/lms_source/lms_source.json
|
||||
@@ -3055,8 +3146,8 @@ msgid "My calendar"
|
||||
msgstr ""
|
||||
|
||||
#. Option for the 'Event' (Select) field in DocType 'LMS Badge'
|
||||
#: frontend/src/pages/Batches.vue:30 frontend/src/pages/Courses.vue:44
|
||||
#: lms/lms/doctype/lms_badge/lms_badge.json
|
||||
#: frontend/src/pages/Batches.vue:30 frontend/src/pages/Courses.vue:45
|
||||
#: frontend/src/pages/Programs.vue:14 lms/lms/doctype/lms_badge/lms_badge.json
|
||||
msgid "New"
|
||||
msgstr ""
|
||||
|
||||
@@ -3080,11 +3171,23 @@ msgstr ""
|
||||
msgid "New Job Applicant"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/Programs.vue:96
|
||||
msgid "New Program"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/ProgramForm.vue:132
|
||||
msgid "New Program Course"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/ProgramForm.vue:133
|
||||
msgid "New Program Member"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/QuizForm.vue:122
|
||||
msgid "New Question"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/QuizForm.vue:453 frontend/src/pages/QuizForm.vue:461
|
||||
#: frontend/src/pages/QuizForm.vue:454 frontend/src/pages/QuizForm.vue:462
|
||||
#: frontend/src/pages/Quizzes.vue:18
|
||||
msgid "New Quiz"
|
||||
msgstr ""
|
||||
@@ -3147,10 +3250,14 @@ msgstr ""
|
||||
msgid "No courses created"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/Courses.vue:146
|
||||
#: frontend/src/pages/Courses.vue:147
|
||||
msgid "No courses found"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/Programs.vue:72
|
||||
msgid "No courses in this program"
|
||||
msgstr ""
|
||||
|
||||
#: lms/templates/courses_under_review.html:14
|
||||
msgid "No courses under review"
|
||||
msgstr ""
|
||||
@@ -3167,6 +3274,10 @@ msgstr ""
|
||||
msgid "No live classes scheduled"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/Programs.vue:82
|
||||
msgid "No programs found"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/Quizzes.vue:56
|
||||
msgid "No quizzes found"
|
||||
msgstr ""
|
||||
@@ -3187,7 +3298,7 @@ msgstr ""
|
||||
msgid "No {0} batches"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/Courses.vue:106
|
||||
#: frontend/src/pages/Courses.vue:107
|
||||
msgid "No {0} courses"
|
||||
msgstr ""
|
||||
|
||||
@@ -3275,15 +3386,19 @@ msgstr ""
|
||||
msgid "Online"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/ProgramForm.vue:156
|
||||
msgid "Only courses for which self learning is disabled can be added to program."
|
||||
msgstr ""
|
||||
|
||||
#: lms/templates/assignment.html:6
|
||||
msgid "Only files of type {0} will be accepted."
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/CourseForm.vue:494 frontend/src/utils/index.js:518
|
||||
#: frontend/src/pages/CourseForm.vue:497 frontend/src/utils/index.js:518
|
||||
msgid "Only image file is allowed."
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/Modals/ChapterModal.vue:223
|
||||
#: frontend/src/components/Modals/ChapterModal.vue:220
|
||||
msgid "Only zip files are allowed"
|
||||
msgstr ""
|
||||
|
||||
@@ -3559,7 +3674,11 @@ msgstr ""
|
||||
msgid "Please click on the following button to set your new password"
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:251
|
||||
#: lms/lms/utils.py:1817 lms/lms/utils.py:1821
|
||||
msgid "Please complete the previous courses in the program to enroll in this course."
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:256
|
||||
msgid "Please enable Zoom Settings to use this feature."
|
||||
msgstr ""
|
||||
|
||||
@@ -3571,11 +3690,19 @@ msgstr ""
|
||||
msgid "Please ensure that you complete all the questions in {0} minutes."
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/Modals/LiveClassModal.vue:164
|
||||
msgid "Please enter a title."
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_assignment_submission/lms_assignment_submission.py:38
|
||||
#: lms/lms/doctype/lms_assignment_submission/lms_assignment_submission.py:98
|
||||
msgid "Please enter a valid URL."
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/Modals/LiveClassModal.vue:176
|
||||
msgid "Please enter a valid time in the format HH:mm."
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_assignment_submission/lms_assignment_submission.py:92
|
||||
msgid "Please enter the URL for assignment submission."
|
||||
msgstr ""
|
||||
@@ -3584,11 +3711,11 @@ msgstr ""
|
||||
msgid "Please enter your answer"
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:65
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:66
|
||||
msgid "Please install the Payments app to create a paid batches."
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_course/lms_course.py:52
|
||||
#: lms/lms/doctype/lms_course/lms_course.py:53
|
||||
msgid "Please install the Payments app to create a paid courses."
|
||||
msgstr ""
|
||||
|
||||
@@ -3613,11 +3740,31 @@ msgstr ""
|
||||
msgid "Please prepare well and be on time for the evaluations."
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/Modals/LiveClassModal.vue:167
|
||||
msgid "Please select a date."
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/Modals/LiveClassModal.vue:191
|
||||
msgid "Please select a duration."
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/Modals/LiveClassModal.vue:188
|
||||
msgid "Please select a future date and time."
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/Modals/LiveClassModal.vue:170
|
||||
msgid "Please select a time."
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/Modals/LiveClassModal.vue:173
|
||||
msgid "Please select a timezone."
|
||||
msgstr ""
|
||||
|
||||
#: lms/templates/emails/job_report.html:6
|
||||
msgid "Please take appropriate action at {0}"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/Modals/ChapterModal.vue:172
|
||||
#: frontend/src/components/Modals/ChapterModal.vue:177
|
||||
msgid "Please upload a SCORM package"
|
||||
msgstr ""
|
||||
|
||||
@@ -3754,8 +3901,30 @@ msgstr ""
|
||||
msgid "Profile Image"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/ProgramForm.vue:154
|
||||
msgid "Program Course"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the program_courses (Table) field in DocType 'LMS Program'
|
||||
#: frontend/src/pages/ProgramForm.vue:17
|
||||
#: lms/lms/doctype/lms_program/lms_program.json
|
||||
msgid "Program Courses"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/ProgramForm.vue:169
|
||||
msgid "Program Member"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the program_members (Table) field in DocType 'LMS Program'
|
||||
#: frontend/src/pages/ProgramForm.vue:78
|
||||
#: lms/lms/doctype/lms_program/lms_program.json
|
||||
msgid "Program Members"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the progress (Float) field in DocType 'LMS Enrollment'
|
||||
#. Label of the progress (Int) field in DocType 'LMS Program Member'
|
||||
#: lms/lms/doctype/lms_enrollment/lms_enrollment.json
|
||||
#: lms/lms/doctype/lms_program_member/lms_program_member.json
|
||||
msgid "Progress"
|
||||
msgstr ""
|
||||
|
||||
@@ -3805,7 +3974,7 @@ msgstr ""
|
||||
#. Label of the question (Text) field in DocType 'LMS Quiz Result'
|
||||
#: frontend/src/components/Modals/Question.vue:38
|
||||
#: frontend/src/pages/AssignmentSubmission.vue:26
|
||||
#: frontend/src/pages/QuizForm.vue:388
|
||||
#: frontend/src/pages/QuizForm.vue:389
|
||||
#: lms/lms/doctype/course_lesson/course_lesson.json
|
||||
#: lms/lms/doctype/lms_assignment/lms_assignment.json
|
||||
#: lms/lms/doctype/lms_assignment_submission/lms_assignment_submission.json
|
||||
@@ -3851,7 +4020,7 @@ msgstr ""
|
||||
msgid "Questions"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/QuizForm.vue:430
|
||||
#: frontend/src/pages/QuizForm.vue:431
|
||||
msgid "Questions deleted successfully"
|
||||
msgstr ""
|
||||
|
||||
@@ -3889,7 +4058,7 @@ msgstr ""
|
||||
msgid "Quiz Title"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/QuizForm.vue:342
|
||||
#: frontend/src/pages/QuizForm.vue:343
|
||||
msgid "Quiz created successfully"
|
||||
msgstr ""
|
||||
|
||||
@@ -3897,7 +4066,7 @@ msgstr ""
|
||||
msgid "Quiz is not available to Guest users. Please login to continue."
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/QuizForm.vue:361
|
||||
#: frontend/src/pages/QuizForm.vue:362
|
||||
msgid "Quiz updated successfully"
|
||||
msgstr ""
|
||||
|
||||
@@ -3906,7 +4075,7 @@ msgstr ""
|
||||
msgid "Quiz will appear at the bottom of the lesson."
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/QuizForm.vue:441 frontend/src/pages/Quizzes.vue:136
|
||||
#: frontend/src/pages/QuizForm.vue:442 frontend/src/pages/Quizzes.vue:136
|
||||
#: frontend/src/pages/Quizzes.vue:146
|
||||
msgid "Quizzes"
|
||||
msgstr ""
|
||||
@@ -4063,19 +4232,19 @@ msgstr ""
|
||||
msgid "Route"
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:194
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:199
|
||||
msgid "Row #{0} Date cannot be outside the batch duration."
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:189
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:194
|
||||
msgid "Row #{0} End time cannot be outside the batch duration."
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:171
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:176
|
||||
msgid "Row #{0} Start time cannot be greater than or equal to end time."
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:180
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:185
|
||||
msgid "Row #{0} Start time cannot be outside the batch duration."
|
||||
msgstr ""
|
||||
|
||||
@@ -4093,6 +4262,7 @@ msgid "SCORM"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the scorm_package (Link) field in DocType 'Course Chapter'
|
||||
#: frontend/src/components/Modals/ChapterModal.vue:22
|
||||
#: lms/lms/doctype/course_chapter/course_chapter.json
|
||||
msgid "SCORM Package"
|
||||
msgstr ""
|
||||
@@ -4115,8 +4285,9 @@ msgstr ""
|
||||
#: frontend/src/components/QuizPlugin.vue:23
|
||||
#: frontend/src/pages/AssignmentSubmission.vue:7
|
||||
#: frontend/src/pages/BatchForm.vue:8 frontend/src/pages/CourseForm.vue:20
|
||||
#: frontend/src/pages/JobCreation.vue:8 frontend/src/pages/LessonForm.vue:10
|
||||
#: frontend/src/pages/QuizForm.vue:34 frontend/src/pages/QuizSubmission.vue:14
|
||||
#: frontend/src/pages/JobCreation.vue:8 frontend/src/pages/LessonForm.vue:14
|
||||
#: frontend/src/pages/ProgramForm.vue:7 frontend/src/pages/QuizForm.vue:34
|
||||
#: frontend/src/pages/QuizSubmission.vue:14
|
||||
#: lms/public/js/common_functions.js:405
|
||||
msgid "Save"
|
||||
msgstr ""
|
||||
@@ -4332,6 +4503,10 @@ msgstr ""
|
||||
msgid "Skip"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/OnboardingBanner.vue:3
|
||||
msgid "Skip Onboarding"
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/course_evaluator/course_evaluator.py:57
|
||||
msgid "Slot Times are overlapping for some schedules."
|
||||
msgstr ""
|
||||
@@ -4505,7 +4680,7 @@ msgstr ""
|
||||
msgid "Student Reviews"
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:47
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:48
|
||||
msgid "Student {0} has already been added to this batch."
|
||||
msgstr ""
|
||||
|
||||
@@ -4573,14 +4748,17 @@ msgstr ""
|
||||
#: frontend/src/components/CourseCardOverlay.vue:161
|
||||
#: frontend/src/components/Modals/AnnouncementModal.vue:99
|
||||
#: frontend/src/components/Modals/AssessmentModal.vue:73
|
||||
#: frontend/src/components/Modals/ChapterModal.vue:148
|
||||
#: frontend/src/components/Modals/ChapterModal.vue:193
|
||||
#: frontend/src/components/Modals/ChapterModal.vue:153
|
||||
#: frontend/src/components/Modals/ChapterModal.vue:198
|
||||
#: frontend/src/components/Modals/Event.vue:255
|
||||
#: frontend/src/components/Modals/Event.vue:310
|
||||
#: frontend/src/components/Modals/Question.vue:261
|
||||
#: frontend/src/components/Modals/Question.vue:312
|
||||
#: frontend/src/pages/CourseForm.vue:457 frontend/src/pages/QuizForm.vue:342
|
||||
#: frontend/src/pages/QuizForm.vue:361 frontend/src/pages/QuizForm.vue:430
|
||||
#: frontend/src/pages/CourseForm.vue:460 frontend/src/pages/ProgramForm.vue:226
|
||||
#: frontend/src/pages/ProgramForm.vue:248
|
||||
#: frontend/src/pages/ProgramForm.vue:269
|
||||
#: frontend/src/pages/ProgramForm.vue:295 frontend/src/pages/QuizForm.vue:343
|
||||
#: frontend/src/pages/QuizForm.vue:362 frontend/src/pages/QuizForm.vue:431
|
||||
msgid "Success"
|
||||
msgstr ""
|
||||
|
||||
@@ -4635,6 +4813,7 @@ msgstr ""
|
||||
#: lms/lms/doctype/lms_live_class/lms_live_class.json
|
||||
#: lms/lms/doctype/lms_mentor_request/lms_mentor_request.json
|
||||
#: lms/lms/doctype/lms_payment/lms_payment.json
|
||||
#: lms/lms/doctype/lms_program/lms_program.json
|
||||
#: lms/lms/doctype/lms_question/lms_question.json
|
||||
#: lms/lms/doctype/lms_quiz/lms_quiz.json
|
||||
#: lms/lms/doctype/lms_quiz_submission/lms_quiz_submission.json
|
||||
@@ -4735,11 +4914,15 @@ msgstr ""
|
||||
msgid "There are no chapters in this course. Create and manage chapters from here."
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/Courses.vue:150
|
||||
#: frontend/src/pages/Courses.vue:151
|
||||
msgid "There are no courses available at the moment. Keep an eye out, fresh learning experiences are on the way soon!"
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:141
|
||||
#: frontend/src/pages/Programs.vue:86
|
||||
msgid "There are no programs available at the moment. Keep an eye out, fresh learning experiences are on the way soon!"
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_batch/lms_batch.py:146
|
||||
msgid "There are no seats available in this batch."
|
||||
msgstr ""
|
||||
|
||||
@@ -4770,11 +4953,11 @@ msgstr ""
|
||||
msgid "This course has:"
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/utils.py:1582
|
||||
#: lms/lms/utils.py:1585
|
||||
msgid "This course is free."
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/SCORMChapter.vue:198
|
||||
#: frontend/src/pages/SCORMChapter.vue:197
|
||||
msgid "This is a chapter in the course {0}"
|
||||
msgstr ""
|
||||
|
||||
@@ -4866,6 +5049,7 @@ msgstr ""
|
||||
#. Label of the title (Data) field in DocType 'LMS Course'
|
||||
#. Label of the title (Data) field in DocType 'LMS Exercise'
|
||||
#. Label of the title (Data) field in DocType 'LMS Live Class'
|
||||
#. Label of the title (Data) field in DocType 'LMS Program'
|
||||
#. Label of the title (Data) field in DocType 'LMS Quiz'
|
||||
#. Label of the title (Data) field in DocType 'LMS Sidebar Item'
|
||||
#. Label of the title (Data) field in DocType 'LMS Timetable Template'
|
||||
@@ -4873,7 +5057,8 @@ msgstr ""
|
||||
#: frontend/src/components/Modals/DiscussionModal.vue:18
|
||||
#: frontend/src/components/Modals/LiveClassModal.vue:23
|
||||
#: frontend/src/pages/BatchForm.vue:20 frontend/src/pages/CourseForm.vue:32
|
||||
#: frontend/src/pages/JobCreation.vue:20 frontend/src/pages/QuizForm.vue:48
|
||||
#: frontend/src/pages/JobCreation.vue:20 frontend/src/pages/ProgramForm.vue:11
|
||||
#: frontend/src/pages/Programs.vue:107 frontend/src/pages/QuizForm.vue:48
|
||||
#: frontend/src/pages/Quizzes.vue:114 lms/lms/doctype/cohort/cohort.json
|
||||
#: lms/lms/doctype/cohort_subgroup/cohort_subgroup.json
|
||||
#: lms/lms/doctype/cohort_web_page/cohort_web_page.json
|
||||
@@ -4886,6 +5071,7 @@ msgstr ""
|
||||
#: lms/lms/doctype/lms_course/lms_course.json
|
||||
#: lms/lms/doctype/lms_exercise/lms_exercise.json
|
||||
#: lms/lms/doctype/lms_live_class/lms_live_class.json
|
||||
#: lms/lms/doctype/lms_program/lms_program.json
|
||||
#: lms/lms/doctype/lms_quiz/lms_quiz.json
|
||||
#: lms/lms/doctype/lms_sidebar_item/lms_sidebar_item.json
|
||||
#: lms/lms/doctype/lms_timetable_template/lms_timetable_template.json
|
||||
@@ -4894,7 +5080,7 @@ msgstr ""
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/Modals/ChapterModal.vue:169
|
||||
#: frontend/src/components/Modals/ChapterModal.vue:174
|
||||
msgid "Title is required"
|
||||
msgstr ""
|
||||
|
||||
@@ -4909,7 +5095,7 @@ msgstr ""
|
||||
msgid "To Date"
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/utils.py:1593
|
||||
#: lms/lms/utils.py:1596
|
||||
msgid "To join this batch, please contact the Administrator."
|
||||
msgstr ""
|
||||
|
||||
@@ -4973,7 +5159,7 @@ msgstr ""
|
||||
msgid "Type"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/Quiz.vue:580
|
||||
#: frontend/src/components/Quiz.vue:583
|
||||
msgid "Type your answer"
|
||||
msgstr ""
|
||||
|
||||
@@ -5257,7 +5443,7 @@ msgstr ""
|
||||
msgid "You are not enrolled in this course. Please enroll to access this lesson."
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/Courses.vue:134
|
||||
#: frontend/src/pages/Courses.vue:135
|
||||
msgid "You can add chapters and lessons to it."
|
||||
msgstr ""
|
||||
|
||||
@@ -5429,6 +5615,10 @@ msgstr ""
|
||||
msgid "jane@example.com"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/Programs.vue:32
|
||||
msgid "members"
|
||||
msgstr ""
|
||||
|
||||
#: lms/templates/quiz/quiz.html:106
|
||||
msgid "of"
|
||||
msgstr ""
|
||||
@@ -5441,7 +5631,7 @@ msgstr ""
|
||||
msgid "posted by"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/QuizForm.vue:389
|
||||
#: frontend/src/pages/QuizForm.vue:390
|
||||
msgid "question_detail"
|
||||
msgstr ""
|
||||
|
||||
@@ -5473,7 +5663,7 @@ msgstr ""
|
||||
msgid "{0} has submitted the assignment {1}"
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_enrollment/lms_enrollment.py:53
|
||||
#: lms/lms/doctype/lms_enrollment/lms_enrollment.py:57
|
||||
msgid "{0} is already a Student of {1} course through {2} batch"
|
||||
msgstr ""
|
||||
|
||||
@@ -5481,7 +5671,7 @@ msgstr ""
|
||||
msgid "{0} is already a mentor for course {1}"
|
||||
msgstr ""
|
||||
|
||||
#: lms/lms/doctype/lms_enrollment/lms_enrollment.py:26
|
||||
#: lms/lms/doctype/lms_enrollment/lms_enrollment.py:30
|
||||
msgid "{0} is already a {1} of the course {2}"
|
||||
msgstr ""
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: frappe\n"
|
||||
"Report-Msgid-Bugs-To: jannat@frappe.io\n"
|
||||
"POT-Creation-Date: 2024-11-15 16:04+0000\n"
|
||||
"PO-Revision-Date: 2024-11-18 17:31\n"
|
||||
"PO-Revision-Date: 2024-11-24 19:19\n"
|
||||
"Last-Translator: jannat@frappe.io\n"
|
||||
"Language-Team: Swedish\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -78,7 +78,7 @@ msgstr "Godkännande av Villkor och/eller Principer"
|
||||
#. Option for the 'Status' (Select) field in DocType 'Cohort Join Request'
|
||||
#: lms/lms/doctype/cohort_join_request/cohort_join_request.json
|
||||
msgid "Accepted"
|
||||
msgstr "Godkänd"
|
||||
msgstr "Accepterad"
|
||||
|
||||
#. Label of the account_id (Data) field in DocType 'Zoom Settings'
|
||||
#: lms/lms/doctype/zoom_settings/zoom_settings.json
|
||||
|
||||
1699
lms/locale/zh.po
1699
lms/locale/zh.po
File diff suppressed because it is too large
Load Diff
@@ -93,4 +93,5 @@ lms.patches.v2_0.sidebar_settings
|
||||
lms.patches.v2_0.delete_certificate_request_notification #18-09-2024
|
||||
lms.patches.v2_0.add_course_statistics #21-10-2024
|
||||
lms.patches.v2_0.give_discussions_permissions
|
||||
lms.patches.v2_0.delete_web_forms
|
||||
lms.patches.v2_0.delete_web_forms
|
||||
lms.patches.v2_0.update_desk_access_for_lms_roles
|
||||
9
lms/patches/v2_0/update_desk_access_for_lms_roles.py
Normal file
9
lms/patches/v2_0/update_desk_access_for_lms_roles.py
Normal file
@@ -0,0 +1,9 @@
|
||||
import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
roles = ["Course Creator", "Moderator", "Batch Evaluator", "LMS Student"]
|
||||
|
||||
for role in roles:
|
||||
if frappe.db.exists("Role", role):
|
||||
frappe.db.set_value("Role", role, "desk_access", 0)
|
||||
Reference in New Issue
Block a user