From f28e78d4736d8b7da306e19da0650521555cc92e Mon Sep 17 00:00:00 2001 From: Thomas Marstrander Date: Tue, 14 May 2019 13:40:00 +0200 Subject: [PATCH] JI-1139 Add xAPI extension for statement when there exists alternatives 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 --- .h5pignore | 2 ++ js/blanks.js | 70 +++++++++++++++++++++++++++++++++------------------- 2 files changed, 47 insertions(+), 25 deletions(-) create mode 100644 .h5pignore diff --git a/.h5pignore b/.h5pignore new file mode 100644 index 0000000..909043a --- /dev/null +++ b/.h5pignore @@ -0,0 +1,2 @@ +crowdin.yml +.gitignore \ No newline at end of file diff --git a/js/blanks.js b/js/blanks.js index ae731d6..3cea7d3 100644 --- a/js/blanks.js +++ b/js/blanks.js @@ -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 @@ -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; }; @@ -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'; + } }; /**