From 77bd638fb1b2a7bab6c261e4fa433ec2adf39dba Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Thu, 5 Oct 2023 17:00:04 +0200 Subject: [PATCH] MOBILE-4362 scorm: Fix grade calculation when using LASTATTEMPT The app always used the last attempt, but LMS uses the last attempt that has at least 1 SCO with completed or passed status --- .../mod/scorm/components/index/index.ts | 11 ++--- src/addons/mod/scorm/services/scorm.ts | 47 ++++++++++++------- 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/addons/mod/scorm/components/index/index.ts b/src/addons/mod/scorm/components/index/index.ts index 8fca36f6c..8b7759bf4 100644 --- a/src/addons/mod/scorm/components/index/index.ts +++ b/src/addons/mod/scorm/components/index/index.ts @@ -301,12 +301,7 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom return; } - const grade = await AddonModScorm.getAttemptGrade(this.scorm, attempt, offline); - - attempts[attempt] = { - num: attempt, - grade: grade, - }; + attempts[attempt] = await AddonModScorm.getAttemptGrade(this.scorm, attempt, offline); } /** @@ -344,10 +339,10 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom // Now format the grades. this.onlineAttempts.forEach((attempt) => { - attempt.gradeFormatted = AddonModScorm.formatGrade(scorm, attempt.grade); + attempt.gradeFormatted = AddonModScorm.formatGrade(scorm, attempt.score); }); this.offlineAttempts.forEach((attempt) => { - attempt.gradeFormatted = AddonModScorm.formatGrade(scorm, attempt.grade); + attempt.gradeFormatted = AddonModScorm.formatGrade(scorm, attempt.score); }); this.gradeFormatted = AddonModScorm.formatGrade(scorm, this.grade); diff --git a/src/addons/mod/scorm/services/scorm.ts b/src/addons/mod/scorm/services/scorm.ts index 6665c0f6c..0754ec8a3 100644 --- a/src/addons/mod/scorm/services/scorm.ts +++ b/src/addons/mod/scorm/services/scorm.ts @@ -119,17 +119,22 @@ export class AddonModScormProvider { switch (scorm.whatgrade) { case AddonModScormProvider.FIRSTATTEMPT: - return onlineAttempts[1] ? onlineAttempts[1].grade : -1; + return onlineAttempts[1] ? onlineAttempts[1].score : -1; case AddonModScormProvider.LASTATTEMPT: { - // Search the last attempt number. - let max = 0; - Object.keys(onlineAttempts).forEach((attemptNumber) => { - max = Math.max(Number(attemptNumber), max); - }); + // Search the last completed attempt number. + let lastCompleted = 0; + for (const attemptNumber in onlineAttempts) { + if (onlineAttempts[attemptNumber].hasCompletedPassedSCO) { + lastCompleted = Math.max(onlineAttempts[attemptNumber].num, lastCompleted); + } + } - if (max > 0) { - return onlineAttempts[max].grade; + if (lastCompleted > 0) { + return onlineAttempts[lastCompleted].score; + } else if (onlineAttempts[1]) { + // If no completed attempt found, use the first attempt for consistency with LMS. + return onlineAttempts[1].score; } return -1; @@ -139,7 +144,7 @@ export class AddonModScormProvider { // Search the highest grade. let grade = 0; for (const attemptNumber in onlineAttempts) { - grade = Math.max(onlineAttempts[attemptNumber].grade, grade); + grade = Math.max(onlineAttempts[attemptNumber].score, grade); } return grade; @@ -151,7 +156,7 @@ export class AddonModScormProvider { let total = 0; for (const attemptNumber in onlineAttempts) { - sumGrades += onlineAttempts[attemptNumber].grade; + sumGrades += onlineAttempts[attemptNumber].score; total++; } @@ -589,8 +594,8 @@ export class AddonModScormProvider { } /** - * Get the grade for a certain SCORM and attempt. - * Based on Moodle's scorm_grade_user_attempt. + * Get the grade data for a certain attempt. + * Mostly based on Moodle's scorm_grade_user_attempt. * * @param scorm SCORM. * @param attempt Attempt number. @@ -598,7 +603,12 @@ export class AddonModScormProvider { * @param siteId Site ID. If not defined, current site. * @returns Promise resolved with the grade. If the attempt hasn't reported grade/completion, it will be -1. */ - async getAttemptGrade(scorm: AddonModScormScorm, attempt: number, offline?: boolean, siteId?: string): Promise { + async getAttemptGrade( + scorm: AddonModScormScorm, + attempt: number, + offline?: boolean, + siteId?: string, + ): Promise { const attemptScore = { scos: 0, values: 0, @@ -654,7 +664,11 @@ export class AddonModScormProvider { score = attemptScore.max; // Remote Learner GRADEHIGHEST is default. } - return score; + return { + num: attempt, + score, + hasCompletedPassedSCO: attemptScore.scos > 0, + }; } /** @@ -2052,11 +2066,12 @@ export type AddonModScormOrganization = { }; /** - * Grade for an attempt. + * Grade data for an attempt. */ export type AddonModScormAttemptGrade = { num: number; - grade: number; + score: number; + hasCompletedPassedSCO: boolean; // Whether it has at least 1 SCO with status completed or passed. }; /**