Skip to content

Commit

Permalink
Adding ability to send common data points to LMS for easier processing
Browse files Browse the repository at this point in the history
  • Loading branch information
jcputney committed Oct 23, 2024
1 parent 095518f commit 022d286
Show file tree
Hide file tree
Showing 10 changed files with 405 additions and 37 deletions.
55 changes: 32 additions & 23 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "scorm-again",
"version": "2.1.0",
"version": "2.2.0",
"description": "A modern SCORM JavaScript run-time library for AICC, SCORM 1.2, and SCORM 2004",
"main": "dist/scorm-again.min.js",
"type": "module",
Expand Down
20 changes: 16 additions & 4 deletions src/BaseAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import APIConstants from "./constants/api_constants";
import { formatMessage, stringMatches, unflatten } from "./utilities";
import { BaseCMI } from "./cmi/common/base_cmi";
import { debounce } from "./utilities/debounce";
import { RefObject, ResultObject, Settings } from "./types/api_types";
import {
RefObject,
CommitObject,
ResultObject,
Settings,
} from "./types/api_types";
import { DefaultSettings } from "./constants/default_settings";
import { IBaseAPI } from "./interfaces/IBaseAPI";
import { ScheduledCommit } from "./helpers/scheduled_commit";
Expand Down Expand Up @@ -165,6 +170,13 @@ export default abstract class BaseAPI implements IBaseAPI {
*/
abstract renderCommitCMI(_terminateCommit: boolean): RefObject | Array<any>;

/**
* Render the commit object to the shortened format for LMS commit
* @param {boolean} _terminateCommit
* @return {CommitObject}
*/
abstract renderCommitObject(_terminateCommit: boolean): CommitObject;

/**
* Logging for all SCORM actions
*
Expand Down Expand Up @@ -1114,13 +1126,13 @@ export default abstract class BaseAPI implements IBaseAPI {
/**
* Send the request to the LMS
* @param {string} url
* @param {RefObject|Array} params
* @param {CommitObject|RefObject|Array} params
* @param {boolean} immediate
* @return {ResultObject}
*/
async processHttpRequest(
url: string,
params: RefObject | Array<any>,
params: CommitObject | RefObject | Array<any>,
immediate: boolean = false,
): Promise<ResultObject> {
const api = this;
Expand All @@ -1143,7 +1155,7 @@ export default abstract class BaseAPI implements IBaseAPI {

const process = async (
url: string,
params: RefObject | Array<any>,
params: CommitObject | RefObject | Array<any>,
settings: Settings,
): Promise<ResultObject> => {
try {
Expand Down
76 changes: 71 additions & 5 deletions src/Scorm12API.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import BaseAPI from "./BaseAPI";
import { CMI } from "./cmi/scorm12/cmi";
import * as Utilities from "./utilities";
import { stringMatches } from "./utilities";
import APIConstants from "./constants/api_constants";
import ErrorCodes from "./constants/error_codes";

Expand All @@ -12,8 +13,16 @@ import {
CMIInteractionsObjectivesObject,
} from "./cmi/scorm12/interactions";
import { NAV } from "./cmi/scorm12/nav";
import { RefObject, ResultObject, Settings } from "./types/api_types";
import { stringMatches } from "./utilities";
import {
CommitObject,
RefObject,
ResultObject,
ScoreObject,
Settings,
} from "./types/api_types";
import * as console from "node:console";
import Regex from "./constants/regex";
import { CompletionStatus, SuccessStatus } from "./constants/enums";

/**
* API class for SCORM 1.2
Expand Down Expand Up @@ -320,6 +329,61 @@ export default class Scorm12API extends BaseAPI {
}
}

/**
* Render the cmi object to the proper format for LMS commit
* @param {boolean} terminateCommit
* @return {CommitObject}
*/
renderCommitObject(terminateCommit: boolean): CommitObject {
const cmiExport = this.renderCommitCMI(terminateCommit);
const totalTimeHHMMSS = this.cmi.getCurrentTotalTime();
const totalTimeSeconds = Utilities.getTimeAsSeconds(
totalTimeHHMMSS,
Regex.scorm12.CMITimespan,
);
const lessonStatus = this.cmi.core.lesson_status;
let completionStatus = CompletionStatus.unknown;
let successStatus = SuccessStatus.unknown;
if (lessonStatus) {
completionStatus =
lessonStatus === "completed" || lessonStatus === "passed"
? CompletionStatus.completed
: CompletionStatus.incomplete;
if (lessonStatus === "passed") {
successStatus = SuccessStatus.passed;
} else if (lessonStatus === "failed") {
successStatus = SuccessStatus.failed;
}
}

const score = this.cmi.core.score;
let scoreObject: ScoreObject = null;
if (score) {
scoreObject = {};

if (!Number.isNaN(Number.parseFloat(score.raw))) {
scoreObject.raw = Number.parseFloat(score.raw);
}
if (!Number.isNaN(Number.parseFloat(score.min))) {
scoreObject.min = Number.parseFloat(score.min);
}
if (!Number.isNaN(Number.parseFloat(score.max))) {
scoreObject.max = Number.parseFloat(score.max);
}
}

const commitObject: CommitObject = {
successStatus: successStatus,
completionStatus: completionStatus,
runtimeData: cmiExport,
totalTimeSeconds: totalTimeSeconds,
};
if (scoreObject) {
commitObject.score = scoreObject;
}
return commitObject;
}

/**
* Attempts to store the data to the LMS
*
Expand Down Expand Up @@ -361,9 +425,11 @@ export default class Scorm12API extends BaseAPI {
}
}

const commitObject = this.renderCommitCMI(
terminateCommit || this.settings.alwaysSendTotalTime,
);
const shouldTerminateCommit =
terminateCommit || this.settings.alwaysSendTotalTime;
const commitObject = this.settings.renderCommonCommitFields
? this.renderCommitObject(shouldTerminateCommit)
: this.renderCommitCMI(shouldTerminateCommit);

if (this.apiLogLevel === APIConstants.global.LOG_LEVEL_DEBUG) {
console.debug(
Expand Down
78 changes: 74 additions & 4 deletions src/Scorm2004API.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,14 @@ import {
import { CMICommentsObject } from "./cmi/scorm2004/comments";
import { CMIObjectivesObject } from "./cmi/scorm2004/objectives";
import { ADL } from "./cmi/scorm2004/adl";
import { RefObject, ResultObject, Settings } from "./types/api_types";
import {
CommitObject,
RefObject,
ResultObject,
ScoreObject,
Settings,
} from "./types/api_types";
import { CompletionStatus, SuccessStatus } from "./constants/enums";

/**
* API class for SCORM 2004
Expand Down Expand Up @@ -613,6 +620,67 @@ export default class Scorm2004API extends BaseAPI {
}
}

/**
* Render the cmi object to the proper format for LMS commit
* @param {boolean} terminateCommit
* @return {CommitObject}
*/
renderCommitObject(terminateCommit: boolean): CommitObject {
const cmiExport = this.renderCommitCMI(terminateCommit);
const totalTimeDuration = this.cmi.getCurrentTotalTime();
const totalTimeSeconds = Utilities.getDurationAsSeconds(
totalTimeDuration,
Regex.scorm2004.CMITimespan,
);

let completionStatus = CompletionStatus.unknown;
let successStatus = SuccessStatus.unknown;
if (this.cmi.completion_status) {
if (this.cmi.completion_status === "completed") {
completionStatus = CompletionStatus.completed;
} else if (this.cmi.completion_status === "incomplete") {
completionStatus = CompletionStatus.incomplete;
}
}
if (this.cmi.success_status) {
if (this.cmi.success_status === "passed") {
successStatus = SuccessStatus.passed;
} else if (this.cmi.success_status === "failed") {
successStatus = SuccessStatus.failed;
}
}

const score = this.cmi.score;
let scoreObject: ScoreObject = null;
if (score) {
scoreObject = {};

if (!Number.isNaN(Number.parseFloat(score.raw))) {
scoreObject.raw = Number.parseFloat(score.raw);
}
if (!Number.isNaN(Number.parseFloat(score.min))) {
scoreObject.min = Number.parseFloat(score.min);
}
if (!Number.isNaN(Number.parseFloat(score.max))) {
scoreObject.max = Number.parseFloat(score.max);
}
if (!Number.isNaN(Number.parseFloat(score.scaled))) {
scoreObject.scaled = Number.parseFloat(score.scaled);
}
}

const commitObject: CommitObject = {
completionStatus: completionStatus,
successStatus: successStatus,
totalTimeSeconds: totalTimeSeconds,
runtimeData: cmiExport,
};
if (scoreObject) {
commitObject.score = scoreObject;
}
return commitObject;
}

/**
* Attempts to store the data to the LMS
*
Expand Down Expand Up @@ -650,9 +718,11 @@ export default class Scorm2004API extends BaseAPI {
navRequest = true;
}

const commitObject = this.renderCommitCMI(
terminateCommit || this.settings.alwaysSendTotalTime,
);
const shouldTerminateCommit =
terminateCommit || this.settings.alwaysSendTotalTime;
const commitObject = this.settings.renderCommonCommitFields
? this.renderCommitObject(shouldTerminateCommit)
: this.renderCommitCMI(shouldTerminateCommit);

if (this.apiLogLevel === APIConstants.global.LOG_LEVEL_DEBUG) {
console.debug(
Expand Down
1 change: 1 addition & 0 deletions src/constants/default_settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const DefaultSettings: Settings = {
logLevel: APIConstants.global.LOG_LEVEL_ERROR,
selfReportSessionTime: false,
alwaysSendTotalTime: false,
renderCommonCommitFields: false,
strict_errors: true,
xhrHeaders: {},
xhrWithCredentials: false,
Expand Down
12 changes: 12 additions & 0 deletions src/constants/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,15 @@ export enum NAVBoolean {
true = "true",
false = "false",
}

export enum SuccessStatus {
passed = "passed",
failed = "failed",
unknown = "unknown",
}

export enum CompletionStatus {
completed = "completed",
incomplete = "incomplete",
unknown = "unknown",
}
18 changes: 18 additions & 0 deletions src/types/api_types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { CompletionStatus, SuccessStatus } from "../constants/enums";

export type Settings = {
autocommit?: boolean;
autocommitSeconds?: number;
Expand All @@ -19,6 +21,7 @@ export type Settings = {
scoItemIds?: string[];
scoItemIdValidator?: false | ((scoItemId: string) => boolean);
mastery_override?: boolean;
renderCommonCommitFields?: boolean;
};

export type RefObject = {
Expand All @@ -30,3 +33,18 @@ export type ResultObject = {
errorCode: number;
navRequest?: string;
};

export type ScoreObject = {
raw?: number;
min?: number;
max?: number;
scaled?: number;
};

export type CommitObject = {
successStatus: SuccessStatus;
completionStatus: CompletionStatus;
totalTimeSeconds: number;
runtimeData: RefObject;
score?: ScoreObject;
};
Loading

0 comments on commit 022d286

Please sign in to comment.