From 99397ad1f493c1ea8472714aeea6a41c808cd983 Mon Sep 17 00:00:00 2001
From: raizasafeel <89463672+raizasafeel@users.noreply.github.com>
Date: Tue, 24 Mar 2026 12:58:44 +0530
Subject: [PATCH 1/3] feat(scorm): show completion in frontend
---
frontend/src/components/CourseOutline.vue | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/frontend/src/components/CourseOutline.vue b/frontend/src/components/CourseOutline.vue
index b423ec08..68de0c7f 100644
--- a/frontend/src/components/CourseOutline.vue
+++ b/frontend/src/components/CourseOutline.vue
@@ -75,6 +75,12 @@
/>
+
{
})
}
+const isScormChapterComplete = (chapter) => {
+ return chapter.lessons?.length && chapter.lessons.every((l) => l.is_complete)
+}
+
const isActiveLesson = (lessonNumber) => {
return (
route.params.chapterNumber == lessonNumber.split('-')[0] &&
From 89505eac7db790c8d645242eae761c02796f7ac1 Mon Sep 17 00:00:00 2001
From: raizasafeel <89463672+raizasafeel@users.noreply.github.com>
Date: Tue, 24 Mar 2026 15:23:43 +0530
Subject: [PATCH 2/3] fix(scorm): save_progress no longer impended by race
condition
---
frontend/src/pages/SCORMChapter.vue | 4 +++-
lms/lms/doctype/course_lesson/course_lesson.py | 3 ++-
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/frontend/src/pages/SCORMChapter.vue b/frontend/src/pages/SCORMChapter.vue
index b3dfe0d5..02be01e9 100644
--- a/frontend/src/pages/SCORMChapter.vue
+++ b/frontend/src/pages/SCORMChapter.vue
@@ -109,9 +109,10 @@ const getDataFromLMS = (key) => {
let saveTimeout = null
const debouncedSaveProgress = (scormDetails) => {
+ if (isSuccessfullyCompleted.value) return
clearTimeout(saveTimeout)
saveTimeout = setTimeout(() => {
- saveProgress(scormDetails)
+ if (!isSuccessfullyCompleted.value) saveProgress(scormDetails)
}, 300)
}
@@ -124,6 +125,7 @@ const saveDataToLMS = (key, value) => {
(key === 'cmi.completion_status' && value === 'incomplete')
if (isLessonStatus || isCompletionStatus) {
+ if (isSuccessfullyCompleted.value) return
isSuccessfullyCompleted.value = true
}
diff --git a/lms/lms/doctype/course_lesson/course_lesson.py b/lms/lms/doctype/course_lesson/course_lesson.py
index c47e56c7..5c0ebd6e 100644
--- a/lms/lms/doctype/course_lesson/course_lesson.py
+++ b/lms/lms/doctype/course_lesson/course_lesson.py
@@ -78,7 +78,7 @@ def save_progress(lesson: str, course: str, scorm_details: dict = None):
if not membership:
return 0
- frappe.db.set_value("LMS Enrollment", membership, "current_lesson", lesson)
+ frappe.db.set_value("LMS Enrollment", membership, "current_lesson", lesson, update_modified=False)
progress_already_exists = frappe.db.exists(
"LMS Course Progress", {"lesson": lesson, "member": frappe.session.user}
)
@@ -133,6 +133,7 @@ def save_progress(lesson: str, course: str, scorm_details: dict = None):
# Had to get doc, as on_change doesn't trigger when you use set_value. The trigger is necessary for badge to get assigned.
enrollment = frappe.get_doc("LMS Enrollment", membership)
enrollment.progress = progress
+ enrollment.flags.ignore_version = True
enrollment.save()
enrollment.run_method("on_change")
From 400c950bb79d88bafb6652ae45c0fbb24c844af8 Mon Sep 17 00:00:00 2001
From: raizasafeel <89463672+raizasafeel@users.noreply.github.com>
Date: Tue, 24 Mar 2026 15:33:53 +0530
Subject: [PATCH 3/3] fix: progress updated on video completion
---
frontend/src/pages/Lesson.vue | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/frontend/src/pages/Lesson.vue b/frontend/src/pages/Lesson.vue
index 0f3a9856..0f2d8b8a 100644
--- a/frontend/src/pages/Lesson.vue
+++ b/frontend/src/pages/Lesson.vue
@@ -700,6 +700,31 @@ const updateVideoWatchDuration = () => {
}
})
}
+ attachVideoEndedListeners()
+}
+
+const attachVideoEndedListeners = () => {
+ const onVideoEnded = () => {
+ markProgress()
+ trackVideoWatchDuration()
+ }
+
+ document.querySelectorAll('video').forEach((video) => {
+ if (!video._lmsEndedAttached) {
+ video.addEventListener('ended', onVideoEnded)
+ video._lmsEndedAttached = true
+ }
+ })
+
+ plyrSources.value.forEach((plyrSource) => {
+ if (!plyrSource._lmsEndedAttached) {
+ plyrSource.on('ended', onVideoEnded)
+ plyrSource.on('statechange', (event) => {
+ if (event.detail?.code === 0) onVideoEnded()
+ })
+ plyrSource._lmsEndedAttached = true
+ }
+ })
}
const updatePlyrVideoTime = (video) => {