Skip to content

Commit

Permalink
JI-1139 Add xAPI extension for statement when there exists alternatives
Browse files Browse the repository at this point in the history
This is needed to reduce the solution space that grows exponentially
when there are alternatives in the crp, since crp requires
and exhaustive list of all possible solution combinations.
@see https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Data.md#correct-responses-pattern
  • Loading branch information
thomasmars committed May 14, 2019
1 parent 8d7c7c0 commit f28e78d
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 25 deletions.
2 changes: 2 additions & 0 deletions .h5pignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
crowdin.yml
.gitignore
70 changes: 45 additions & 25 deletions js/blanks.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ H5P.Blanks = (function ($, Question) {
var STATE_SHOWING_SOLUTION = 'showing-solution';
var STATE_FINISHED = 'finished';

const XAPI_ALTERNATIVE_EXTENSION = 'https://h5p.org/x-api/alternatives';
const XAPI_CASE_SENSITIVITY = 'https://h5p.org/x-api/case-sensitivity';
const XAPI_REPORTING_VERSION_EXTENSION = 'https://h5p.org/x-api/h5p-reporting-version';

/**
* @typedef {Object} Params
* Parameters/configuration object for Blanks
Expand Down Expand Up @@ -624,39 +628,48 @@ H5P.Blanks = (function ($, Question) {
};
definition.type = 'http://adlnet.gov/expapi/activities/cmi.interaction';
definition.interactionType = 'fill-in';
definition.correctResponsesPattern = ['{case_matters=' + this.params.behaviour.caseSensitive + '}'];
var firstCorrectResponse = true;
const crpPrefix = '{case_matters=' + this.params.behaviour.caseSensitive + '}';
const clozeSolutions = [];
// xAPI forces us to create solution patterns for all possible solution combinations
for (var i = 0; i < this.params.questions.length; i++) {
var question = this.handleBlanks(this.params.questions[i], function (solution) {
// Store new patterns for each extra alternative answer
var newPatterns = [];
for (var j = 0; j < definition.correctResponsesPattern.length; j++) {
if (!firstCorrectResponse) {
definition.correctResponsesPattern[j] += '[,]';
}
var prefix = definition.correctResponsesPattern[j];
for (var k = 0; k < solution.solutions.length; k++) {
if (k === 0) {
// This is the first possible answr, just add it to the pattern
definition.correctResponsesPattern[j] += solution.solutions[k];
}
else {
// This is an alternative possible answer, we need to create a new permutation
newPatterns.push(prefix + solution.solutions[k]);
}
}
}
// Add any new permutations to the list of response patterns
definition.correctResponsesPattern = definition.correctResponsesPattern.concat(newPatterns);

firstCorrectResponse = false;
// Collect solutions
clozeSolutions.push(solution.solutions);

// We replace the solutions in the question with a "blank"
return '__________';
});
definition.description['en-US'] += question;
}

const hasAlternatives = clozeSolutions.some(function (cloze) {
return cloze.length > 1;
});

/**
* Note:
* If alternatives are used we don't provide a correct responses pattern
* since the solution space grows exponentially. We instead provide an
* extension for allowing reports to be generated.
* This is the recommended approach ref:
* @see(https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Data.md#correct-responses-pattern)
*/
if (hasAlternatives) {
// Use extension to avoid exponentially growing solution space
definition.extensions = definition.extensions || {};
definition.extensions[XAPI_CASE_SENSITIVITY] = this.params.behaviour.caseSensitive;
definition.extensions[XAPI_ALTERNATIVE_EXTENSION] = clozeSolutions;
}
else {
// Use correct responses pattern
const singleAnswer = clozeSolutions.join('[,]');

definition.correctResponsesPattern = [
crpPrefix + singleAnswer,
];
}


return definition;
};

Expand All @@ -665,7 +678,14 @@ H5P.Blanks = (function ($, Question) {
*/
Blanks.prototype.addQuestionToXAPI = function (xAPIEvent) {
var definition = xAPIEvent.getVerifiedStatementValue(['object', 'definition']);
$.extend(definition, this.getxAPIDefinition());
$.extend(true, definition, this.getxAPIDefinition());

// Set reporting module version if alternative extension is used
if (definition.extensions && definition.extensions[XAPI_ALTERNATIVE_EXTENSION]) {
const context = xAPIEvent.getVerifiedStatementValue(['context']);
context.extensions = context.extensions || {};
context.extensions[XAPI_REPORTING_VERSION_EXTENSION] = '1.1.0';
}
};

/**
Expand Down

0 comments on commit f28e78d

Please sign in to comment.