Skip to content

Commit

Permalink
Merge pull request #928 from pateljannat/completion-certificate
Browse files Browse the repository at this point in the history
feat: completion certificate
  • Loading branch information
pateljannat authored Jul 12, 2024
2 parents 63d613a + 166996d commit 15330cb
Show file tree
Hide file tree
Showing 13 changed files with 173 additions and 227 deletions.
4 changes: 2 additions & 2 deletions frontend/src/components/AppSidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
</div>
<div
v-if="sidebarSettings.data?.web_pages?.length || isModerator"
class="pt-1"
class="mt-4"
>
<div
class="flex items-center justify-between pr-2 cursor-pointer"
Expand All @@ -36,7 +36,7 @@
/>
</span>
<span class="ml-2">
{{ __('Web Pages') }}
{{ __('More') }}
</span>
</div>
<Button v-if="isModerator" variant="ghost" @click="openPageModal()">
Expand Down
46 changes: 45 additions & 1 deletion frontend/src/components/CourseCardOverlay.vue
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,15 @@
{{ __('Start Learning') }}
</span>
</Button>
<Button
v-if="canGetCertificate"
@click="fetchCertificate()"
variant="subtle"
class="w-full mt-2"
size="md"
>
{{ __('Get Certificate') }}
</Button>
<router-link
v-if="user?.data?.is_moderator || is_instructor()"
:to="{
Expand Down Expand Up @@ -136,7 +145,7 @@ function enrollStudent() {
})
setTimeout(() => {
window.location.href = `/login?redirect-to=${window.location.pathname}`
}, 3000)
}, 2000)
} else {
const enrollStudentResource = createResource({
url: 'lms.lms.doctype.lms_enrollment.lms_enrollment.create_membership',
Expand Down Expand Up @@ -174,4 +183,39 @@ const is_instructor = () => {
})
return user_is_instructor
}
const canGetCertificate = computed(() => {
if (
props.course.data?.enable_certification &&
props.course.data?.membership?.progress == 100
) {
return true
}
return false
})
const certificate = createResource({
url: 'lms.lms.doctype.lms_certificate.lms_certificate.create_certificate',
makeParams(values) {
return {
course: values.course,
}
},
onSuccess(data) {
console.log(data)
window.open(
`/api/method/frappe.utils.print_format.download_pdf?doctype=LMS+Certificate&name=${
data.name
}&format=${encodeURIComponent(data.template)}`,
'_blank'
)
},
})
const fetchCertificate = () => {
certificate.submit({
course: props.course.data?.name,
member: user.data?.name,
})
}
</script>
8 changes: 6 additions & 2 deletions frontend/src/components/VideoBlock.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
<template>
<div ref="videoContainer" class="video-block group relative">
<video @timeupdate="updateTime" @ended="videoEnded" class="rounded-lg">
<video
@timeupdate="updateTime"
@ended="videoEnded"
class="rounded-lg border border-gray-100"
>
<source :src="fileURL" :type="type" />
</video>
<div
class="flex items-center space-x-2 bg-gray-200 rounded-lg p-0.5 absolute bottom-3 w-[98%] left-0 right-0 mx-auto"
class="flex items-center space-x-2 bg-gray-200 rounded-md p-0.5 absolute bottom-3 w-[98%] left-0 right-0 mx-auto"
>
<Button variant="ghost">
<template #icon>
Expand Down
11 changes: 10 additions & 1 deletion frontend/src/pages/CreateCourse.vue
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@
<div class="text-lg font-semibold mt-5 mb-4">
{{ __('Settings') }}
</div>
<div class="grid grid-cols-2 gap-10 mb-4">
<div class="grid grid-cols-3 gap-10 mb-4">
<div
v-if="user.data?.is_moderator"
class="flex flex-col space-y-3"
Expand Down Expand Up @@ -147,11 +147,18 @@
v-model="course.featured"
:label="__('Featured')"
/>
</div>
<div class="flex flex-col space-y-3">
<FormControl
type="checkbox"
v-model="course.disable_self_learning"
:label="__('Disable Self Enrollment')"
/>
<FormControl
type="checkbox"
v-model="course.enable_certification"
:label="__('Completion Certificate')"
/>
</div>
</div>
</div>
Expand Down Expand Up @@ -244,6 +251,7 @@ const course = reactive({
featured: false,
upcoming: false,
disable_self_learning: false,
enable_certification: false,
paid_course: false,
course_price: '',
currency: '',
Expand Down Expand Up @@ -337,6 +345,7 @@ const courseResource = createResource({
'disable_self_learning',
'paid_course',
'featured',
'enable_certification',
]
for (let idx in checkboxes) {
let key = checkboxes[idx]
Expand Down
52 changes: 46 additions & 6 deletions frontend/src/pages/Lesson.vue
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,17 @@
</span>
</Button>
</router-link>
<router-link
v-else
:to="{
name: 'CourseDetail',
params: { courseName: courseName },
}"
>
<Button>
{{ __('Back to Course') }}
</Button>
</router-link>
</div>
</div>

Expand Down Expand Up @@ -160,12 +171,12 @@
{{ lesson.data.course_title }}
</div>
<div v-if="user && lesson.data.membership" class="text-sm mt-3">
{{ Math.ceil(lesson.data.membership.progress) }}% completed
{{ Math.ceil(lessonProgress) }}% {{ __('completed') }}
</div>

<ProgressBar
v-if="user && lesson.data.membership"
:progress="lesson.data.membership.progress"
:progress="lessonProgress"
/>
</div>
<CourseOutline
Expand All @@ -179,7 +190,7 @@
</template>
<script setup>
import { createResource, Breadcrumbs, Button } from 'frappe-ui'
import { computed, watch, inject, ref } from 'vue'
import { computed, watch, inject, ref, onMounted, onBeforeUnmount } from 'vue'
import CourseOutline from '@/components/CourseOutline.vue'
import UserAvatar from '@/components/UserAvatar.vue'
import { useRoute } from 'vue-router'
Expand All @@ -196,6 +207,9 @@ const route = useRoute()
const allowDiscussions = ref(false)
const editor = ref(null)
const instructorEditor = ref(null)
const lessonProgress = ref(0)
const timer = ref(0)
let timerInterval
const props = defineProps({
courseName: {
Expand All @@ -212,6 +226,10 @@ const props = defineProps({
},
})
onMounted(() => {
startTimer()
})
const lesson = createResource({
url: 'lms.lms.utils.get_lesson',
cache: ['lesson', props.courseName, props.chapterNumber, props.lessonNumber],
Expand All @@ -224,7 +242,7 @@ const lesson = createResource({
},
auto: true,
onSuccess(data) {
markProgress(data)
lessonProgress.value = data.membership?.progress
if (data.content) editor.value = renderEditor('editor', data.content)
if (data.instructor_content?.blocks?.length)
instructorEditor.value = renderEditor(
Expand Down Expand Up @@ -256,8 +274,10 @@ const renderEditor = (holder, content) => {
})
}
const markProgress = (data) => {
if (user.data && !data.progress) progress.submit()
const markProgress = () => {
if (user.data && !lesson.data?.progress) {
progress.submit()
}
}
const progress = createResource({
Expand All @@ -268,6 +288,9 @@ const progress = createResource({
course: props.courseName,
}
},
onSuccess(data) {
lessonProgress.value = data
},
})
const breadcrumbs = computed(() => {
Expand Down Expand Up @@ -304,10 +327,27 @@ watch(
chapter: newChapterNumber,
lesson: newLessonNumber,
})
clearInterval(timerInterval)
timer.value = 0
startTimer()
}
}
)
const startTimer = () => {
timerInterval = setInterval(() => {
timer.value++
if (timer.value == 30) {
clearInterval(timerInterval)
markProgress()
}
}, 1000)
}
onBeforeUnmount(() => {
clearInterval(timerInterval)
})
const checkIfDiscussionsAllowed = () => {
let quizPresent = false
JSON.parse(lesson.data?.content)?.blocks?.forEach((block) => {
Expand Down
47 changes: 1 addition & 46 deletions lms/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,52 +176,7 @@
]

jinja = {
"methods": [
"lms.page_renderers.get_profile_url",
"lms.overrides.user.get_enrolled_courses",
"lms.overrides.user.get_course_membership",
"lms.overrides.user.get_authored_courses",
"lms.overrides.user.get_palette",
"lms.lms.utils.get_membership",
"lms.lms.utils.get_lessons",
"lms.lms.utils.get_tags",
"lms.lms.utils.get_instructors",
"lms.lms.utils.get_students",
"lms.lms.utils.get_average_rating",
"lms.lms.utils.is_certified",
"lms.lms.utils.get_lesson_index",
"lms.lms.utils.get_lesson_url",
"lms.lms.utils.get_chapters",
"lms.lms.utils.get_slugified_chapter_title",
"lms.lms.utils.get_progress",
"lms.lms.utils.render_html",
"lms.lms.utils.is_mentor",
"lms.lms.utils.is_cohort_staff",
"lms.lms.utils.get_mentors",
"lms.lms.utils.get_reviews",
"lms.lms.utils.is_eligible_to_review",
"lms.lms.utils.get_initial_members",
"lms.lms.utils.get_sorted_reviews",
"lms.lms.utils.is_instructor",
"lms.lms.utils.convert_number_to_character",
"lms.lms.utils.get_signup_optin_checks",
"lms.lms.utils.get_popular_courses",
"lms.lms.utils.format_amount",
"lms.lms.utils.first_lesson_exists",
"lms.lms.utils.get_courses_under_review",
"lms.lms.utils.has_course_instructor_role",
"lms.lms.utils.has_course_moderator_role",
"lms.lms.utils.get_certificates",
"lms.lms.utils.format_number",
"lms.lms.utils.get_lesson_count",
"lms.lms.utils.get_all_memberships",
"lms.lms.utils.get_filtered_membership",
"lms.lms.utils.show_start_learing_cta",
"lms.lms.utils.can_create_courses",
"lms.lms.utils.get_telemetry_boot_info",
"lms.lms.utils.is_onboarding_complete",
"lms.www.utils.is_student",
],
"methods": ["lms.lms.utils.get_signup_optin_checks"],
"filters": [],
}
## Specify the additional tabs to be included in the user profile page.
Expand Down
9 changes: 6 additions & 3 deletions lms/lms/doctype/course_lesson/course_lesson.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def save_progress(lesson, course):
"LMS Enrollment", {"course": course, "member": frappe.session.user}
)
if not membership:
return 0
return

frappe.db.set_value("LMS Enrollment", membership, "current_lesson", lesson)

Expand All @@ -104,7 +104,7 @@ def save_progress(lesson, course):
if frappe.db.exists(
"LMS Course Progress", {"lesson": lesson, "member": frappe.session.user}
):
return 0
return

frappe.get_doc(
{
Expand All @@ -116,9 +116,12 @@ def save_progress(lesson, course):
).save(ignore_permissions=True)

progress = get_course_progress(course)
frappe.db.set_value("LMS Enrollment", membership, "progress", progress)
# Had to get doc, as on_change doesn't trigger when you use set_value. The trigger is necesary for badge to get assigned.
enrollment = frappe.get_doc("LMS Enrollment", membership)
enrollment.progress = progress
enrollment.save()
enrollment.run_method("on_change")

return progress


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@
"label": "Comments"
},
{
"fetch_from": "course.evaluator",
"fieldname": "evaluator",
"fieldtype": "Link",
"label": "Evaluator",
Expand Down
Loading

0 comments on commit 15330cb

Please sign in to comment.