Compare commits

...

53 Commits

Author SHA1 Message Date
Frappe PR Bot
4b3a71e424 chore(release): Bumped to Version 2.10.0 2024-11-06 05:17:44 +00:00
Jannat Patel
5499e7294d Merge pull request #1095 from pateljannat/issues-46
fix: misc issues
2024-11-06 10:34:17 +05:30
Jannat Patel
fe1f78f8aa Merge pull request #1093 from frappe/l10n_develop2
chore: sync translations from crowdin
2024-11-05 16:13:22 +05:30
Jannat Patel
1709c6b658 Merge pull request #1094 from frappe/pot_develop_2024-11-01
chore: update POT file
2024-11-05 16:13:05 +05:30
Jannat Patel
d3583a2cfb fix: set event in live class 2024-11-04 12:01:58 +05:30
Jannat Patel
634035fbc0 fix: misc issues 2024-11-04 09:54:53 +05:30
Jannat Patel
3c5b18411b chore: Swedish translations 2024-11-03 20:08:50 +05:30
frappe-pr-bot
82bb45a9ef chore: update POT file 2024-11-01 16:04:24 +00:00
Jannat Patel
373f3df196 chore: Turkish translations 2024-11-01 19:00:24 +05:30
Jannat Patel
6021f15bac chore: Turkish translations 2024-10-31 18:59:42 +05:30
Jannat Patel
8f6f35d7c1 Merge pull request #1090 from iamejaaz/add-required-attribute
feat: add required indicator on the course add page
2024-10-31 11:48:06 +05:30
Jannat Patel
7aa5f4d20b Merge pull request #1086 from 0xflotus/patch-1
fix: small bug in course_progress_summary.py
2024-10-31 11:39:08 +05:30
Jannat Patel
64b54b05a6 Merge pull request #1085 from pateljannat/new-onboarding
feat: onboarding
2024-10-31 11:35:45 +05:30
Jannat Patel
22b1f22df4 fix: empty state conditions 2024-10-31 11:16:39 +05:30
Jannat Patel
ae4e5539d7 fix: removed chapter description when fetching outline 2024-10-31 09:51:50 +05:30
Ejaaz Khan
dbd96329b5 style: format code with precommit 2024-10-31 00:22:28 +05:30
Ejaaz Khan
c118ec7c4a feat: add required indicator on the course add page 2024-10-30 23:59:26 +05:30
0xflotus
7aab449502 fix: changed ranges 2024-10-30 18:47:36 +01:00
Jannat Patel
cf166b3a57 Merge pull request #1089 from 0xflotus/patch-2
chore: add some german translations
2024-10-30 23:12:33 +05:30
Jannat Patel
da5910d40d test: changed labels as per new onboarding 2024-10-30 23:11:47 +05:30
Jannat Patel
8640ecf9be refactor: course list data 2024-10-30 22:12:59 +05:30
0xflotus
c4faceff30 chore: add some german translations 2024-10-30 14:55:25 +01:00
0xflotus
01bd017bda fix: fixed labels 2024-10-29 19:33:22 +01:00
0xflotus
d76357981b fix: small bug in course_progress_summary.py
This is a small logical fix.

Otherwise if `row.progress == 10 or row.progress == 40 or row.progress == 70` wouldn't have an effect.
2024-10-29 19:28:14 +01:00
Jannat Patel
19b759e9fb feat: onboarding 2024-10-29 23:00:38 +05:30
Jannat Patel
df3bca6405 Merge pull request #1081 from frappe/l10n_develop2
chore: sync translations from crowdin
2024-10-28 09:34:34 +05:30
Jannat Patel
5cde79b5eb chore: Persian translations 2024-10-27 17:10:17 +05:30
Jannat Patel
9b35cdbddc chore: Bosnian translations 2024-10-26 16:54:33 +05:30
Jannat Patel
70ec22004a chore: Persian translations 2024-10-26 16:54:32 +05:30
Jannat Patel
95ed77421a chore: Chinese Simplified translations 2024-10-26 16:54:31 +05:30
Jannat Patel
d64ec9817c chore: Turkish translations 2024-10-26 16:54:29 +05:30
Jannat Patel
ce01b7634f chore: Swedish translations 2024-10-26 16:54:28 +05:30
Jannat Patel
e0819f83bc chore: Russian translations 2024-10-26 16:54:27 +05:30
Jannat Patel
f87d28c2f5 chore: Polish translations 2024-10-26 16:54:25 +05:30
Jannat Patel
544b59744b chore: Hungarian translations 2024-10-26 16:54:24 +05:30
Jannat Patel
467dfb831d chore: German translations 2024-10-26 16:54:23 +05:30
Jannat Patel
4c4b4eaf55 chore: Arabic translations 2024-10-26 16:54:21 +05:30
Jannat Patel
227e5d00e5 chore: Spanish translations 2024-10-26 16:54:20 +05:30
Jannat Patel
73e9e384c8 chore: French translations 2024-10-26 16:54:18 +05:30
Jannat Patel
5bebdcba68 Merge pull request #1080 from frappe/l10n_develop2
chore: sync translations from crowdin
2024-10-25 16:48:48 +05:30
Jannat Patel
1c2e52ae4b chore: Esperanto translations 2024-10-25 16:35:56 +05:30
Jannat Patel
9377e89561 chore: Bosnian translations 2024-10-25 16:35:54 +05:30
Jannat Patel
4cae05ecbe chore: Persian translations 2024-10-25 16:35:53 +05:30
Jannat Patel
909dcfd51e chore: Chinese Simplified translations 2024-10-25 16:35:51 +05:30
Jannat Patel
2bd96a1f2a chore: Turkish translations 2024-10-25 16:35:49 +05:30
Jannat Patel
aca41080ee chore: Swedish translations 2024-10-25 16:35:48 +05:30
Jannat Patel
1c351696a9 chore: Russian translations 2024-10-25 16:35:46 +05:30
Jannat Patel
51a8958aa6 chore: Polish translations 2024-10-25 16:35:45 +05:30
Jannat Patel
777b8aed02 chore: Hungarian translations 2024-10-25 16:35:43 +05:30
Jannat Patel
3672b90075 chore: German translations 2024-10-25 16:35:42 +05:30
Jannat Patel
92c7e613db chore: Arabic translations 2024-10-25 16:35:40 +05:30
Jannat Patel
5c58b85a00 chore: Spanish translations 2024-10-25 16:35:39 +05:30
Jannat Patel
8af82daa37 chore: French translations 2024-10-25 16:35:36 +05:30
54 changed files with 68407 additions and 4852 deletions

View File

@@ -13,6 +13,6 @@ module.exports = defineConfig({
openMode: 0, openMode: 0,
}, },
e2e: { e2e: {
baseUrl: "http://test_site_ui:8000", baseUrl: "http://test:8000",
}, },
}); });

View File

@@ -5,7 +5,7 @@ describe("Course Creation", () => {
cy.visit("/lms/courses"); cy.visit("/lms/courses");
// Create a course // Create a course
cy.get("a").contains("New").click(); cy.get("header").children().last().children().last().click();
cy.wait(1000); cy.wait(1000);
cy.url().should("include", "/courses/new/edit"); cy.url().should("include", "/courses/new/edit");
@@ -73,7 +73,7 @@ describe("Course Creation", () => {
.should("be.visible") .should("be.visible")
.within(() => { .within(() => {
cy.get("label").contains("Title").type("Test Chapter"); cy.get("label").contains("Title").type("Test Chapter");
cy.button("Add Chapter").click(); cy.button("Create").click();
}); });
// Add Lesson // Add Lesson

View File

@@ -23,7 +23,7 @@
"codemirror-editor-vue3": "^2.8.0", "codemirror-editor-vue3": "^2.8.0",
"dayjs": "^1.11.6", "dayjs": "^1.11.6",
"feather-icons": "^4.28.0", "feather-icons": "^4.28.0",
"frappe-ui": "^0.1.69", "frappe-ui": "^0.1.72",
"lucide-vue-next": "^0.383.0", "lucide-vue-next": "^0.383.0",
"markdown-it": "^14.0.0", "markdown-it": "^14.0.0",
"pinia": "^2.0.33", "pinia": "^2.0.33",

View File

@@ -25,7 +25,7 @@
</div> </div>
</template> </template>
<script setup> <script setup>
import { createListResource, Avatar } from 'frappe-ui' import { createResource, Avatar } from 'frappe-ui'
import { timeAgo } from '@/utils' import { timeAgo } from '@/utils'
const props = defineProps({ const props = defineProps({
@@ -35,24 +35,15 @@ const props = defineProps({
}, },
}) })
const communications = createListResource({ const communications = createResource({
doctype: 'Communication', url: 'lms.lms.api.get_announcements',
fields: [ makeParams(value) {
'subject', return {
'content', batch: props.batch,
'recipients', }
'cc',
'communication_date',
'sender',
'sender_full_name',
],
filters: {
reference_doctype: 'LMS Batch',
reference_name: props.batch,
}, },
orderBy: 'communication_date desc',
auto: true, auto: true,
cache: ['batch', props.batch], cache: ['announcement', props.batch],
}) })
</script> </script>
<style> <style>

View File

@@ -2,6 +2,7 @@
<div> <div>
<label class="block mb-1" :class="labelClasses" v-if="label"> <label class="block mb-1" :class="labelClasses" v-if="label">
{{ label }} {{ label }}
<span class="text-red-500" v-if="required">*</span>
</label> </label>
<div class="grid grid-cols-3 gap-1"> <div class="grid grid-cols-3 gap-1">
<Button <Button
@@ -115,6 +116,9 @@ const props = defineProps({
type: Function, type: Function,
default: (value) => `${value} is an Invalid value`, default: (value) => `${value} is an Invalid value`,
}, },
required: {
type: Boolean,
},
}) })
const values = defineModel() const values = defineModel()

View File

@@ -30,29 +30,29 @@
</div> </div>
<div class="flex flex-col flex-auto p-4"> <div class="flex flex-col flex-auto p-4">
<div class="flex items-center justify-between mb-2"> <div class="flex items-center justify-between mb-2">
<div v-if="course.lesson_count"> <div v-if="course.lessons">
<Tooltip :text="__('Lessons')"> <Tooltip :text="__('Lessons')">
<span class="flex items-center"> <span class="flex items-center">
<BookOpen class="h-4 w-4 stroke-1.5 text-gray-700 mr-1" /> <BookOpen class="h-4 w-4 stroke-1.5 text-gray-700 mr-1" />
{{ course.lesson_count }} {{ course.lessons }}
</span> </span>
</Tooltip> </Tooltip>
</div> </div>
<div v-if="course.enrollment_count"> <div v-if="course.enrollments">
<Tooltip :text="__('Enrolled Students')"> <Tooltip :text="__('Enrolled Students')">
<span class="flex items-center"> <span class="flex items-center">
<Users class="h-4 w-4 stroke-1.5 text-gray-700 mr-1" /> <Users class="h-4 w-4 stroke-1.5 text-gray-700 mr-1" />
{{ course.enrollment_count }} {{ course.enrollments }}
</span> </span>
</Tooltip> </Tooltip>
</div> </div>
<div v-if="course.avg_rating"> <div v-if="course.rating">
<Tooltip :text="__('Average Rating')"> <Tooltip :text="__('Average Rating')">
<span class="flex items-center"> <span class="flex items-center">
<Star class="h-4 w-4 stroke-1.5 text-gray-700 mr-1" /> <Star class="h-4 w-4 stroke-1.5 text-gray-700 mr-1" />
{{ course.avg_rating }} {{ course.rating }}
</span> </span>
</Tooltip> </Tooltip>
</div> </div>

View File

@@ -93,21 +93,19 @@
<div class="flex items-center mb-3"> <div class="flex items-center mb-3">
<BookOpen class="h-5 w-5 stroke-1.5 text-gray-600" /> <BookOpen class="h-5 w-5 stroke-1.5 text-gray-600" />
<span class="ml-2"> <span class="ml-2">
{{ course.data.lesson_count }} {{ __('Lessons') }} {{ course.data.lessons }} {{ __('Lessons') }}
</span> </span>
</div> </div>
<div class="flex items-center mb-3"> <div class="flex items-center mb-3">
<Users class="h-5 w-5 stroke-1.5 text-gray-600" /> <Users class="h-5 w-5 stroke-1.5 text-gray-600" />
<span class="ml-2"> <span class="ml-2">
{{ course.data.enrollment_count_formatted }} {{ formatAmount(course.data.enrollments) }}
{{ __('Enrolled Students') }} {{ __('Enrolled Students') }}
</span> </span>
</div> </div>
<div class="flex items-center"> <div class="flex items-center">
<Star class="h-5 w-5 stroke-1.5 fill-orange-500 text-gray-50" /> <Star class="h-5 w-5 stroke-1.5 fill-orange-500 text-gray-50" />
<span class="ml-2"> <span class="ml-2"> {{ course.data.rating }} {{ __('Rating') }} </span>
{{ course.data.avg_rating }} {{ __('Rating') }}
</span>
</div> </div>
</div> </div>
</div> </div>
@@ -116,7 +114,7 @@
import { BookOpen, Users, Star } from 'lucide-vue-next' import { BookOpen, Users, Star } from 'lucide-vue-next'
import { computed, inject } from 'vue' import { computed, inject } from 'vue'
import { Button, createResource } from 'frappe-ui' import { Button, createResource } from 'frappe-ui'
import { showToast } from '@/utils/' import { showToast, formatAmount } from '@/utils/'
import { capture } from '@/telemetry' import { capture } from '@/telemetry'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'

View File

@@ -76,7 +76,7 @@ const props = defineProps({
required: true, required: true,
}, },
avg_rating: { avg_rating: {
type: Number, type: String,
required: true, required: true,
}, },
membership: { membership: {

View File

@@ -21,7 +21,7 @@
<div class="space-y-2"> <div class="space-y-2">
<div <div
class="flex items-center text-sm font-medium space-x-2 cursor-pointer" class="flex text-sm font-medium space-x-2 cursor-pointer"
@click="openHelpDialog('upload')" @click="openHelpDialog('upload')"
> >
<span class="leading-5"> <span class="leading-5">
@@ -56,6 +56,21 @@
}} }}
</div> </div>
</div> </div>
<div class="space-y-2">
<div class="flex items-center text-sm font-medium space-x-2">
<span>
{{ __('What does include in preview mean?') }}
</span>
</div>
<div class="text-xs text-gray-600 mb-1 leading-5">
{{
__(
'If Include in Preview is enabled for a lesson then the lesson will also be accessible to non logged in users.'
)
}}
</div>
</div>
</div> </div>
<ExplanationVideos v-model="showExplanation" :type="type" /> <ExplanationVideos v-model="showExplanation" :type="type" />
</template> </template>

View File

@@ -48,7 +48,7 @@
<a <a
:href="cls.join_url" :href="cls.join_url"
target="_blank" target="_blank"
class="w-1/2 cursor-pointer inline-flex items-center justify-center gap-2 transition-colors focus:outline-none text-gray-800 bg-gray-100 hover:bg-gray-200 active:bg-gray-300 focus-visible:ring focus-visible:ring-gray-400 h-7 text-base px-2 rounded" class="w-full cursor-pointer inline-flex items-center justify-center gap-2 transition-colors focus:outline-none text-gray-800 bg-gray-100 hover:bg-gray-200 active:bg-gray-300 focus-visible:ring focus-visible:ring-gray-400 h-7 text-base px-2 rounded"
> >
<Video class="h-4 w-4 stroke-1.5" /> <Video class="h-4 w-4 stroke-1.5" />
{{ __('Join') }} {{ __('Join') }}

View File

@@ -44,7 +44,7 @@
<script setup> <script setup>
import { Dialog, Input, TextEditor, createResource } from 'frappe-ui' import { Dialog, Input, TextEditor, createResource } from 'frappe-ui'
import { reactive } from 'vue' import { reactive } from 'vue'
import { createToast } from '@/utils/' import { showToast } from '@/utils/'
const show = defineModel() const show = defineModel()
@@ -94,22 +94,14 @@ const makeAnnouncement = (close) => {
}, },
onSuccess() { onSuccess() {
close() close()
createToast({ showToast(
title: 'Success', __('Success'),
text: 'Announcement has been sent successfully', __('Announcement has been sent successfully'),
icon: 'Check', 'check'
iconClasses: 'bg-green-600 text-white rounded-md p-px', )
})
}, },
onError(err) { onError(err) {
createToast({ showToast(__('Error'), __(err.messages?.[0] || err), 'check')
title: 'Error',
text: err.messages?.[0] || err,
icon: 'x',
iconClasses: 'bg-red-600 text-white rounded-md p-px',
position: 'top-center',
timeout: 10,
})
}, },
} }
) )

View File

@@ -2,11 +2,11 @@
<Dialog <Dialog
v-model="show" v-model="show"
:options="{ :options="{
title: __('Add Chapter'), title: chapterDetail ? __('Edit Chapter') : __('Add Chapter'),
size: 'lg', size: 'lg',
actions: [ actions: [
{ {
label: chapterDetail ? __('Edit Chapter') : __('Add Chapter'), label: chapterDetail ? __('Edit') : __('Create'),
variant: 'solid', variant: 'solid',
onClick: (close) => onClick: (close) =>
chapterDetail ? editChapter(close) : addChapter(close), chapterDetail ? editChapter(close) : addChapter(close),

View File

@@ -1,7 +1,7 @@
<template> <template>
<div v-if="quiz.data"> <div v-if="quiz.data">
<div <div
class="bg-blue-100 space-y-1 py-2 px-2 rounded-md text-sm text-blue-800" class="bg-blue-100 space-y-1 py-2 px-2 mb-4 rounded-md text-sm text-blue-800"
> >
<div class="leading-5"> <div class="leading-5">
{{ {{

View File

@@ -236,7 +236,7 @@ const breadcrumbs = computed(() => {
const isStudent = computed(() => { const isStudent = computed(() => {
return ( return (
user?.data && user?.data &&
batch.data?.students.length && batch.data?.students?.length &&
batch.data?.students.includes(user.data.name) batch.data?.students.includes(user.data.name)
) )
}) })

View File

@@ -32,57 +32,65 @@
</div> </div>
</div> </div>
<div class="mb-4"> <div class="mb-4">
<div> <div class="text-xs text-gray-600 mb-2">
<FileUploader {{ __('Meta Image') }}
v-if="!batch.image" </div>
class="mt-4" <FileUploader
:fileTypes="['image/*']" v-if="!batch.image"
:validateFile="validateFile" :fileTypes="['image/*']"
@success="(file) => saveImage(file)" :validateFile="validateFile"
> @success="(file) => saveImage(file)"
<template v-slot="{ file, progress, uploading, openFileSelector }"> >
<div class="mb-4"> <template v-slot="{ file, progress, uploading, openFileSelector }">
<Button @click="openFileSelector" :loading="uploading">
{{ uploading ? `Uploading ${progress}%` : 'Upload an image' }}
</Button>
</div>
</template>
</FileUploader>
<div v-else class="mb-4">
<div class="text-xs text-gray-600 mb-1">
{{ __('Meta Image') }}
</div>
<div class="flex items-center"> <div class="flex items-center">
<div class="border rounded-md p-2 mr-2"> <div class="border rounded-md w-fit py-5 px-20">
<FileText class="h-5 w-5 stroke-1.5 text-gray-700" /> <Image class="size-5 stroke-1 text-gray-700" />
</div> </div>
<div class="flex flex-col"> <div class="ml-4">
<span> <Button @click="openFileSelector">
{{ batch.image.file_name }} {{ __('Upload') }}
</span> </Button>
<span class="text-sm text-gray-500 mt-1"> <div class="mt-2 text-gray-600 text-sm">
{{ getFileSize(batch.image.file_size) }} {{
</span> __(
'Appears when the batch URL is shared on any online platform'
)
}}
</div>
</div>
</div>
</template>
</FileUploader>
<div v-else class="mb-4">
<div class="flex items-center">
<img :src="batch.image.file_url" class="border rounded-md w-40" />
<div class="ml-4">
<Button @click="removeImage()">
{{ __('Remove') }}
</Button>
<div class="mt-2 text-gray-600 text-sm">
{{
__(
'Appears when the batch URL is shared on any online platform'
)
}}
</div> </div>
<X
@click="removeImage()"
class="bg-gray-200 rounded-md cursor-pointer stroke-1.5 w-5 h-5 p-1 ml-4"
/>
</div> </div>
</div> </div>
</div> </div>
<MultiSelect
v-model="instructors"
doctype="User"
:label="__('Instructors')"
/>
</div> </div>
<MultiSelect
v-model="instructors"
doctype="User"
:label="__('Instructors')"
/>
<div class="mb-4"> <div class="mb-4">
<FormControl <FormControl
v-model="batch.description" v-model="batch.description"
:label="__('Description')" :label="__('Description')"
type="textarea" type="textarea"
class="my-4" class="my-4"
:placeholder="__('Short description of the batch')"
/> />
<div> <div>
<label class="block text-sm text-gray-600 mb-1"> <label class="block text-sm text-gray-600 mb-1">
@@ -133,6 +141,7 @@
v-model="batch.timezone" v-model="batch.timezone"
:label="__('Timezone')" :label="__('Timezone')"
type="text" type="text"
:placeholder="__('Example: IST (+5:30)')"
class="mb-4" class="mb-4"
/> />
</div> </div>
@@ -149,6 +158,7 @@
:label="__('Seat Count')" :label="__('Seat Count')"
type="number" type="number"
class="mb-4" class="mb-4"
:placeholder="__('Number of seats available')"
/> />
<FormControl <FormControl
v-model="batch.evaluation_end_date" v-model="batch.evaluation_end_date"
@@ -228,11 +238,11 @@ import {
createResource, createResource,
} from 'frappe-ui' } from 'frappe-ui'
import Link from '@/components/Controls/Link.vue' import Link from '@/components/Controls/Link.vue'
import MultiSelect from '@/components/Controls/MultiSelect.vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { getFileSize, showToast } from '../utils' import { showToast } from '../utils'
import { X, FileText } from 'lucide-vue-next' import { Image } from 'lucide-vue-next'
import { capture } from '@/telemetry' import { capture } from '@/telemetry'
import MultiSelect from '@/components/Controls/MultiSelect.vue'
const router = useRouter() const router = useRouter()
const user = inject('$user') const user = inject('$user')

View File

@@ -40,6 +40,7 @@
{{ __('Loading Batches...') }} {{ __('Loading Batches...') }}
</div> </div>
<Tabs <Tabs
v-if="hasBatches"
v-model="tabIndex" v-model="tabIndex"
:tabs="makeTabs" :tabs="makeTabs"
tablistClass="overflow-x-visible flex-wrap !gap-3 md:flex-nowrap" tablistClass="overflow-x-visible flex-wrap !gap-3 md:flex-nowrap"
@@ -79,24 +80,63 @@
<BatchCard :batch="batch" /> <BatchCard :batch="batch" />
</router-link> </router-link>
</div> </div>
<div <div v-else class="p-5 italic text-gray-500">
v-else {{ __('No {0} batches').format(tab.label.toLowerCase()) }}
class="grid flex-1 place-items-center text-xl font-medium text-gray-500"
>
<div class="flex flex-col items-center justify-center mt-4">
<div>
{{ __('No {0} batches found').format(tab.label.toLowerCase()) }}
</div>
</div>
</div> </div>
</template> </template>
</Tabs> </Tabs>
<div
v-else-if="
!batches.loading &&
!hasBatches &&
(user.data?.is_instructor || user.data?.is_moderator)
"
class="grid grid-cols-3 p-5"
>
<router-link
:to="{
name: 'BatchForm',
params: {
batchName: 'new',
},
}"
>
<div class="bg-gray-50 py-32 px-5 rounded-md">
<div class="flex flex-col items-center text-center space-y-2">
<Plus
class="size-10 stroke-1 text-gray-800 p-1 rounded-full border bg-white"
/>
<div class="font-medium">
{{ __('Create a Batch') }}
</div>
<span class="text-gray-700 text-sm leading-4">
{{ __('You can link courses and assessments to it.') }}
</span>
</div>
</div>
</router-link>
</div>
<div
v-else-if="!batches.loading && !hasBatches"
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 batches found') }}
</div>
<div>
{{
__(
'There are no batches available at the moment. Keep an eye out, fresh learning experiences are on the way soon!'
)
}}
</div>
</div>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { import {
createListResource,
createResource, createResource,
Breadcrumbs, Breadcrumbs,
Button, Button,
@@ -104,13 +144,14 @@ import {
Badge, Badge,
Select, Select,
} from 'frappe-ui' } from 'frappe-ui'
import { Plus } from 'lucide-vue-next' import { BookOpen, Plus } from 'lucide-vue-next'
import BatchCard from '@/components/BatchCard.vue' import BatchCard from '@/components/BatchCard.vue'
import { inject, ref, computed, onMounted, watch } from 'vue' import { inject, ref, computed, onMounted, watch } from 'vue'
import { updateDocumentTitle } from '@/utils' import { updateDocumentTitle } from '@/utils'
const user = inject('$user') const user = inject('$user')
const currentCategory = ref(null) const currentCategory = ref(null)
const hasBatches = ref(false)
onMounted(() => { onMounted(() => {
let queries = new URLSearchParams(location.search) let queries = new URLSearchParams(location.search)
@@ -119,10 +160,10 @@ onMounted(() => {
} }
}) })
const batches = createListResource({ const batches = createResource({
doctype: 'LMS Batch', doctype: 'LMS Batch',
url: 'lms.lms.utils.get_batches', url: 'lms.lms.utils.get_batches',
cache: ['batches', user?.data?.email], cache: ['batches', user.data?.email],
auto: true, auto: true,
}) })
@@ -183,6 +224,14 @@ const addToTabs = (label) => {
}) })
} }
watch(batches, () => {
Object.keys(batches.data).forEach((key) => {
if (batches.data[key].length) {
hasBatches.value = true
}
})
})
watch( watch(
() => currentCategory.value, () => currentCategory.value,
() => { () => {

View File

@@ -16,16 +16,16 @@
</div> </div>
<div class="flex items-center"> <div class="flex items-center">
<Tooltip <Tooltip
v-if="course.data.avg_rating" v-if="course.data.rating"
:text="__('Average Rating')" :text="__('Average Rating')"
class="flex items-center" class="flex items-center"
> >
<Star class="h-5 w-5 text-gray-100 fill-orange-500" /> <Star class="h-5 w-5 text-gray-100 fill-orange-500" />
<span class="ml-1"> <span class="ml-1">
{{ course.data.avg_rating }} {{ course.data.rating }}
</span> </span>
</Tooltip> </Tooltip>
<span v-if="course.data.avg_rating" class="mx-3">&middot;</span> <span v-if="course.data.rating" class="mx-3">&middot;</span>
<Tooltip <Tooltip
v-if="course.data.enrollment_count" v-if="course.data.enrollment_count"
:text="__('Enrolled Students')" :text="__('Enrolled Students')"
@@ -74,7 +74,7 @@
</div> </div>
<CourseReviews <CourseReviews
:courseName="course.data.name" :courseName="course.data.name"
:avg_rating="course.data.avg_rating" :avg_rating="course.data.rating"
:membership="course.data.membership" :membership="course.data.membership"
/> />
</div> </div>
@@ -116,7 +116,7 @@ const breadcrumbs = computed(() => {
let items = [{ label: 'All Courses', route: { name: 'Courses' } }] let items = [{ label: 'All Courses', route: { name: 'Courses' } }]
items.push({ items.push({
label: course?.data?.title, label: course?.data?.title,
route: { name: 'CourseDetail', params: { course: course?.data?.name } }, route: { name: 'CourseDetail', params: { courseName: course?.data?.name } },
}) })
return items return items
}) })

View File

@@ -23,15 +23,23 @@
v-model="course.title" v-model="course.title"
:label="__('Title')" :label="__('Title')"
class="mb-4" class="mb-4"
:required="true"
/> />
<FormControl <FormControl
v-model="course.short_introduction" v-model="course.short_introduction"
:label="__('Short Introduction')" :label="__('Short Introduction')"
:placeholder="
__(
'A one line introduction to the course that appears on the course card'
)
"
class="mb-4" class="mb-4"
:required="true"
/> />
<div class="mb-4"> <div class="mb-4">
<div class="mb-1.5 text-sm text-gray-700"> <div class="mb-1.5 text-sm text-gray-600">
{{ __('Course Description') }} {{ __('Course Description') }}
<span class="text-red-500">*</span>
</div> </div>
<TextEditor <TextEditor
:content="course.description" :content="course.description"
@@ -41,49 +49,62 @@
editorClass="prose-sm max-w-none border-b border-x bg-gray-100 rounded-b-md py-1 px-2 min-h-[7rem]" editorClass="prose-sm max-w-none border-b border-x bg-gray-100 rounded-b-md py-1 px-2 min-h-[7rem]"
/> />
</div> </div>
<FileUploader <div class="mb-4">
v-if="!course.course_image" <div class="text-xs text-gray-600 mb-2">
:fileTypes="['image/*']"
:validateFile="validateFile"
@success="(file) => saveImage(file)"
>
<template
v-slot="{ file, progress, uploading, openFileSelector }"
>
<div class="mb-4">
<Button @click="openFileSelector" :loading="uploading">
{{
uploading ? `Uploading ${progress}%` : 'Upload an image'
}}
</Button>
</div>
</template>
</FileUploader>
<div v-else class="mb-4">
<div class="text-xs text-gray-600 mb-1">
{{ __('Course Image') }} {{ __('Course Image') }}
<span class="text-red-500">*</span>
</div> </div>
<div class="flex items-center"> <FileUploader
<div class="border rounded-md p-2 mr-2"> v-if="!course.course_image"
<FileText class="h-5 w-5 stroke-1.5 text-gray-700" /> :fileTypes="['image/*']"
:validateFile="validateFile"
@success="(file) => saveImage(file)"
>
<template
v-slot="{ file, progress, uploading, openFileSelector }"
>
<div class="flex items-center">
<div class="border rounded-md w-fit py-5 px-20">
<Image class="size-5 stroke-1 text-gray-700" />
</div>
<div class="ml-4">
<Button @click="openFileSelector">
{{ __('Upload') }}
</Button>
<div class="mt-2 text-gray-600 text-sm">
{{
__('Appears on the course card in the course list')
}}
</div>
</div>
</div>
</template>
</FileUploader>
<div v-else class="mb-4">
<div class="flex items-center">
<img
:src="course.course_image.file_url"
class="border rounded-md w-40"
/>
<div class="ml-4">
<Button @click="removeImage()">
{{ __('Remove') }}
</Button>
<div class="mt-2 text-gray-600 text-sm">
{{ __('Appears on the course card in the course list') }}
</div>
</div>
</div> </div>
<div class="flex flex-col">
<span>
{{ course.course_image.file_name }}
</span>
<span class="text-sm text-gray-500 mt-1">
{{ getFileSize(course.course_image.file_size) }}
</span>
</div>
<X
@click="removeImage()"
class="bg-gray-200 rounded-md cursor-pointer stroke-1.5 w-5 h-5 p-1 ml-4"
/>
</div> </div>
</div> </div>
<FormControl <FormControl
v-model="course.video_link" v-model="course.video_link"
:label="__('Preview Video')" :label="__('Preview Video')"
:placeholder="
__(
'Paste the youtube link of a short video introducing the course'
)
"
class="mb-4" class="mb-4"
/> />
<div class="mb-4"> <div class="mb-4">
@@ -104,6 +125,8 @@
</div> </div>
<FormControl <FormControl
v-model="newTag" v-model="newTag"
:placeholder="__('Keywords for the course')"
class="w-52"
@keyup.enter="updateTags()" @keyup.enter="updateTags()"
id="tags" id="tags"
/> />
@@ -121,6 +144,7 @@
v-model="instructors" v-model="instructors"
doctype="User" doctype="User"
:label="__('Instructors')" :label="__('Instructors')"
:required="true"
/> />
</div> </div>
<div class="container border-t"> <div class="container border-t">
@@ -130,7 +154,7 @@
<div class="grid grid-cols-3 gap-10 mb-4"> <div class="grid grid-cols-3 gap-10 mb-4">
<div <div
v-if="user.data?.is_moderator" v-if="user.data?.is_moderator"
class="flex flex-col space-y-3" class="flex flex-col space-y-4"
> >
<FormControl <FormControl
type="checkbox" type="checkbox"
@@ -224,14 +248,9 @@ import {
reactive, reactive,
watch, watch,
} from 'vue' } from 'vue'
import { import { convertToTitleCase, showToast, updateDocumentTitle } from '@/utils'
convertToTitleCase,
showToast,
getFileSize,
updateDocumentTitle,
} from '@/utils'
import Link from '@/components/Controls/Link.vue' import Link from '@/components/Controls/Link.vue'
import { FileText, X } from 'lucide-vue-next' import { FileText, Image, X } from 'lucide-vue-next'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import CourseOutline from '@/components/CourseOutline.vue' import CourseOutline from '@/components/CourseOutline.vue'
import MultiSelect from '@/components/Controls/MultiSelect.vue' import MultiSelect from '@/components/Controls/MultiSelect.vue'

View File

@@ -8,7 +8,7 @@
:items="[{ label: __('Courses'), route: { name: 'Courses' } }]" :items="[{ label: __('Courses'), route: { name: 'Courses' } }]"
/> />
<div class="flex space-x-2 justify-end"> <div class="flex space-x-2 justify-end">
<div class="w-46 md:w-44"> <div class="w-40 md:w-44">
<FormControl <FormControl
v-if="categories.data?.length" v-if="categories.data?.length"
type="select" type="select"
@@ -48,6 +48,7 @@
</header> </header>
<div class=""> <div class="">
<Tabs <Tabs
v-if="hasCourses"
v-model="tabIndex" v-model="tabIndex"
tablistClass="overflow-x-visible flex-wrap !gap-3 md:flex-nowrap" tablistClass="overflow-x-visible flex-wrap !gap-3 md:flex-nowrap"
:tabs="makeTabs" :tabs="makeTabs"
@@ -101,18 +102,57 @@
<CourseCard :course="course" /> <CourseCard :course="course" />
</router-link> </router-link>
</div> </div>
<div <div v-else class="p-5 italic text-gray-500">
v-else {{ __('No {0} courses').format(tab.label.toLowerCase()) }}
class="grid flex-1 place-items-center text-xl font-medium text-gray-500"
>
<div class="flex flex-col items-center justify-center mt-4">
<div>
{{ __('No {0} courses found').format(tab.label.toLowerCase()) }}
</div>
</div>
</div> </div>
</template> </template>
</Tabs> </Tabs>
<div
v-else-if="
!courses.loading &&
(user.data?.is_moderator || user.data?.is_instructor)
"
class="grid grid-cols-3 p-5"
>
<router-link
:to="{
name: 'CourseForm',
params: {
courseName: 'new',
},
}"
>
<div class="bg-gray-50 py-32 px-5 rounded-md">
<div class="flex flex-col items-center text-center space-y-2">
<Plus
class="size-10 stroke-1 text-gray-800 p-1 rounded-full border bg-white"
/>
<div class="font-medium">
{{ __('Create a Course') }}
</div>
<span class="text-gray-700 text-sm leading-4">
{{ __('You can add chapters and lessons to it.') }}
</span>
</div>
</div>
</router-link>
</div>
<div
v-else-if="!courses.loading && !hasCourses"
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 courses found') }}
</div>
<div>
{{
__(
'There are no courses available at the moment. Keep an eye out, fresh learning experiences are on the way soon!'
)
}}
</div>
</div>
</div> </div>
</div> </div>
</template> </template>
@@ -127,13 +167,14 @@ import {
createResource, createResource,
} from 'frappe-ui' } from 'frappe-ui'
import CourseCard from '@/components/CourseCard.vue' import CourseCard from '@/components/CourseCard.vue'
import { Plus, Search } from 'lucide-vue-next' import { BookOpen, Plus, Search } from 'lucide-vue-next'
import { ref, computed, inject, onMounted, watch } from 'vue' import { ref, computed, inject, onMounted, watch } from 'vue'
import { updateDocumentTitle } from '@/utils' import { updateDocumentTitle } from '@/utils'
const user = inject('$user') const user = inject('$user')
const searchQuery = ref('') const searchQuery = ref('')
const currentCategory = ref(null) const currentCategory = ref(null)
const hasCourses = ref(false)
onMounted(() => { onMounted(() => {
let queries = new URLSearchParams(location.search) let queries = new URLSearchParams(location.search)
@@ -223,6 +264,16 @@ const categories = createResource({
}, },
}) })
watch(courses, () => {
if (courses.data) {
Object.keys(courses.data).forEach((section) => {
if (courses.data[section].length) {
hasCourses.value = true
}
})
}
})
watch( watch(
() => currentCategory.value, () => currentCategory.value,
() => { () => {

View File

@@ -17,14 +17,9 @@
) )
}} }}
</p> </p>
<router-link <Button v-if="user.data" @click="enrollStudent()" variant="solid">
v-if="user.data" {{ __('Start Learning') }}
:to="{ name: 'CourseDetail', params: { courseName: courseName } }" </Button>
>
<Button variant="solid">
{{ __('Start Learning') }}
</Button>
</router-link>
<Button v-else @click="redirectToLogin()"> <Button v-else @click="redirectToLogin()">
{{ __('Login') }} {{ __('Login') }}
</Button> </Button>
@@ -194,7 +189,7 @@ import { createResource, Breadcrumbs, Button } from 'frappe-ui'
import { computed, watch, inject, ref, onMounted, onBeforeUnmount } from 'vue' import { computed, watch, inject, ref, onMounted, onBeforeUnmount } from 'vue'
import CourseOutline from '@/components/CourseOutline.vue' import CourseOutline from '@/components/CourseOutline.vue'
import UserAvatar from '@/components/UserAvatar.vue' import UserAvatar from '@/components/UserAvatar.vue'
import { useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
import { ChevronLeft, ChevronRight } from 'lucide-vue-next' import { ChevronLeft, ChevronRight } from 'lucide-vue-next'
import Discussions from '@/components/Discussions.vue' import Discussions from '@/components/Discussions.vue'
import { getEditorTools, updateDocumentTitle } from '../utils' import { getEditorTools, updateDocumentTitle } from '../utils'
@@ -204,6 +199,7 @@ import CourseInstructors from '@/components/CourseInstructors.vue'
import ProgressBar from '@/components/ProgressBar.vue' import ProgressBar from '@/components/ProgressBar.vue'
const user = inject('$user') const user = inject('$user')
const router = useRouter()
const route = useRoute() const route = useRoute()
const allowDiscussions = ref(false) const allowDiscussions = ref(false)
const editor = ref(null) const editor = ref(null)
@@ -301,14 +297,14 @@ const breadcrumbs = computed(() => {
let items = [{ label: 'All Courses', route: { name: 'Courses' } }] let items = [{ label: 'All Courses', route: { name: 'Courses' } }]
items.push({ items.push({
label: lesson?.data?.course_title, label: lesson?.data?.course_title,
route: { name: 'CourseDetail', params: { course: props.courseName } }, route: { name: 'CourseDetail', params: { courseName: props.courseName } },
}) })
items.push({ items.push({
label: lesson?.data?.title, label: lesson?.data?.title,
route: { route: {
name: 'Lesson', name: 'Lesson',
params: { params: {
course: props.courseName, courseName: props.courseName,
chapterNumber: props.chapterNumber, chapterNumber: props.chapterNumber,
lessonNumber: props.lessonNumber, lessonNumber: props.lessonNumber,
}, },
@@ -379,6 +375,30 @@ const allowInstructorContent = () => {
return false return false
} }
const enrollment = createResource({
url: 'frappe.client.insert',
makeParams() {
return {
doc: {
doctype: 'LMS Enrollment',
course: props.courseName,
member: user.data?.name,
},
}
},
})
const enrollStudent = () => {
enrollment.submit(
{},
{
onSuccess() {
window.location.reload()
},
}
)
}
const redirectToLogin = () => { const redirectToLogin = () => {
window.location.href = `/login?redirect-to=/lms/courses/${props.courseName}` window.location.href = `/login?redirect-to=/lms/courses/${props.courseName}`
} }

View File

@@ -69,7 +69,7 @@
</div> </div>
</template> </template>
<script setup> <script setup>
import { Breadcrumbs, FormControl, createResource, Button } from 'frappe-ui' import { Breadcrumbs, Button, createResource, FormControl } from 'frappe-ui'
import { import {
computed, computed,
reactive, reactive,

View File

@@ -57,6 +57,15 @@ export function formatNumberIntoCurrency(number, currency) {
return '' return ''
} }
// create a function that formats numbers in thousands to k
export function formatAmount(amount) {
if (amount > 999) {
return (amount / 1000).toFixed(1) + 'k'
}
return amount
}
export function convertToTitleCase(str) { export function convertToTitleCase(str) {
if (!str) { if (!str) {
return '' return ''

View File

@@ -51,7 +51,7 @@ export class Quiz {
app.mount(this.wrapper) app.mount(this.wrapper)
return return
} }
this.wrapper.innerHTML = `<div class='border rounded-md p-10 text-center mb-2'> this.wrapper.innerHTML = `<div class='border rounded-md p-10 text-center bg-gray-50 mb-2'>
<span class="font-medium"> <span class="font-medium">
Quiz: ${quiz} Quiz: ${quiz}
</span> </span>

View File

@@ -1224,10 +1224,10 @@ fraction.js@^4.3.7:
resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7"
integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==
frappe-ui@^0.1.69: frappe-ui@^0.1.72:
version "0.1.69" version "0.1.72"
resolved "https://registry.yarnpkg.com/frappe-ui/-/frappe-ui-0.1.69.tgz#bfc6d19dff97d2666c36da63f5de62f819539406" resolved "https://registry.yarnpkg.com/frappe-ui/-/frappe-ui-0.1.72.tgz#f5550056ddee7ad4341f2c1825d046404d221820"
integrity sha512-MKHYTcRvmccZwTYlIcmf4OCbJQH5eqKXsq3Cj2lbnmoWuuTh9m7T3AoRKEwOIlZ0mSGCH9yzaF2BINBXGpIJdQ== integrity sha512-XWYKmCjw3ViD+/+tZMUiYqwHFlMGMsVuazOYiN5bKlE+aiheJsnHlOOUyQswYX1Y7jNxuC7gGpSLNg2ZpXA7hA==
dependencies: dependencies:
"@headlessui/vue" "^1.7.14" "@headlessui/vue" "^1.7.14"
"@popperjs/core" "^2.11.2" "@popperjs/core" "^2.11.2"

View File

@@ -1 +1 @@
__version__ = "2.9.0" __version__ = "2.10.0"

View File

@@ -110,7 +110,8 @@ doc_events = {
# --------------- # ---------------
scheduler_events = { scheduler_events = {
"hourly": [ "hourly": [
"lms.lms.doctype.lms_certificate_request.lms_certificate_request.schedule_evals" "lms.lms.doctype.lms_certificate_request.lms_certificate_request.schedule_evals",
"lms.lms.api.update_course_statistics",
], ],
"daily": ["lms.job.doctype.job_opportunity.job_opportunity.update_job_openings"], "daily": ["lms.job.doctype.job_opportunity.job_opportunity.update_job_openings"],
} }

View File

@@ -6,8 +6,9 @@ from frappe.translate import get_all_translations
from frappe import _ from frappe import _
from frappe.query_builder import DocType from frappe.query_builder import DocType
from frappe.query_builder.functions import Count from frappe.query_builder.functions import Count
from frappe.utils import time_diff, now_datetime, get_datetime from frappe.utils import time_diff, now_datetime, get_datetime, flt
from typing import Optional from typing import Optional
from lms.lms.utils import get_average_rating, get_lesson_count
@frappe.whitelist() @frappe.whitelist()
@@ -760,3 +761,44 @@ def get_payment_gateway_details(payment_gateway):
"doctype": doctype, "doctype": doctype,
"docname": docname, "docname": docname,
} }
def update_course_statistics():
courses = frappe.get_all("LMS Course", fields=["name"])
for course in courses:
lessons = get_lesson_count(course.name)
enrollments = frappe.db.count(
"LMS Enrollment", {"course": course.name, "member_type": "Student"}
)
avg_rating = get_average_rating(course.name) or 0
avg_rating = flt(avg_rating, frappe.get_system_settings("float_precision") or 3)
frappe.db.set_value(
"LMS Course",
course.name,
{"lessons": lessons, "enrollments": enrollments, "rating": avg_rating},
)
@frappe.whitelist()
def get_announcements(batch):
return frappe.get_all(
"Communication",
filters={
"reference_doctype": "LMS Batch",
"reference_name": batch,
},
fields=[
"subject",
"content",
"recipients",
"cc",
"communication_date",
"sender",
"sender_full_name",
],
order_by="communication_date desc",
)

View File

@@ -7,17 +7,3 @@ from frappe.model.document import Document
class BatchStudent(Document): class BatchStudent(Document):
pass pass
@frappe.whitelist()
def enroll_batch(batch_name):
if frappe.db.exists(
"Batch Student", {"student": frappe.session.user, "parent": batch_name}
):
frappe.throw("You are already enrolled in this batch")
enrollment = frappe.new_doc("Batch Student")
enrollment.student = frappe.session.user
enrollment.parent = batch_name
enrollment.parentfield = "students"
enrollment.parenttype = "LMS Batch"
enrollment.save(ignore_permissions=True)

View File

@@ -9,9 +9,8 @@
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [ "field_order": [
"course", "course",
"title",
"column_break_3", "column_break_3",
"description", "title",
"section_break_5", "section_break_5",
"lessons" "lessons"
], ],
@@ -35,11 +34,6 @@
"fieldname": "column_break_3", "fieldname": "column_break_3",
"fieldtype": "Column Break" "fieldtype": "Column Break"
}, },
{
"fieldname": "description",
"fieldtype": "Small Text",
"label": "Description"
},
{ {
"fieldname": "section_break_5", "fieldname": "section_break_5",
"fieldtype": "Section Break" "fieldtype": "Section Break"
@@ -59,7 +53,7 @@
"link_fieldname": "chapter" "link_fieldname": "chapter"
} }
], ],
"modified": "2023-09-29 17:03:58.013819", "modified": "2024-10-29 16:54:20.904683",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "LMS", "module": "LMS",
"name": "Course Chapter", "name": "Course Chapter",

View File

@@ -33,6 +33,7 @@ class LMSBatch(Document):
self.validate_timetable() self.validate_timetable()
self.send_confirmation_mail() self.send_confirmation_mail()
self.validate_evaluation_end_date() self.validate_evaluation_end_date()
self.add_students_to_live_class()
def validate_batch_end_date(self): def validate_batch_end_date(self):
if self.end_date < self.start_date: if self.end_date < self.start_date:
@@ -139,6 +140,27 @@ class LMSBatch(Document):
if cint(self.seat_count) < len(self.students): if cint(self.seat_count) < len(self.students):
frappe.throw(_("There are no seats available in this batch.")) frappe.throw(_("There are no seats available in this batch."))
def add_students_to_live_class(self):
for student in self.students:
if student.is_new():
live_classes = frappe.get_all(
"LMS Live Class", {"batch_name": self.name}, ["name", "event"]
)
for live_class in live_classes:
if live_class.event:
frappe.get_doc(
{
"doctype": "Event Participants",
"reference_doctype": "User",
"reference_docname": student.student,
"email": student.student,
"parent": live_class.event,
"parenttype": "Event",
"parentfield": "event_participants",
}
).save()
def validate_timetable(self): def validate_timetable(self):
for schedule in self.timetable: for schedule in self.timetable:
if schedule.start_time and schedule.end_time: if schedule.start_time and schedule.end_time:

View File

@@ -48,7 +48,12 @@
"certification_section", "certification_section",
"enable_certification", "enable_certification",
"column_break_rxww", "column_break_rxww",
"expiry" "expiry",
"tab_4_tab",
"statistics_section",
"enrollments",
"lessons",
"rating"
], ],
"fields": [ "fields": [
{ {
@@ -249,6 +254,36 @@
"fieldtype": "Link", "fieldtype": "Link",
"label": "Category", "label": "Category",
"options": "LMS Category" "options": "LMS Category"
},
{
"fieldname": "tab_4_tab",
"fieldtype": "Tab Break",
"label": "Statistics"
},
{
"fieldname": "statistics_section",
"fieldtype": "Section Break"
},
{
"default": "0",
"fieldname": "enrollments",
"fieldtype": "Data",
"label": "Enrollments",
"read_only": 1
},
{
"default": "0",
"fieldname": "lessons",
"fieldtype": "Data",
"label": "Lessons",
"read_only": 1
},
{
"default": "0",
"fieldname": "rating",
"fieldtype": "Data",
"label": "Rating",
"read_only": 1
} }
], ],
"is_published_field": "published", "is_published_field": "published",
@@ -275,7 +310,7 @@
} }
], ],
"make_attachments_public": 1, "make_attachments_public": 1,
"modified": "2024-09-21 10:23:58.633912", "modified": "2024-10-30 23:08:31.842860",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "LMS", "module": "LMS",
"name": "LMS Course", "name": "LMS Course",

View File

@@ -187,192 +187,3 @@ def reindex_exercises(doc):
course = frappe.get_doc("LMS Course", course_data["name"]) course = frappe.get_doc("LMS Course", course_data["name"])
course.reindex_exercises() course.reindex_exercises()
frappe.msgprint("All exercises in this course have been re-indexed.") frappe.msgprint("All exercises in this course have been re-indexed.")
@frappe.whitelist(allow_guest=True)
def search_course(text):
courses = frappe.get_all(
"LMS Course",
filters={"published": True},
or_filters={
"title": ["like", f"%{text}%"],
"tags": ["like", f"%{text}%"],
"short_introduction": ["like", f"%{text}%"],
"description": ["like", f"%{text}%"],
},
fields=["name", "title"],
)
return courses
@frappe.whitelist()
def submit_for_review(course):
chapters = frappe.get_all("Chapter Reference", {"parent": course})
if not len(chapters):
return "No Chp"
frappe.db.set_value("LMS Course", course, "status", "Under Review")
return "OK"
@frappe.whitelist()
def save_course(
tags,
title,
short_introduction,
video_link,
description,
course,
published,
upcoming,
image=None,
paid_course=False,
course_price=None,
currency=None,
):
if not can_create_courses(course):
return
if course:
doc = frappe.get_doc("LMS Course", course)
else:
doc = frappe.get_doc({"doctype": "LMS Course"})
doc.update(
{
"title": title,
"short_introduction": short_introduction,
"video_link": video_link,
"image": image,
"description": description,
"tags": tags,
"published": cint(published),
"upcoming": cint(upcoming),
"paid_course": cint(paid_course),
"course_price": course_price,
"currency": currency,
}
)
doc.save(ignore_permissions=True)
return doc.name
@frappe.whitelist()
def save_chapter(course, title, chapter_description, idx, chapter):
if chapter:
doc = frappe.get_doc("Course Chapter", chapter)
else:
doc = frappe.get_doc({"doctype": "Course Chapter"})
doc.update({"course": course, "title": title, "description": chapter_description})
doc.save(ignore_permissions=True)
if chapter:
chapter_reference = frappe.get_doc("Chapter Reference", {"chapter": chapter})
else:
chapter_reference = frappe.get_doc(
{
"doctype": "Chapter Reference",
"parent": course,
"parenttype": "LMS Course",
"parentfield": "chapters",
"idx": idx,
}
)
chapter_reference.update({"chapter": doc.name})
chapter_reference.save(ignore_permissions=True)
return doc.name
@frappe.whitelist()
def save_lesson(
title,
body,
chapter,
preview,
idx,
lesson,
instructor_notes=None,
youtube=None,
quiz_id=None,
question=None,
file_type=None,
):
if lesson:
doc = frappe.get_doc("Course Lesson", lesson)
else:
doc = frappe.get_doc({"doctype": "Course Lesson"})
doc.update(
{
"chapter": chapter,
"title": title,
"body": body,
"instructor_notes": instructor_notes,
"include_in_preview": preview,
"youtube": youtube,
"quiz_id": quiz_id,
"question": question,
"file_type": file_type,
}
)
doc.save(ignore_permissions=True)
if lesson:
lesson_reference = frappe.get_doc("Lesson Reference", {"lesson": lesson})
else:
lesson_reference = frappe.get_doc(
{
"doctype": "Lesson Reference",
"parent": chapter,
"parenttype": "Course Chapter",
"parentfield": "lessons",
"idx": idx,
}
)
lesson_reference.update({"lesson": doc.name})
lesson_reference.save(ignore_permissions=True)
return doc.name
@frappe.whitelist()
def reorder_lesson(old_chapter, old_lesson_array, new_chapter, new_lesson_array):
if old_chapter == new_chapter:
sort_lessons(new_chapter, new_lesson_array)
else:
sort_lessons(old_chapter, old_lesson_array)
sort_lessons(new_chapter, new_lesson_array)
def sort_lessons(chapter, lesson_array):
lesson_array = json.loads(lesson_array)
for les in lesson_array:
ref = frappe.get_all("Lesson Reference", {"lesson": les}, ["name", "idx"])
if ref:
frappe.db.set_value(
"Lesson Reference",
ref[0].name,
{
"parent": chapter,
"idx": lesson_array.index(les) + 1,
},
)
@frappe.whitelist()
def reorder_chapter(chapter_array):
chapter_array = json.loads(chapter_array)
for chap in chapter_array:
ref = frappe.get_all("Chapter Reference", {"chapter": chap}, ["name", "idx"])
if ref:
frappe.db.set_value(
"Chapter Reference",
ref[0].name,
{
"idx": chapter_array.index(chap) + 1,
},
)

View File

@@ -75,7 +75,8 @@
"in_standard_filter": 1, "in_standard_filter": 1,
"label": "Course", "label": "Course",
"options": "LMS Course", "options": "LMS Course",
"reqd": 1 "reqd": 1,
"search_index": 1
}, },
{ {
"fieldname": "current_lesson", "fieldname": "current_lesson",
@@ -126,7 +127,7 @@
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"links": [], "links": [],
"modified": "2024-05-14 14:50:08.405033", "modified": "2024-10-30 12:44:16.103598",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "LMS", "module": "LMS",
"name": "LMS Enrollment", "name": "LMS Enrollment",

View File

@@ -10,19 +10,20 @@
"title", "title",
"host", "host",
"batch_name", "batch_name",
"event",
"column_break_astv", "column_break_astv",
"date",
"time",
"duration",
"section_break_glxh",
"description", "description",
"section_break_glxh",
"date",
"duration",
"column_break_spvt", "column_break_spvt",
"time",
"timezone", "timezone",
"password",
"auto_recording",
"section_break_yrpq", "section_break_yrpq",
"password",
"start_url", "start_url",
"column_break_yokr", "column_break_yokr",
"auto_recording",
"join_url" "join_url"
], ],
"fields": [ "fields": [
@@ -122,11 +123,18 @@
"fieldtype": "Select", "fieldtype": "Select",
"label": "Auto Recording", "label": "Auto Recording",
"options": "No Recording\nLocal\nCloud" "options": "No Recording\nLocal\nCloud"
},
{
"fieldname": "event",
"fieldtype": "Link",
"label": "Event",
"options": "Event",
"read_only": 1
} }
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"links": [], "links": [],
"modified": "2024-01-09 11:22:33.272341", "modified": "2024-10-31 15:41:35.540856",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "LMS", "module": "LMS",
"name": "LMS Live Class", "name": "LMS Live Class",

View File

@@ -16,6 +16,7 @@ class LMSLiveClass(Document):
if calendar: if calendar:
event = self.create_event() event = self.create_event()
self.add_event_participants(event, calendar) self.add_event_participants(event, calendar)
frappe.db.set_value(self.doctype, self.name, "event", event.name)
def create_event(self): def create_event(self):
start = f"{self.date} {self.time}" start = f"{self.date} {self.time}"

View File

@@ -76,6 +76,7 @@
"default": "0", "default": "0",
"fieldname": "payment_received", "fieldname": "payment_received",
"fieldtype": "Check", "fieldtype": "Check",
"in_standard_filter": 1,
"label": "Payment Received" "label": "Payment Received"
}, },
{ {
@@ -140,7 +141,7 @@
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"links": [], "links": [],
"modified": "2023-10-26 16:54:12.408274", "modified": "2024-10-31 15:33:39.420366",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "LMS", "module": "LMS",
"name": "LMS Payment", "name": "LMS Payment",

View File

@@ -86,32 +86,32 @@ def get_charts(data):
completed = 0 completed = 0
less_than_hundred = 0 less_than_hundred = 0
less_than_seventy = 0 less_than_seventy_one = 0
less_than_forty = 0 less_than_forty_one = 0
less_than_ten = 0 less_than_eleven = 0
for row in data: for row in data:
if row.progress == 100: if row.progress == 100:
completed += 1 completed += 1
elif row.progress < 100 and row.progress > 70: elif row.progress < 100 and row.progress > 70:
less_than_hundred += 1 less_than_hundred += 1
elif row.progress < 70 and row.progress > 40: elif row.progress < 71 and row.progress > 40:
less_than_seventy += 1 less_than_seventy_one += 1
elif row.progress < 40 and row.progress > 10: elif row.progress < 41 and row.progress > 10:
less_than_forty += 1 less_than_forty_one += 1
elif row.progress < 10: elif row.progress < 11:
less_than_ten += 1 less_than_eleven += 1
charts = { charts = {
"data": { "data": {
"labels": ["0-10", "10-40", "40-70", "70-99", "100"], "labels": ["0-10", "11-40", "41-70", "71-99", "100"],
"datasets": [ "datasets": [
{ {
"name": "Progress (%)", "name": "Progress (%)",
"values": [ "values": [
less_than_ten, less_than_eleven,
less_than_forty, less_than_forty_one,
less_than_seventy, less_than_seventy_one,
less_than_hundred, less_than_hundred,
completed, completed,
], ],

View File

@@ -109,7 +109,7 @@ def get_chapters(course):
chapter_details = frappe.db.get_value( chapter_details = frappe.db.get_value(
"Course Chapter", "Course Chapter",
{"name": chapter.chapter}, {"name": chapter.chapter},
["name", "title", "description"], ["name", "title"],
as_dict=True, as_dict=True,
) )
chapter.update(chapter_details) chapter.update(chapter_details)
@@ -157,11 +157,12 @@ def get_lesson_details(chapter, progress=False):
"file_type", "file_type",
"instructor_notes", "instructor_notes",
"course", "course",
"content",
], ],
as_dict=True, as_dict=True,
) )
lesson_details.number = f"{chapter.idx}.{row.idx}" lesson_details.number = f"{chapter.idx}.{row.idx}"
lesson_details.icon = get_lesson_icon(lesson_details.body) lesson_details.icon = get_lesson_icon(lesson_details.body, lesson_details.content)
if progress: if progress:
lesson_details.is_complete = get_progress(lesson_details.course, lesson_details.name) lesson_details.is_complete = get_progress(lesson_details.course, lesson_details.name)
@@ -170,20 +171,38 @@ def get_lesson_details(chapter, progress=False):
return lessons return lessons
def get_lesson_icon(content): def get_lesson_icon(body, content):
icon = None if content:
macros = find_macros(content) content = json.loads(content)
for block in content.get("blocks"):
if block.get("type") == "upload" and block.get("data").get("file_type").lower() in [
"mp4",
"webm",
"ogg",
"mov",
]:
return "icon-youtube"
if block.get("type") == "embed" and block.get("data").get("service") in [
"youtube",
"vimeo",
]:
return "icon-youtube"
if block.get("type") == "quiz":
return "icon-quiz"
return "icon-list"
macros = find_macros(body)
for macro in macros: for macro in macros:
if macro[0] == "YouTubeVideo" or macro[0] == "Video": if macro[0] == "YouTubeVideo" or macro[0] == "Video":
icon = "icon-youtube" return "icon-youtube"
elif macro[0] == "Quiz": elif macro[0] == "Quiz":
icon = "icon-quiz" return "icon-quiz"
if not icon: return "icon-list"
icon = "icon-list"
return icon
@frappe.whitelist(allow_guest=True) @frappe.whitelist(allow_guest=True)
@@ -1027,23 +1046,13 @@ def get_course_details(course):
"currency", "currency",
"amount_usd", "amount_usd",
"enable_certification", "enable_certification",
"lessons",
"enrollments",
"rating",
], ],
as_dict=1, as_dict=1,
) )
course_details.tags = course_details.tags.split(",") if course_details.tags else [] course_details.tags = course_details.tags.split(",") if course_details.tags else []
course_details.lesson_count = get_lesson_count(course_details.name)
course_details.enrollment_count = frappe.db.count(
"LMS Enrollment", {"course": course_details.name, "member_type": "Student"}
)
course_details.enrollment_count_formatted = format_number(
course_details.enrollment_count
)
avg_rating = get_average_rating(course_details.name) or 0
course_details.avg_rating = flt(
avg_rating, frappe.get_system_settings("float_precision") or 3
)
course_details.instructors = get_instructors(course_details.name) course_details.instructors = get_instructors(course_details.name)
if course_details.paid_course: if course_details.paid_course:
@@ -1092,14 +1101,14 @@ def get_categorized_courses(courses):
): ):
new.append(course) new.append(course)
if course.membership and course.published: if course.membership:
enrolled.append(course) enrolled.append(course)
elif course.is_instructor: elif course.is_instructor:
created.append(course) created.append(course)
categories = [live, enrolled, created] categories = [live, enrolled, created]
for category in categories: for category in categories:
category.sort(key=lambda x: x.enrollment_count, reverse=True) category.sort(key=lambda x: x.enrollments, reverse=True)
live.sort(key=lambda x: x.featured, reverse=True) live.sort(key=lambda x: x.featured, reverse=True)
@@ -1124,7 +1133,7 @@ def get_course_outline(course, progress=False):
chapter_details = frappe.db.get_value( chapter_details = frappe.db.get_value(
"Course Chapter", "Course Chapter",
chapter.chapter, chapter.chapter,
["name", "title", "description"], ["name", "title"],
as_dict=True, as_dict=True,
) )
chapter_details["idx"] = chapter.idx chapter_details["idx"] = chapter.idx

5361
lms/locale/ar.po Normal file

File diff suppressed because it is too large Load Diff

5361
lms/locale/bs.po Normal file

File diff suppressed because it is too large Load Diff

5361
lms/locale/de.po Normal file

File diff suppressed because it is too large Load Diff

5361
lms/locale/eo.po Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

5361
lms/locale/fa.po Normal file

File diff suppressed because it is too large Load Diff

5361
lms/locale/fr.po Normal file

File diff suppressed because it is too large Load Diff

5361
lms/locale/hu.po Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -7,8 +7,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Frappe LMS VERSION\n" "Project-Id-Version: Frappe LMS VERSION\n"
"Report-Msgid-Bugs-To: jannat@frappe.io\n" "Report-Msgid-Bugs-To: jannat@frappe.io\n"
"POT-Creation-Date: 2024-10-18 16:04+0000\n" "POT-Creation-Date: 2024-11-01 16:04+0000\n"
"PO-Revision-Date: 2024-10-18 16:04+0000\n" "PO-Revision-Date: 2024-11-01 16:04+0000\n"
"Last-Translator: jannat@frappe.io\n" "Last-Translator: jannat@frappe.io\n"
"Language-Team: jannat@frappe.io\n" "Language-Team: jannat@frappe.io\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@@ -60,6 +60,10 @@ msgstr ""
msgid "<span style=\"font-size: 18px;\"><b>Statistics</b></span>" msgid "<span style=\"font-size: 18px;\"><b>Statistics</b></span>"
msgstr "" msgstr ""
#: frontend/src/pages/CourseForm.vue:32
msgid "A one line introduction to the course that appears on the course card"
msgstr ""
#: frontend/src/pages/ProfileAbout.vue:4 #: frontend/src/pages/ProfileAbout.vue:4
msgid "About" msgid "About"
msgstr "" msgstr ""
@@ -100,7 +104,6 @@ msgstr ""
#: frontend/src/components/CourseOutline.vue:11 #: frontend/src/components/CourseOutline.vue:11
#: frontend/src/components/CreateOutline.vue:18 #: frontend/src/components/CreateOutline.vue:18
#: frontend/src/components/Modals/ChapterModal.vue:5 #: frontend/src/components/Modals/ChapterModal.vue:5
#: frontend/src/components/Modals/ChapterModal.vue:9
msgid "Add Chapter" msgid "Add Chapter"
msgstr "" msgstr ""
@@ -223,7 +226,7 @@ msgstr ""
#. Label of the amount (Currency) field in DocType 'Web Form' #. Label of the amount (Currency) field in DocType 'Web Form'
#. Label of the amount (Currency) field in DocType 'LMS Batch' #. Label of the amount (Currency) field in DocType 'LMS Batch'
#. Label of the amount (Currency) field in DocType 'LMS Payment' #. Label of the amount (Currency) field in DocType 'LMS Payment'
#: frontend/src/pages/BatchForm.vue:198 lms/fixtures/custom_field.json #: frontend/src/pages/BatchForm.vue:208 lms/fixtures/custom_field.json
#: lms/lms/doctype/lms_batch/lms_batch.json #: lms/lms/doctype/lms_batch/lms_batch.json
#: lms/lms/doctype/lms_payment/lms_payment.json #: lms/lms/doctype/lms_payment/lms_payment.json
#: lms/public/js/common_functions.js:379 #: lms/public/js/common_functions.js:379
@@ -267,6 +270,14 @@ msgstr ""
msgid "Answer" msgid "Answer"
msgstr "" msgstr ""
#: frontend/src/pages/CourseForm.vue:76 frontend/src/pages/CourseForm.vue:94
msgid "Appears on the course card in the course list"
msgstr ""
#: frontend/src/pages/BatchForm.vue:55 frontend/src/pages/BatchForm.vue:73
msgid "Appears when the batch URL is shared on any online platform"
msgstr ""
#: frontend/src/pages/JobDetail.vue:131 #: frontend/src/pages/JobDetail.vue:131
msgid "Applications Received" msgid "Applications Received"
msgstr "" msgstr ""
@@ -465,7 +476,7 @@ msgid "Batch Description"
msgstr "" msgstr ""
#. Label of the batch_details (Text Editor) field in DocType 'LMS Batch' #. Label of the batch_details (Text Editor) field in DocType 'LMS Batch'
#: frontend/src/pages/BatchForm.vue:89 lms/lms/doctype/lms_batch/lms_batch.json #: frontend/src/pages/BatchForm.vue:97 lms/lms/doctype/lms_batch/lms_batch.json
#: lms/public/js/common_functions.js:349 #: lms/public/js/common_functions.js:349
#: lms/templates/emails/batch_confirmation.html:30 #: lms/templates/emails/batch_confirmation.html:30
msgid "Batch Details" msgid "Batch Details"
@@ -632,8 +643,8 @@ msgstr ""
#. Label of the category (Link) field in DocType 'LMS Batch' #. Label of the category (Link) field in DocType 'LMS Batch'
#. Label of the category (Data) field in DocType 'LMS Category' #. Label of the category (Data) field in DocType 'LMS Category'
#. Label of the category (Link) field in DocType 'LMS Course' #. Label of the category (Link) field in DocType 'LMS Course'
#: frontend/src/pages/BatchForm.vue:179 frontend/src/pages/Batches.vue:16 #: frontend/src/pages/BatchForm.vue:189 frontend/src/pages/Batches.vue:16
#: frontend/src/pages/CourseForm.vue:116 frontend/src/pages/Courses.vue:17 #: frontend/src/pages/CourseForm.vue:139 frontend/src/pages/Courses.vue:17
#: frontend/src/pages/JobDetail.vue:102 #: frontend/src/pages/JobDetail.vue:102
#: lms/lms/doctype/lms_batch/lms_batch.json #: lms/lms/doctype/lms_batch/lms_batch.json
#: lms/lms/doctype/lms_category/lms_category.json #: lms/lms/doctype/lms_category/lms_category.json
@@ -934,7 +945,7 @@ msgstr ""
msgid "Completed" msgid "Completed"
msgstr "" msgstr ""
#: frontend/src/pages/CourseForm.vue:168 #: frontend/src/pages/CourseForm.vue:192
msgid "Completion Certificate" msgid "Completion Certificate"
msgstr "" msgstr ""
@@ -984,7 +995,7 @@ msgstr ""
msgid "Contract" msgid "Contract"
msgstr "" msgstr ""
#: lms/lms/utils.py:423 #: lms/lms/utils.py:442
msgid "Cookie Policy" msgid "Cookie Policy"
msgstr "" msgstr ""
@@ -1105,7 +1116,7 @@ msgstr ""
msgid "Course Data" msgid "Course Data"
msgstr "" msgstr ""
#: frontend/src/pages/CourseForm.vue:34 #: frontend/src/pages/CourseForm.vue:41
msgid "Course Description" msgid "Course Description"
msgstr "" msgstr ""
@@ -1114,7 +1125,7 @@ msgstr ""
msgid "Course Evaluator" msgid "Course Evaluator"
msgstr "" msgstr ""
#: frontend/src/pages/CourseForm.vue:64 #: frontend/src/pages/CourseForm.vue:54
msgid "Course Image" msgid "Course Image"
msgstr "" msgstr ""
@@ -1137,7 +1148,7 @@ msgid "Course Name"
msgstr "" msgstr ""
#. Label of the course_price (Currency) field in DocType 'LMS Course' #. Label of the course_price (Currency) field in DocType 'LMS Course'
#: frontend/src/pages/CourseForm.vue:186 #: frontend/src/pages/CourseForm.vue:210
#: lms/lms/doctype/lms_course/lms_course.json #: lms/lms/doctype/lms_course/lms_course.json
msgid "Course Price" msgid "Course Price"
msgstr "" msgstr ""
@@ -1170,7 +1181,7 @@ msgstr ""
msgid "Course already added to the batch." msgid "Course already added to the batch."
msgstr "" msgstr ""
#: frontend/src/pages/CourseForm.vue:433 #: frontend/src/pages/CourseForm.vue:457
msgid "Course price and currency are mandatory for paid courses" msgid "Course price and currency are mandatory for paid courses"
msgstr "" msgstr ""
@@ -1208,6 +1219,10 @@ msgstr ""
msgid "Cover Image" msgid "Cover Image"
msgstr "" msgstr ""
#: frontend/src/components/Modals/ChapterModal.vue:9
msgid "Create"
msgstr ""
#: lms/lms/doctype/lms_certificate_evaluation/lms_certificate_evaluation.js:7 #: lms/lms/doctype/lms_certificate_evaluation/lms_certificate_evaluation.js:7
msgid "Create LMS Certificate" msgid "Create LMS Certificate"
msgstr "" msgstr ""
@@ -1216,7 +1231,11 @@ msgstr ""
msgid "Create LMS Certificate Evaluation" msgid "Create LMS Certificate Evaluation"
msgstr "" msgstr ""
#: lms/templates/onboarding_header.html:19 #: frontend/src/pages/Batches.vue:110
msgid "Create a Batch"
msgstr ""
#: frontend/src/pages/Courses.vue:131 lms/templates/onboarding_header.html:19
msgid "Create a Course" msgid "Create a Course"
msgstr "" msgstr ""
@@ -1232,7 +1251,7 @@ msgstr ""
#. Label of the currency (Link) field in DocType 'LMS Batch' #. Label of the currency (Link) field in DocType 'LMS Batch'
#. Label of the currency (Link) field in DocType 'LMS Course' #. Label of the currency (Link) field in DocType 'LMS Course'
#. Label of the currency (Link) field in DocType 'LMS Payment' #. Label of the currency (Link) field in DocType 'LMS Payment'
#: frontend/src/pages/BatchForm.vue:206 frontend/src/pages/CourseForm.vue:193 #: frontend/src/pages/BatchForm.vue:216 frontend/src/pages/CourseForm.vue:217
#: lms/fixtures/custom_field.json lms/lms/doctype/lms_batch/lms_batch.json #: lms/fixtures/custom_field.json lms/lms/doctype/lms_batch/lms_batch.json
#: lms/lms/doctype/lms_course/lms_course.json #: lms/lms/doctype/lms_course/lms_course.json
#: lms/lms/doctype/lms_payment/lms_payment.json #: lms/lms/doctype/lms_payment/lms_payment.json
@@ -1290,7 +1309,7 @@ msgstr ""
#. Label of the section_break_glxh (Section Break) field in DocType 'LMS Live #. Label of the section_break_glxh (Section Break) field in DocType 'LMS Live
#. Class' #. Class'
#: frontend/src/pages/BatchForm.vue:102 #: frontend/src/pages/BatchForm.vue:110
#: lms/lms/doctype/lms_live_class/lms_live_class.json #: lms/lms/doctype/lms_live_class/lms_live_class.json
msgid "Date and Time" msgid "Date and Time"
msgstr "" msgstr ""
@@ -1338,7 +1357,6 @@ msgstr ""
#. Label of the description (Markdown Editor) field in DocType 'Cohort' #. Label of the description (Markdown Editor) field in DocType 'Cohort'
#. Label of the description (Markdown Editor) field in DocType 'Cohort #. Label of the description (Markdown Editor) field in DocType 'Cohort
#. Subgroup' #. Subgroup'
#. Label of the description (Small Text) field in DocType 'Course Chapter'
#. Label of the description (Small Text) field in DocType 'LMS Badge' #. Label of the description (Small Text) field in DocType 'LMS Badge'
#. Label of the description (Small Text) field in DocType 'LMS Batch' #. Label of the description (Small Text) field in DocType 'LMS Batch'
#. Label of the description (Markdown Editor) field in DocType 'LMS Batch Old' #. Label of the description (Markdown Editor) field in DocType 'LMS Batch Old'
@@ -1347,12 +1365,11 @@ msgstr ""
#. Label of the description (Text) field in DocType 'LMS Live Class' #. Label of the description (Text) field in DocType 'LMS Live Class'
#. Label of the description (Small Text) field in DocType 'Work Experience' #. Label of the description (Small Text) field in DocType 'Work Experience'
#: frontend/src/components/Modals/LiveClassModal.vue:73 #: frontend/src/components/Modals/LiveClassModal.vue:73
#: frontend/src/pages/BatchForm.vue:83 frontend/src/pages/JobCreation.vue:43 #: frontend/src/pages/BatchForm.vue:90 frontend/src/pages/JobCreation.vue:43
#: lms/job/doctype/job_opportunity/job_opportunity.json #: lms/job/doctype/job_opportunity/job_opportunity.json
#: lms/lms/doctype/certification/certification.json #: lms/lms/doctype/certification/certification.json
#: lms/lms/doctype/cohort/cohort.json #: lms/lms/doctype/cohort/cohort.json
#: lms/lms/doctype/cohort_subgroup/cohort_subgroup.json #: lms/lms/doctype/cohort_subgroup/cohort_subgroup.json
#: lms/lms/doctype/course_chapter/course_chapter.json
#: lms/lms/doctype/lms_badge/lms_badge.json #: lms/lms/doctype/lms_badge/lms_badge.json
#: lms/lms/doctype/lms_batch/lms_batch.json #: lms/lms/doctype/lms_batch/lms_batch.json
#: lms/lms/doctype/lms_batch_old/lms_batch_old.json #: lms/lms/doctype/lms_batch_old/lms_batch_old.json
@@ -1374,7 +1391,7 @@ msgstr ""
msgid "Details" msgid "Details"
msgstr "" msgstr ""
#: frontend/src/pages/CourseForm.vue:163 #: frontend/src/pages/CourseForm.vue:187
msgid "Disable Self Enrollment" msgid "Disable Self Enrollment"
msgstr "" msgstr ""
@@ -1449,13 +1466,14 @@ msgstr ""
#: frontend/src/components/BatchOverlay.vue:93 #: frontend/src/components/BatchOverlay.vue:93
#: frontend/src/components/CourseCardOverlay.vue:86 #: frontend/src/components/CourseCardOverlay.vue:86
#: frontend/src/components/Modals/ChapterModal.vue:9
#: frontend/src/pages/JobDetail.vue:31 frontend/src/pages/Lesson.vue:70 #: frontend/src/pages/JobDetail.vue:31 frontend/src/pages/Lesson.vue:70
#: frontend/src/pages/Profile.vue:32 #: frontend/src/pages/Profile.vue:32
msgid "Edit" msgid "Edit"
msgstr "" msgstr ""
#: frontend/src/components/CourseOutline.vue:106 #: frontend/src/components/CourseOutline.vue:106
#: frontend/src/components/Modals/ChapterModal.vue:9 #: frontend/src/components/Modals/ChapterModal.vue:5
msgid "Edit Chapter" msgid "Edit Chapter"
msgstr "" msgstr ""
@@ -1531,7 +1549,7 @@ msgstr ""
#. Label of the end_date (Date) field in DocType 'Cohort' #. Label of the end_date (Date) field in DocType 'Cohort'
#. Label of the end_date (Date) field in DocType 'LMS Batch' #. Label of the end_date (Date) field in DocType 'LMS Batch'
#: frontend/src/pages/BatchForm.vue:114 lms/lms/doctype/cohort/cohort.json #: frontend/src/pages/BatchForm.vue:122 lms/lms/doctype/cohort/cohort.json
#: lms/lms/doctype/lms_batch/lms_batch.json #: lms/lms/doctype/lms_batch/lms_batch.json
#: lms/public/js/common_functions.js:282 #: lms/public/js/common_functions.js:282
msgid "End Date" msgid "End Date"
@@ -1549,7 +1567,7 @@ msgstr ""
#. Label of the end_time (Time) field in DocType 'LMS Certificate Evaluation' #. Label of the end_time (Time) field in DocType 'LMS Certificate Evaluation'
#. Label of the end_time (Time) field in DocType 'LMS Certificate Request' #. Label of the end_time (Time) field in DocType 'LMS Certificate Request'
#. Label of the end_time (Time) field in DocType 'Scheduled Flow' #. Label of the end_time (Time) field in DocType 'Scheduled Flow'
#: frontend/src/pages/BatchForm.vue:128 #: frontend/src/pages/BatchForm.vue:136
#: frontend/src/pages/ProfileEvaluator.vue:18 #: frontend/src/pages/ProfileEvaluator.vue:18
#: lms/lms/doctype/evaluator_schedule/evaluator_schedule.json #: lms/lms/doctype/evaluator_schedule/evaluator_schedule.json
#: lms/lms/doctype/lms_batch/lms_batch.json #: lms/lms/doctype/lms_batch/lms_batch.json
@@ -1585,13 +1603,15 @@ msgstr ""
msgid "Enrollment Count" msgid "Enrollment Count"
msgstr "" msgstr ""
#: lms/lms/utils.py:1683 #: lms/lms/utils.py:1692
msgid "Enrollment Failed" msgid "Enrollment Failed"
msgstr "" msgstr ""
#. Label of the enrollments (Data) field in DocType 'LMS Course'
#. Label of a chart in the LMS Workspace #. Label of a chart in the LMS Workspace
#. Label of a shortcut in the LMS Workspace #. Label of a shortcut in the LMS Workspace
#: frontend/src/pages/Statistics.vue:45 lms/lms/workspace/lms/lms.json #: frontend/src/pages/Statistics.vue:45
#: lms/lms/doctype/lms_course/lms_course.json lms/lms/workspace/lms/lms.json
msgid "Enrollments" msgid "Enrollments"
msgstr "" msgstr ""
@@ -1633,7 +1653,7 @@ msgid "Evaluation Details"
msgstr "" msgstr ""
#. Label of the evaluation_end_date (Date) field in DocType 'LMS Batch' #. Label of the evaluation_end_date (Date) field in DocType 'LMS Batch'
#: frontend/src/pages/BatchForm.vue:155 #: frontend/src/pages/BatchForm.vue:165
#: lms/lms/doctype/lms_batch/lms_batch.json #: lms/lms/doctype/lms_batch/lms_batch.json
#: lms/public/js/common_functions.js:333 #: lms/public/js/common_functions.js:333
msgid "Evaluation End Date" msgid "Evaluation End Date"
@@ -1695,6 +1715,10 @@ msgstr ""
msgid "Event" msgid "Event"
msgstr "" msgstr ""
#: frontend/src/pages/BatchForm.vue:144
msgid "Example: IST (+5:30)"
msgstr ""
#. Label of the exercise (Link) field in DocType 'Exercise Latest Submission' #. Label of the exercise (Link) field in DocType 'Exercise Latest Submission'
#. Label of the exercise (Link) field in DocType 'Exercise Submission' #. Label of the exercise (Link) field in DocType 'Exercise Submission'
#: lms/lms/doctype/exercise_latest_submission/exercise_latest_submission.json #: lms/lms/doctype/exercise_latest_submission/exercise_latest_submission.json
@@ -1765,7 +1789,7 @@ msgstr ""
#. Label of the featured (Check) field in DocType 'LMS Course' #. Label of the featured (Check) field in DocType 'LMS Course'
#: frontend/src/components/CourseCard.vue:16 #: frontend/src/components/CourseCard.vue:16
#: frontend/src/pages/CourseForm.vue:156 #: frontend/src/pages/CourseForm.vue:180
#: lms/lms/doctype/lms_course/lms_course.json #: lms/lms/doctype/lms_course/lms_course.json
msgid "Featured" msgid "Featured"
msgstr "" msgstr ""
@@ -2028,6 +2052,10 @@ msgstr ""
msgid "Icon" msgid "Icon"
msgstr "" msgstr ""
#: frontend/src/components/LessonHelp.vue:68
msgid "If Include in Preview is enabled for a lesson then the lesson will also be accessible to non logged in users."
msgstr ""
#: lms/templates/emails/mentor_request_creation_email.html:5 #: lms/templates/emails/mentor_request_creation_email.html:5
msgid "If you are not any more interested to mentor the course" msgid "If you are not any more interested to mentor the course"
msgstr "" msgstr ""
@@ -2164,7 +2192,7 @@ msgstr ""
#. Label of the instructors (Table MultiSelect) field in DocType 'LMS Batch' #. Label of the instructors (Table MultiSelect) field in DocType 'LMS Batch'
#. Label of the instructors (Table MultiSelect) field in DocType 'LMS Course' #. Label of the instructors (Table MultiSelect) field in DocType 'LMS Course'
#: frontend/src/pages/BatchForm.vue:77 frontend/src/pages/CourseForm.vue:123 #: frontend/src/pages/BatchForm.vue:85 frontend/src/pages/CourseForm.vue:146
#: lms/lms/doctype/lms_batch/lms_batch.json #: lms/lms/doctype/lms_batch/lms_batch.json
#: lms/lms/doctype/lms_course/lms_course.json #: lms/lms/doctype/lms_course/lms_course.json
msgid "Instructors" msgid "Instructors"
@@ -2309,7 +2337,7 @@ msgstr ""
msgid "Jobs" msgid "Jobs"
msgstr "" msgstr ""
#: frontend/src/components/LiveClass.vue:53 #: frontend/src/components/LiveClass.vue:54
#: lms/templates/upcoming_evals.html:15 #: lms/templates/upcoming_evals.html:15
msgid "Join" msgid "Join"
msgstr "" msgstr ""
@@ -2323,6 +2351,10 @@ msgstr ""
msgid "Join URL" msgid "Join URL"
msgstr "" msgstr ""
#: frontend/src/pages/CourseForm.vue:128
msgid "Keywords for the course"
msgstr ""
#. Name of a Workspace #. Name of a Workspace
#: lms/lms/workspace/lms/lms.json #: lms/lms/workspace/lms/lms.json
msgid "LMS" msgid "LMS"
@@ -2576,9 +2608,11 @@ msgstr ""
#. Label of the lessons (Table) field in DocType 'Course Chapter' #. Label of the lessons (Table) field in DocType 'Course Chapter'
#. Group in Course Chapter's connections #. Group in Course Chapter's connections
#. Label of the lessons (Data) field in DocType 'LMS Course'
#: frontend/src/components/CourseCard.vue:34 #: frontend/src/components/CourseCard.vue:34
#: frontend/src/components/CourseCardOverlay.vue:96 #: frontend/src/components/CourseCardOverlay.vue:96
#: lms/lms/doctype/course_chapter/course_chapter.json #: lms/lms/doctype/course_chapter/course_chapter.json
#: lms/lms/doctype/lms_course/lms_course.json
msgid "Lessons" msgid "Lessons"
msgstr "" msgstr ""
@@ -2751,7 +2785,7 @@ msgid "Maximun Attempts"
msgstr "" msgstr ""
#. Label of the medium (Select) field in DocType 'LMS Batch' #. Label of the medium (Select) field in DocType 'LMS Batch'
#: frontend/src/pages/BatchForm.vue:174 #: frontend/src/pages/BatchForm.vue:184
#: lms/lms/doctype/lms_batch/lms_batch.json #: lms/lms/doctype/lms_batch/lms_batch.json
#: lms/public/js/common_functions.js:309 #: lms/public/js/common_functions.js:309
msgid "Medium" msgid "Medium"
@@ -2899,7 +2933,7 @@ msgid "Mentors"
msgstr "" msgstr ""
#. Label of the meta_image (Attach Image) field in DocType 'LMS Batch' #. Label of the meta_image (Attach Image) field in DocType 'LMS Batch'
#: frontend/src/pages/BatchForm.vue:53 lms/lms/doctype/lms_batch/lms_batch.json #: frontend/src/pages/BatchForm.vue:36 lms/lms/doctype/lms_batch/lms_batch.json
#: lms/public/js/common_functions.js:362 #: lms/public/js/common_functions.js:362
msgid "Meta Image" msgid "Meta Image"
msgstr "" msgstr ""
@@ -2935,11 +2969,11 @@ msgstr ""
msgid "Modified By" msgid "Modified By"
msgstr "" msgstr ""
#: lms/lms/api.py:190 #: lms/lms/api.py:191
msgid "Module Name is incorrect or does not exist." msgid "Module Name is incorrect or does not exist."
msgstr "" msgstr ""
#: lms/lms/api.py:186 #: lms/lms/api.py:187
msgid "Module is incorrect." msgid "Module is incorrect."
msgstr "" msgstr ""
@@ -3006,11 +3040,11 @@ msgstr ""
msgid "New Sign Up" msgid "New Sign Up"
msgstr "" msgstr ""
#: lms/lms/utils.py:613 #: lms/lms/utils.py:632
msgid "New comment in batch {0}" msgid "New comment in batch {0}"
msgstr "" msgstr ""
#: lms/lms/utils.py:606 #: lms/lms/utils.py:625
msgid "New reply on the topic {0} in course {1}" msgid "New reply on the topic {0} in course {1}"
msgstr "" msgstr ""
@@ -3048,6 +3082,10 @@ msgstr ""
msgid "No announcements" msgid "No announcements"
msgstr "" msgstr ""
#: frontend/src/pages/Batches.vue:125
msgid "No batches found"
msgstr ""
#: lms/templates/certificates_section.html:23 #: lms/templates/certificates_section.html:23
msgid "No certificates" msgid "No certificates"
msgstr "" msgstr ""
@@ -3056,6 +3094,10 @@ msgstr ""
msgid "No courses created" msgid "No courses created"
msgstr "" msgstr ""
#: frontend/src/pages/Courses.vue:146
msgid "No courses found"
msgstr ""
#: lms/templates/courses_under_review.html:14 #: lms/templates/courses_under_review.html:14
msgid "No courses under review" msgid "No courses under review"
msgstr "" msgstr ""
@@ -3068,7 +3110,7 @@ msgstr ""
msgid "No jobs posted" msgid "No jobs posted"
msgstr "" msgstr ""
#: frontend/src/components/LiveClass.vue:59 #: frontend/src/components/LiveClass.vue:60
msgid "No live classes scheduled" msgid "No live classes scheduled"
msgstr "" msgstr ""
@@ -3084,12 +3126,12 @@ msgstr ""
msgid "No {0}" msgid "No {0}"
msgstr "" msgstr ""
#: frontend/src/pages/Batches.vue:88 #: frontend/src/pages/Batches.vue:84
msgid "No {0} batches found" msgid "No {0} batches"
msgstr "" msgstr ""
#: frontend/src/pages/Courses.vue:110 #: frontend/src/pages/Courses.vue:106
msgid "No {0} courses found" msgid "No {0} courses"
msgstr "" msgstr ""
#: lms/templates/quiz/quiz.html:147 #: lms/templates/quiz/quiz.html:147
@@ -3144,6 +3186,10 @@ msgstr ""
msgid "Notify me when available" msgid "Notify me when available"
msgstr "" msgstr ""
#: frontend/src/pages/BatchForm.vue:161
msgid "Number of seats available"
msgstr ""
#. Label of the sb_00 (Section Break) field in DocType 'Zoom Settings' #. Label of the sb_00 (Section Break) field in DocType 'Zoom Settings'
#: lms/lms/doctype/zoom_settings/zoom_settings.json #: lms/lms/doctype/zoom_settings/zoom_settings.json
msgid "OAuth Client ID" msgid "OAuth Client ID"
@@ -3176,7 +3222,7 @@ msgstr ""
msgid "Only files of type {0} will be accepted." msgid "Only files of type {0} will be accepted."
msgstr "" msgstr ""
#: frontend/src/pages/CourseForm.vue:449 frontend/src/utils/index.js:509 #: frontend/src/pages/CourseForm.vue:473 frontend/src/utils/index.js:518
msgid "Only image file is allowed." msgid "Only image file is allowed."
msgstr "" msgstr ""
@@ -3284,14 +3330,14 @@ msgid "Pages"
msgstr "" msgstr ""
#. Label of the paid_batch (Check) field in DocType 'LMS Batch' #. Label of the paid_batch (Check) field in DocType 'LMS Batch'
#: frontend/src/pages/BatchForm.vue:194 #: frontend/src/pages/BatchForm.vue:204
#: lms/lms/doctype/lms_batch/lms_batch.json #: lms/lms/doctype/lms_batch/lms_batch.json
#: lms/public/js/common_functions.js:373 #: lms/public/js/common_functions.js:373
msgid "Paid Batch" msgid "Paid Batch"
msgstr "" msgstr ""
#. Label of the paid_course (Check) field in DocType 'LMS Course' #. Label of the paid_course (Check) field in DocType 'LMS Course'
#: frontend/src/pages/CourseForm.vue:181 #: frontend/src/pages/CourseForm.vue:205
#: lms/lms/doctype/lms_course/lms_course.json #: lms/lms/doctype/lms_course/lms_course.json
msgid "Paid Course" msgid "Paid Course"
msgstr "" msgstr ""
@@ -3333,9 +3379,13 @@ msgstr ""
msgid "Password" msgid "Password"
msgstr "" msgstr ""
#: frontend/src/pages/CourseForm.vue:104
msgid "Paste the youtube link of a short video introducing the course"
msgstr ""
#. Label of the payment (Link) field in DocType 'Batch Student' #. Label of the payment (Link) field in DocType 'Batch Student'
#. Label of the payment (Link) field in DocType 'LMS Enrollment' #. Label of the payment (Link) field in DocType 'LMS Enrollment'
#: frontend/src/pages/BatchForm.vue:188 #: frontend/src/pages/BatchForm.vue:198
#: lms/lms/doctype/batch_student/batch_student.json #: lms/lms/doctype/batch_student/batch_student.json
#: lms/lms/doctype/lms_enrollment/lms_enrollment.json #: lms/lms/doctype/lms_enrollment/lms_enrollment.json
msgid "Payment" msgid "Payment"
@@ -3427,7 +3477,7 @@ msgstr ""
msgid "Phone Number" msgid "Phone Number"
msgstr "" msgstr ""
#: frontend/src/components/CourseCardOverlay.vue:143 #: frontend/src/components/CourseCardOverlay.vue:141
msgid "Please Login" msgid "Please Login"
msgstr "" msgstr ""
@@ -3488,7 +3538,7 @@ msgstr ""
msgid "Please login to access this page." msgid "Please login to access this page."
msgstr "" msgstr ""
#: lms/lms/api.py:182 #: lms/lms/api.py:183
msgid "Please login to continue with payment." msgid "Please login to continue with payment."
msgstr "" msgstr ""
@@ -3578,7 +3628,7 @@ msgstr ""
msgid "Preview Image" msgid "Preview Image"
msgstr "" msgstr ""
#: frontend/src/pages/CourseForm.vue:86 #: frontend/src/pages/CourseForm.vue:102
msgid "Preview Video" msgid "Preview Video"
msgstr "" msgstr ""
@@ -3588,7 +3638,7 @@ msgstr ""
#. Label of the pricing_tab (Tab Break) field in DocType 'LMS Batch' #. Label of the pricing_tab (Tab Break) field in DocType 'LMS Batch'
#. Label of the pricing_tab (Tab Break) field in DocType 'LMS Course' #. Label of the pricing_tab (Tab Break) field in DocType 'LMS Course'
#: frontend/src/pages/CourseForm.vue:175 #: frontend/src/pages/CourseForm.vue:199
#: lms/lms/doctype/lms_batch/lms_batch.json #: lms/lms/doctype/lms_batch/lms_batch.json
#: lms/lms/doctype/lms_course/lms_course.json #: lms/lms/doctype/lms_course/lms_course.json
#: lms/public/js/common_functions.js:368 #: lms/public/js/common_functions.js:368
@@ -3606,7 +3656,7 @@ msgstr ""
msgid "Primary Subgroup" msgid "Primary Subgroup"
msgstr "" msgstr ""
#: lms/lms/utils.py:422 #: lms/lms/utils.py:441
msgid "Privacy Policy" msgid "Privacy Policy"
msgstr "" msgstr ""
@@ -3658,7 +3708,7 @@ msgstr ""
#. Label of the published (Check) field in DocType 'LMS Batch' #. Label of the published (Check) field in DocType 'LMS Batch'
#. Label of the published (Check) field in DocType 'LMS Course' #. Label of the published (Check) field in DocType 'LMS Course'
#: frontend/src/components/Modals/Event.vue:108 #: frontend/src/components/Modals/Event.vue:108
#: frontend/src/pages/BatchForm.vue:24 frontend/src/pages/CourseForm.vue:138 #: frontend/src/pages/BatchForm.vue:24 frontend/src/pages/CourseForm.vue:162
#: lms/lms/doctype/lms_batch/lms_batch.json #: lms/lms/doctype/lms_batch/lms_batch.json
#: lms/lms/doctype/lms_course/lms_course.json #: lms/lms/doctype/lms_course/lms_course.json
#: lms/public/js/common_functions.js:266 #: lms/public/js/common_functions.js:266
@@ -3671,7 +3721,7 @@ msgid "Published Courses"
msgstr "" msgstr ""
#. Label of the published_on (Date) field in DocType 'LMS Course' #. Label of the published_on (Date) field in DocType 'LMS Course'
#: frontend/src/pages/CourseForm.vue:142 #: frontend/src/pages/CourseForm.vue:166
#: lms/lms/doctype/lms_course/lms_course.json #: lms/lms/doctype/lms_course/lms_course.json
msgid "Published On" msgid "Published On"
msgstr "" msgstr ""
@@ -3792,11 +3842,13 @@ msgid "Quizzes"
msgstr "" msgstr ""
#. Label of the rating (Rating) field in DocType 'LMS Certificate Evaluation' #. Label of the rating (Rating) field in DocType 'LMS Certificate Evaluation'
#. Label of the rating (Data) field in DocType 'LMS Course'
#. Label of the rating (Rating) field in DocType 'LMS Course Review' #. Label of the rating (Rating) field in DocType 'LMS Course Review'
#: frontend/src/components/CourseCardOverlay.vue:109 #: frontend/src/components/CourseCardOverlay.vue:108
#: frontend/src/components/Modals/Event.vue:86 #: frontend/src/components/Modals/Event.vue:86
#: frontend/src/components/Modals/ReviewModal.vue:20 #: frontend/src/components/Modals/ReviewModal.vue:20
#: lms/lms/doctype/lms_certificate_evaluation/lms_certificate_evaluation.json #: lms/lms/doctype/lms_certificate_evaluation/lms_certificate_evaluation.json
#: lms/lms/doctype/lms_course/lms_course.json
#: lms/lms/doctype/lms_course_review/lms_course_review.json #: lms/lms/doctype/lms_course_review/lms_course_review.json
#: lms/templates/reviews.html:125 #: lms/templates/reviews.html:125
msgid "Rating" msgid "Rating"
@@ -3867,6 +3919,10 @@ msgstr ""
msgid "Related Courses" msgid "Related Courses"
msgstr "" msgstr ""
#: frontend/src/pages/BatchForm.vue:69 frontend/src/pages/CourseForm.vue:91
msgid "Remove"
msgstr ""
#: frontend/src/components/Modals/AnnouncementModal.vue:26 #: frontend/src/components/Modals/AnnouncementModal.vue:26
msgid "Reply To" msgid "Reply To"
msgstr "" msgstr ""
@@ -4022,7 +4078,7 @@ msgid "Search for an icon"
msgstr "" msgstr ""
#. Label of the seat_count (Int) field in DocType 'LMS Batch' #. Label of the seat_count (Int) field in DocType 'LMS Batch'
#: frontend/src/pages/BatchForm.vue:149 #: frontend/src/pages/BatchForm.vue:158
#: lms/lms/doctype/lms_batch/lms_batch.json #: lms/lms/doctype/lms_batch/lms_batch.json
#: lms/public/js/common_functions.js:327 #: lms/public/js/common_functions.js:327
msgid "Seat Count" msgid "Seat Count"
@@ -4066,7 +4122,7 @@ msgid "Set your Password"
msgstr "" msgstr ""
#: frontend/src/components/Modals/Settings.vue:7 #: frontend/src/components/Modals/Settings.vue:7
#: frontend/src/pages/BatchForm.vue:143 frontend/src/pages/CourseForm.vue:128 #: frontend/src/pages/BatchForm.vue:152 frontend/src/pages/CourseForm.vue:152
#: frontend/src/pages/ProfileRoles.vue:4 frontend/src/pages/QuizForm.vue:78 #: frontend/src/pages/ProfileRoles.vue:4 frontend/src/pages/QuizForm.vue:78
msgid "Settings" msgid "Settings"
msgstr "" msgstr ""
@@ -4076,11 +4132,15 @@ msgid "Share on"
msgstr "" msgstr ""
#. Label of the short_introduction (Small Text) field in DocType 'LMS Course' #. Label of the short_introduction (Small Text) field in DocType 'LMS Course'
#: frontend/src/pages/CourseForm.vue:29 #: frontend/src/pages/CourseForm.vue:30
#: lms/lms/doctype/lms_course/lms_course.json #: lms/lms/doctype/lms_course/lms_course.json
msgid "Short Introduction" msgid "Short Introduction"
msgstr "" msgstr ""
#: frontend/src/pages/BatchForm.vue:93
msgid "Short description of the batch"
msgstr ""
#. Label of the show_answer (Check) field in DocType 'LMS Assignment' #. Label of the show_answer (Check) field in DocType 'LMS Assignment'
#: lms/lms/doctype/lms_assignment/lms_assignment.json #: lms/lms/doctype/lms_assignment/lms_assignment.json
msgid "Show Answer" msgid "Show Answer"
@@ -4233,7 +4293,7 @@ msgstr ""
msgid "Stage" msgid "Stage"
msgstr "" msgstr ""
#: frontend/src/components/LiveClass.vue:45 frontend/src/components/Quiz.vue:65 #: frontend/src/components/LiveClass.vue:46 frontend/src/components/Quiz.vue:65
#: lms/templates/quiz/quiz.html:39 #: lms/templates/quiz/quiz.html:39
msgid "Start" msgid "Start"
msgstr "" msgstr ""
@@ -4241,7 +4301,7 @@ msgstr ""
#. Label of the start_date (Date) field in DocType 'Education Detail' #. Label of the start_date (Date) field in DocType 'Education Detail'
#. Label of the start_date (Date) field in DocType 'LMS Batch' #. Label of the start_date (Date) field in DocType 'LMS Batch'
#. Label of the start_date (Date) field in DocType 'LMS Batch Old' #. Label of the start_date (Date) field in DocType 'LMS Batch Old'
#: frontend/src/pages/BatchForm.vue:108 #: frontend/src/pages/BatchForm.vue:116
#: lms/lms/doctype/education_detail/education_detail.json #: lms/lms/doctype/education_detail/education_detail.json
#: lms/lms/doctype/lms_batch/lms_batch.json #: lms/lms/doctype/lms_batch/lms_batch.json
#: lms/lms/doctype/lms_batch_old/lms_batch_old.json #: lms/lms/doctype/lms_batch_old/lms_batch_old.json
@@ -4262,7 +4322,7 @@ msgstr ""
#. Label of the start_time (Time) field in DocType 'LMS Certificate Evaluation' #. Label of the start_time (Time) field in DocType 'LMS Certificate Evaluation'
#. Label of the start_time (Time) field in DocType 'LMS Certificate Request' #. Label of the start_time (Time) field in DocType 'LMS Certificate Request'
#. Label of the start_time (Time) field in DocType 'Scheduled Flow' #. Label of the start_time (Time) field in DocType 'Scheduled Flow'
#: frontend/src/pages/BatchForm.vue:122 #: frontend/src/pages/BatchForm.vue:130
#: frontend/src/pages/ProfileEvaluator.vue:15 #: frontend/src/pages/ProfileEvaluator.vue:15
#: lms/lms/doctype/evaluator_schedule/evaluator_schedule.json #: lms/lms/doctype/evaluator_schedule/evaluator_schedule.json
#: lms/lms/doctype/lms_batch/lms_batch.json #: lms/lms/doctype/lms_batch/lms_batch.json
@@ -4301,7 +4361,9 @@ msgstr ""
msgid "State" msgid "State"
msgstr "" msgstr ""
#. Label of the tab_4_tab (Tab Break) field in DocType 'LMS Course'
#. Label of the statistics (Check) field in DocType 'LMS Settings' #. Label of the statistics (Check) field in DocType 'LMS Settings'
#: lms/lms/doctype/lms_course/lms_course.json
#: lms/lms/doctype/lms_settings/lms_settings.json lms/www/lms.py:133 #: lms/lms/doctype/lms_settings/lms_settings.json lms/www/lms.py:133
msgid "Statistics" msgid "Statistics"
msgstr "" msgstr ""
@@ -4431,7 +4493,7 @@ msgstr ""
#: frontend/src/components/BatchCourses.vue:150 #: frontend/src/components/BatchCourses.vue:150
#: frontend/src/components/BatchOverlay.vue:135 #: frontend/src/components/BatchOverlay.vue:135
#: frontend/src/components/BatchStudents.vue:157 #: frontend/src/components/BatchStudents.vue:157
#: frontend/src/components/CourseCardOverlay.vue:163 #: frontend/src/components/CourseCardOverlay.vue:161
#: frontend/src/components/Modals/AssessmentModal.vue:73 #: frontend/src/components/Modals/AssessmentModal.vue:73
#: frontend/src/components/Modals/Event.vue:255 #: frontend/src/components/Modals/Event.vue:255
#: frontend/src/components/Modals/Event.vue:310 #: frontend/src/components/Modals/Event.vue:310
@@ -4505,7 +4567,7 @@ msgid "System Manager"
msgstr "" msgstr ""
#. Label of the tags (Data) field in DocType 'LMS Course' #. Label of the tags (Data) field in DocType 'LMS Course'
#: frontend/src/pages/CourseForm.vue:91 #: frontend/src/pages/CourseForm.vue:112
#: lms/lms/doctype/lms_course/lms_course.json #: lms/lms/doctype/lms_course/lms_course.json
msgid "Tags" msgid "Tags"
msgstr "" msgstr ""
@@ -4533,7 +4595,7 @@ msgstr ""
msgid "Temporarily Disabled" msgid "Temporarily Disabled"
msgstr "" msgstr ""
#: lms/lms/utils.py:421 #: lms/lms/utils.py:440
msgid "Terms of Use" msgid "Terms of Use"
msgstr "" msgstr ""
@@ -4585,10 +4647,18 @@ msgstr ""
msgid "The status of your application has changed." msgid "The status of your application has changed."
msgstr "" msgstr ""
#: frontend/src/pages/Batches.vue:129
msgid "There are no batches available at the moment. Keep an eye out, fresh learning experiences are on the way soon!"
msgstr ""
#: frontend/src/components/CreateOutline.vue:12 #: frontend/src/components/CreateOutline.vue:12
msgid "There are no chapters in this course. Create and manage chapters from here." msgid "There are no chapters in this course. Create and manage chapters from here."
msgstr "" msgstr ""
#: frontend/src/pages/Courses.vue:150
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:140 #: lms/lms/doctype/lms_batch/lms_batch.py:140
msgid "There are no seats available in this batch." msgid "There are no seats available in this batch."
msgstr "" msgstr ""
@@ -4620,7 +4690,7 @@ msgstr ""
msgid "This course has:" msgid "This course has:"
msgstr "" msgstr ""
#: lms/lms/utils.py:1563 #: lms/lms/utils.py:1572
msgid "This course is free." msgid "This course is free."
msgstr "" msgstr ""
@@ -4689,7 +4759,7 @@ msgstr ""
#. Label of the timezone (Data) field in DocType 'LMS Certificate Request' #. Label of the timezone (Data) field in DocType 'LMS Certificate Request'
#. Label of the timezone (Data) field in DocType 'LMS Live Class' #. Label of the timezone (Data) field in DocType 'LMS Live Class'
#: frontend/src/components/Modals/LiveClassModal.vue:44 #: frontend/src/components/Modals/LiveClassModal.vue:44
#: frontend/src/pages/BatchForm.vue:134 #: frontend/src/pages/BatchForm.vue:142
#: lms/lms/doctype/lms_batch/lms_batch.json #: lms/lms/doctype/lms_batch/lms_batch.json
#: lms/lms/doctype/lms_certificate_request/lms_certificate_request.json #: lms/lms/doctype/lms_certificate_request/lms_certificate_request.json
#: lms/lms/doctype/lms_live_class/lms_live_class.json #: lms/lms/doctype/lms_live_class/lms_live_class.json
@@ -4755,7 +4825,7 @@ msgstr ""
msgid "To Date is mandatory in Work Experience." msgid "To Date is mandatory in Work Experience."
msgstr "" msgstr ""
#: lms/lms/utils.py:1574 #: lms/lms/utils.py:1583
msgid "To join this batch, please contact the Administrator." msgid "To join this batch, please contact the Administrator."
msgstr "" msgstr ""
@@ -4872,7 +4942,7 @@ msgstr ""
#. Option for the 'Status' (Select) field in DocType 'Cohort' #. Option for the 'Status' (Select) field in DocType 'Cohort'
#. Label of the upcoming (Check) field in DocType 'LMS Course' #. Label of the upcoming (Check) field in DocType 'LMS Course'
#: frontend/src/pages/CourseForm.vue:151 lms/lms/doctype/cohort/cohort.json #: frontend/src/pages/CourseForm.vue:175 lms/lms/doctype/cohort/cohort.json
#: lms/lms/doctype/lms_course/lms_course.json #: lms/lms/doctype/lms_course/lms_course.json
msgid "Upcoming" msgid "Upcoming"
msgstr "" msgstr ""
@@ -4896,6 +4966,10 @@ msgstr ""
msgid "Update Password" msgid "Update Password"
msgstr "" msgstr ""
#: frontend/src/pages/BatchForm.vue:51 frontend/src/pages/CourseForm.vue:72
msgid "Upload"
msgstr ""
#: frontend/src/pages/AssignmentSubmission.vue:69 #: frontend/src/pages/AssignmentSubmission.vue:69
msgid "Upload File" msgid "Upload File"
msgstr "" msgstr ""
@@ -5018,6 +5092,10 @@ msgstr ""
msgid "Welcome to {0}!" msgid "Welcome to {0}!"
msgstr "" msgstr ""
#: frontend/src/components/LessonHelp.vue:63
msgid "What does include in preview mean?"
msgstr ""
#: lms/templates/courses_under_review.html:15 #: lms/templates/courses_under_review.html:15
msgid "When a course gets submitted for review, it will be listed here." msgid "When a course gets submitted for review, it will be listed here."
msgstr "" msgstr ""
@@ -5071,11 +5149,11 @@ msgstr ""
msgid "You already have an evaluation on {0} at {1} for the course {2}." msgid "You already have an evaluation on {0} at {1} for the course {2}."
msgstr "" msgstr ""
#: lms/lms/api.py:206 #: lms/lms/api.py:207
msgid "You are already enrolled for this batch." msgid "You are already enrolled for this batch."
msgstr "" msgstr ""
#: lms/lms/api.py:198 #: lms/lms/api.py:199
msgid "You are already enrolled for this course." msgid "You are already enrolled for this course."
msgstr "" msgstr ""
@@ -5087,6 +5165,10 @@ msgstr ""
msgid "You are not a mentor of the course {0}" msgid "You are not a mentor of the course {0}"
msgstr "" msgstr ""
#: frontend/src/pages/Courses.vue:134
msgid "You can add chapters and lessons to it."
msgstr ""
#: lms/templates/emails/lms_course_interest.html:13 #: lms/templates/emails/lms_course_interest.html:13
#: lms/templates/emails/lms_invite_request_approved.html:11 #: lms/templates/emails/lms_invite_request_approved.html:11
msgid "You can also copy-paste following link in your browser" msgid "You can also copy-paste following link in your browser"
@@ -5104,6 +5186,10 @@ msgstr ""
msgid "You can find their resume attached to this email." msgid "You can find their resume attached to this email."
msgstr "" msgstr ""
#: frontend/src/pages/Batches.vue:113
msgid "You can link courses and assessments to it."
msgstr ""
#: lms/lms/doctype/lms_certificate_request/lms_certificate_request.py:115 #: lms/lms/doctype/lms_certificate_request/lms_certificate_request.py:115
msgid "You cannot schedule evaluations after {0}." msgid "You cannot schedule evaluations after {0}."
msgstr "" msgstr ""
@@ -5145,7 +5231,7 @@ msgstr ""
msgid "You have been enrolled in this batch" msgid "You have been enrolled in this batch"
msgstr "" msgstr ""
#: frontend/src/components/CourseCardOverlay.vue:164 #: frontend/src/components/CourseCardOverlay.vue:162
msgid "You have been enrolled in this course" msgid "You have been enrolled in this course"
msgstr "" msgstr ""
@@ -5157,7 +5243,7 @@ msgstr ""
msgid "You haven't enrolled for any courses" msgid "You haven't enrolled for any courses"
msgstr "" msgstr ""
#: frontend/src/components/CourseCardOverlay.vue:144 #: frontend/src/components/CourseCardOverlay.vue:142
msgid "You need to login first to enroll for this course" msgid "You need to login first to enroll for this course"
msgstr "" msgstr ""
@@ -5275,7 +5361,7 @@ msgstr ""
msgid "you can" msgid "you can"
msgstr "" msgstr ""
#: lms/lms/api.py:731 lms/lms/api.py:739 #: lms/lms/api.py:732 lms/lms/api.py:740
msgid "{0} Settings not found" msgid "{0} Settings not found"
msgstr "" msgstr ""
@@ -5311,7 +5397,7 @@ msgstr ""
msgid "{0} is your evaluator" msgid "{0} is your evaluator"
msgstr "" msgstr ""
#: lms/lms/utils.py:690 #: lms/lms/utils.py:709
msgid "{0} mentioned you in a comment" msgid "{0} mentioned you in a comment"
msgstr "" msgstr ""
@@ -5319,11 +5405,11 @@ msgstr ""
msgid "{0} mentioned you in a comment in your batch." msgid "{0} mentioned you in a comment in your batch."
msgstr "" msgstr ""
#: lms/lms/utils.py:643 lms/lms/utils.py:649 #: lms/lms/utils.py:662 lms/lms/utils.py:668
msgid "{0} mentioned you in a comment in {1}" msgid "{0} mentioned you in a comment in {1}"
msgstr "" msgstr ""
#: lms/lms/utils.py:461 #: lms/lms/utils.py:480
msgid "{0}k" msgid "{0}k"
msgstr "" msgstr ""

5361
lms/locale/pl.po Normal file

File diff suppressed because it is too large Load Diff

5361
lms/locale/ru.po Normal file

File diff suppressed because it is too large Load Diff

5361
lms/locale/sv.po Normal file

File diff suppressed because it is too large Load Diff

5361
lms/locale/tr.po Normal file

File diff suppressed because it is too large Load Diff

5361
lms/locale/zh.po Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -91,3 +91,4 @@ lms.patches.v2_0.fix_progress_percentage
lms.patches.v2_0.add_discussion_topic_titles lms.patches.v2_0.add_discussion_topic_titles
lms.patches.v2_0.sidebar_settings lms.patches.v2_0.sidebar_settings
lms.patches.v2_0.delete_certificate_request_notification #18-09-2024 lms.patches.v2_0.delete_certificate_request_notification #18-09-2024
lms.patches.v2_0.add_course_statistics #21-10-2024

View File

@@ -0,0 +1,6 @@
import frappe
from lms.lms.api import update_course_statistics
def execute():
update_course_statistics()