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) => {