Skip to content

Commit

Permalink
New: expanded use of cmi.interactions data elements to include more…
Browse files Browse the repository at this point in the history
… context for reporting purposes (#321)
  • Loading branch information
danielghost authored Jan 15, 2025
1 parent aeed76e commit 0ed5434
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 18 deletions.
25 changes: 15 additions & 10 deletions js/adapt-stateful-session.js
Original file line number Diff line number Diff line change
Expand Up @@ -216,22 +216,27 @@ export default class StatefulSession extends Backbone.Controller {
offlineStorage.set('objectiveStatus', id, completionStatus);
}

onQuestionRecordInteraction(questionView) {
onQuestionRecordInteraction(view) {
if (!this.shouldRecordInteractions) return;
if (!this.scorm.isSupported('cmi.interactions._count')) return;
// View functions are deprecated: getResponseType, getResponse, isCorrect, getLatency
const questionModel = questionView.model;
const responseType = (questionModel.getResponseType ? questionModel.getResponseType() : questionView.getResponseType());
const model = view.model;
const responseType = model.getResponseType();
// If responseType doesn't contain any data, assume that the question
// component hasn't been set up for cmi.interaction tracking
if (_.isEmpty(responseType)) return;
const modelId = model.get('_id');
const id = this._uniqueInteractionIds
? `${this.scorm.getInteractionCount()}-${questionModel.get('_id')}`
: questionModel.get('_id');
const response = (questionModel.getResponse ? questionModel.getResponse() : questionView.getResponse());
const result = (questionModel.isCorrect ? questionModel.isCorrect() : questionView.isCorrect());
const latency = (questionModel.getLatency ? questionModel.getLatency() : questionView.getLatency());
offlineStorage.set('interaction', id, response, result, latency, responseType);
? `${this.scorm.getInteractionCount()}-${modelId}`
: modelId;
const response = model.getResponse();
const result = model.isCorrect();
const latency = model?.getLatency?.() ?? view.getLatency();
const correctResponsesPattern = model.getInteractionObject()?.correctResponsesPattern;
const objectiveIds = Adapt?.scoring?.getSubsetsByModelId(modelId)
.filter(set => set.type !== 'adapt')
.map(({ id }) => id);
const description = model.get('body');
offlineStorage.set('interaction', id, response, result, latency, responseType, correctResponsesPattern, objectiveIds, description);
}

onContentObjectCompleteChange(model) {
Expand Down
33 changes: 25 additions & 8 deletions js/scorm/wrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ class ScormWrapper {
}));
}

recordInteraction(id, response, correct, latency, type) {
recordInteraction(id, response, correct, latency, type, correctResponsesPattern, objectiveIds, description) {
if (!this.isChildSupported('cmi.interactions.n.id') || !this.isSupported('cmi.interactions._count')) return;
switch (type) {
case 'choice':
Expand Down Expand Up @@ -634,48 +634,65 @@ class ScormWrapper {
this.setValueIfChildSupported(`${cmiPrefix}.time`, this.getCMITime());
}

recordInteractionScorm2004(id, response, correct, latency, type) {
recordInteractionScorm2004(id, response, correct, latency, type, correctResponsesPattern, objectiveIds, description) {
id = id.trim();
const cmiPrefix = `cmi.interactions.${this.getInteractionCount()}`;
this.setValue(`${cmiPrefix}.id`, id);
this.setValue(`${cmiPrefix}.type`, type);
this.setValue(`${cmiPrefix}.learner_response`, response);
this.setValue(`${cmiPrefix}.result`, correct ? 'correct' : 'incorrect');
if (latency !== null && latency !== undefined) this.setValue(`${cmiPrefix}.latency`, this.convertToSCORM2004Time(latency));
if (correctResponsesPattern?.length) {
correctResponsesPattern.forEach((response, index) => {
this.setValue(`${cmiPrefix}.correct_responses.${index}.pattern`, response);
});
}
if (objectiveIds?.length) {
objectiveIds.forEach((id, index) => {
this.setValue(`${cmiPrefix}.objectives.${index}.id`, id);
});
}
if (description) {
const maxLength = this.maxCharLimitOverride ?? 250;
// strip HTML
description = $(`<p>${description}</p>`).text();
if (description.length > maxLength) description = description.substr(0, maxLength).trim();
this.setValue(`${cmiPrefix}.description`, description);
}
this.setValue(`${cmiPrefix}.timestamp`, this.getISO8601Timestamp());
}

recordInteractionMultipleChoice(id, response, correct, latency, type) {
recordInteractionMultipleChoice(id, response, correct, latency, type, correctResponsesPattern, objectiveIds, description) {
if (this.isSCORM2004()) {
response = response.replace(/,|#/g, '[,]');
} else {
response = response.replace(/#/g, ',');
response = this.checkResponse(response, 'choice');
}
const scormRecordInteraction = this.isSCORM2004() ? this.recordInteractionScorm2004 : this.recordInteractionScorm12;
scormRecordInteraction.call(this, id, response, correct, latency, type);
scormRecordInteraction.call(this, id, response, correct, latency, type, correctResponsesPattern, objectiveIds, description);
}

recordInteractionMatching(id, response, correct, latency, type) {
recordInteractionMatching(id, response, correct, latency, type, correctResponsesPattern, objectiveIds, description) {
response = response.replace(/#/g, ',');
if (this.isSCORM2004()) {
response = response.replace(/,/g, '[,]').replace(/\./g, '[.]');
} else {
response = this.checkResponse(response, 'matching');
}
const scormRecordInteraction = this.isSCORM2004() ? this.recordInteractionScorm2004 : this.recordInteractionScorm12;
scormRecordInteraction.call(this, id, response, correct, latency, type);
scormRecordInteraction.call(this, id, response, correct, latency, type, correctResponsesPattern, objectiveIds, description);
}

recordInteractionFillIn(id, response, correct, latency, type) {
recordInteractionFillIn(id, response, correct, latency, type, correctResponsesPattern, objectiveIds, description) {
let maxLength = this.isSCORM2004() ? 250 : 255;
maxLength = this.maxCharLimitOverride ?? maxLength;
if (response.length > maxLength) {
response = response.substr(0, maxLength);
this.logger.warn(`ScormWrapper::recordInteractionFillIn: response data for ${id} is longer than the maximum allowed length of ${maxLength} characters; data will be truncated to avoid an error.`);
}
const scormRecordInteraction = this.isSCORM2004() ? this.recordInteractionScorm2004 : this.recordInteractionScorm12;
scormRecordInteraction.call(this, id, response, correct, latency, type);
scormRecordInteraction.call(this, id, response, correct, latency, type, correctResponsesPattern, objectiveIds, description);
}

getObjectiveCount() {
Expand Down

0 comments on commit 0ed5434

Please sign in to comment.