Skip to content

Commit

Permalink
fix: rules for deleting SCORE and MAXSCORE outcomes
Browse files Browse the repository at this point in the history
  • Loading branch information
viktar-dzmitryieu-tao committed Jun 13, 2024
1 parent b2c6e05 commit 5b4b76f
Show file tree
Hide file tree
Showing 3 changed files with 246 additions and 19 deletions.
32 changes: 17 additions & 15 deletions src/qtiItem/helper/maxScore.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export default {
* Set the normal maximum to the item
* @param {Object} item - the standard qti item model object
*/
setNormalMaximum: function setNormalMaximum(item) {
setNormalMaximum(item) {
var normalMaximum,
scoreOutcome = item.getOutcomeDeclaration('SCORE');

Expand Down Expand Up @@ -81,13 +81,15 @@ export default {
* Set the maximum score of the item
* @param {Object} item - the standard qti item model object
*/
setMaxScore: function setMaxScore(item) {
setMaxScore(item) {
var hasInvalidInteraction = false,
scoreOutcome = item.getOutcomeDeclaration('SCORE'),
customOutcomes,
maxScore,
maxScoreOutcome;

const customOutcomes = _(item.getOutcomes()).filter(function (outcome) {
return outcome.id() !== 'SCORE' && outcome.id() !== 'MAXSCORE';
});
//try setting the computed normal maximum only if the processing type is known, i.e. 'templateDriven'
if (scoreOutcome && item.responseProcessing && item.responseProcessing.processingType === 'templateDriven') {
const interactions = item.getInteractions();
Expand All @@ -107,9 +109,6 @@ export default {
},
0
);
customOutcomes = _(item.getOutcomes()).filter(function (outcome) {
return outcome.id() !== 'SCORE' && outcome.id() !== 'MAXSCORE';
});

if (customOutcomes.size()) {
maxScore = customOutcomes.reduce(function (acc, outcome) {
Expand Down Expand Up @@ -150,9 +149,12 @@ export default {
const template = responseHelper.getTemplateNameFromUri(responseDeclaration.template);
return template !== 'NONE';
});
const outcomesWithExternalScored = customOutcomes.filter(outcome => {
return externalScoredValues.includes(outcome.attr('externalScored'));
});
// remove MAXSCORE and SCORE outcome variables when all interactions are configured with none response processing rule,
// and the externalScored property of the SCORE variable is set to None
if (!scoreOutcome.attr('externalScored') && isAllResponseProcessingRulesNone) {
if (!scoreOutcome.attr('externalScored') && isAllResponseProcessingRulesNone && outcomesWithExternalScored.size() === 0) {
item.removeOutcome('MAXSCORE');
item.removeOutcome('SCORE');
}
Expand All @@ -164,7 +166,7 @@ export default {
* @param {Array} choiceCollection
* @returns {Array}
*/
getMatchMaxOrderedChoices: function getMatchMaxOrderedChoices(choiceCollection) {
getMatchMaxOrderedChoices(choiceCollection) {
return _(choiceCollection)
.map(function (choice) {
var matchMax = parseInt(choice.attr('matchMax'), 10);
Expand All @@ -186,7 +188,7 @@ export default {
* @param {Object} interaction - a standard interaction model object
* @returns {Number}
*/
choiceInteractionBased: function choiceInteractionBased(interaction, options) {
choiceInteractionBased(interaction, options) {
var responseDeclaration = interaction.getResponseDeclaration();
var mapDefault = parseFloat(responseDeclaration.mappingAttributes.defaultValue || 0);
var template = responseHelper.getTemplateNameFromUri(responseDeclaration.template);
Expand Down Expand Up @@ -296,7 +298,7 @@ export default {
* @param {Object} interaction - a standard interaction model object
* @returns {Number}
*/
orderInteractionBased: function orderInteractionBased(interaction) {
orderInteractionBased(interaction) {
var minChoice = _ignoreMinChoice ? 0 : parseInt(interaction.attr('minChoices') || 0, 10);
var maxChoice = parseInt(interaction.attr('maxChoices') || 0, 10);
var responseDeclaration = interaction.getResponseDeclaration();
Expand Down Expand Up @@ -340,7 +342,7 @@ export default {
* @param {Object} interaction - a standard interaction model object
* @returns {Number}
*/
associateInteractionBased: function associateInteractionBased(interaction, options) {
associateInteractionBased(interaction, options) {
var responseDeclaration = interaction.getResponseDeclaration();
var template = responseHelper.getTemplateNameFromUri(responseDeclaration.template);
var maxAssoc = parseInt(interaction.attr('maxAssociations') || 0, 10);
Expand Down Expand Up @@ -526,7 +528,7 @@ export default {
* @param {Object} interaction - a standard interaction model object
* @returns {Number}
*/
gapMatchInteractionBased: function gapMatchInteractionBased(interaction) {
gapMatchInteractionBased(interaction) {
var responseDeclaration = interaction.getResponseDeclaration();
var template = responseHelper.getTemplateNameFromUri(responseDeclaration.template);
var maxAssoc = 0;
Expand Down Expand Up @@ -720,7 +722,7 @@ export default {
* @param {Object} interaction - a standard interaction model object
* @returns {Number}
*/
selectPointInteractionBased: function selectPointInteractionBased(interaction) {
selectPointInteractionBased(interaction) {
var maxChoice = parseInt(interaction.attr('maxChoices'), 10);
var minChoice = _ignoreMinChoice ? 0 : parseInt(interaction.attr('minChoices'), 10);
var responseDeclaration = interaction.getResponseDeclaration();
Expand Down Expand Up @@ -771,7 +773,7 @@ export default {
* @param {Object} interaction - a standard interaction model object
* @returns {Number}
*/
sliderInteractionBased: function sliderInteractionBased(interaction) {
sliderInteractionBased(interaction) {
var responseDeclaration = interaction.getResponseDeclaration();
var template = responseHelper.getTemplateNameFromUri(responseDeclaration.template);
var max, scoreMaps;
Expand Down Expand Up @@ -819,7 +821,7 @@ export default {
* @param {Object} interaction - a standard interaction model object
* @returns {Number}
*/
textEntryInteractionBased: function textEntryInteractionBased(interaction) {
textEntryInteractionBased(interaction) {
var responseDeclaration = interaction.getResponseDeclaration();
var template = responseHelper.getTemplateNameFromUri(responseDeclaration.template);
var max, scoreMaps;
Expand Down
207 changes: 207 additions & 0 deletions test/qtiItem/maxScore/data/external-scored-outcome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
{
"identifier": "i666ac8acca467202406131223408054",
"serial": "item_666ad7e04dbfa162481935",
"qtiClass": "assessmentItem",
"attributes": {
"identifier": "i666ac8acca467202406131223408054",
"title": "Item 3",
"label": "Item 3",
"xml:lang": "en-US",
"adaptive": false,
"timeDependent": false,
"toolName": "TAO",
"toolVersion": "2024.06",
"class": ""
},
"body": {
"serial": "container_containeritembody_666ad7e04dbbd649916256",
"body": "\n <div class=\"grid-row\">\n <div class=\"col-12\">\n {{interaction_hottextinteraction_666ad7e0509e0038016277}}\n <\/div>\n <\/div>\n <div class=\"grid-row\">\n <div class=\"col-12\">\n {{interaction_hottextinteraction_666ad7e051230989121581}}\n <\/div>\n <\/div>\n ",
"elements": {
"interaction_hottextinteraction_666ad7e0509e0038016277": {
"serial": "interaction_hottextinteraction_666ad7e0509e0038016277",
"qtiClass": "hottextInteraction",
"attributes": {
"responseIdentifier": "RESPONSE",
"maxChoices": 0,
"minChoices": 0
},
"body": {
"serial": "container_containerhottext_666ad7e050d89560168530",
"body": "\n <p>Lorem ipsum dolor sit amet, consectetur adipisicing ...<\/p>\n ",
"elements": {},
"attributes": [],
"debug": {
"relatedItem": "item_666ad7e04dbfa162481935"
}
},
"debug": {
"relatedItem": "item_666ad7e04dbfa162481935"
},
"prompt": {
"serial": "container_containerstatic_666ad7e050d44698016231",
"body": "",
"elements": {},
"attributes": [],
"debug": {
"relatedItem": "item_666ad7e04dbfa162481935"
}
}
},
"interaction_hottextinteraction_666ad7e051230989121581": {
"serial": "interaction_hottextinteraction_666ad7e051230989121581",
"qtiClass": "hottextInteraction",
"attributes": {
"responseIdentifier": "RESPONSE_1",
"maxChoices": 0,
"minChoices": 0
},
"body": {
"serial": "container_containerhottext_666ad7e051312229314143",
"body": "\n <p>Lorem ipsum dolor sit amet, consectetur adipisicing ...<\/p>\n ",
"elements": {},
"attributes": [],
"debug": {
"relatedItem": "item_666ad7e04dbfa162481935"
}
},
"debug": {
"relatedItem": "item_666ad7e04dbfa162481935"
},
"prompt": {
"serial": "container_containerstatic_666ad7e0512d7853841108",
"body": "",
"elements": {},
"attributes": [],
"debug": {
"relatedItem": "item_666ad7e04dbfa162481935"
}
}
}
},
"attributes": [],
"debug": {
"relatedItem": "item_666ad7e04dbfa162481935"
}
},
"debug": {
"relatedItem": "item_666ad7e04dbfa162481935"
},
"namespaces": {
"": "http:\/\/www.imsglobal.org\/xsd\/imsqti_v2p2",
"m": "http:\/\/www.w3.org\/1998\/Math\/MathML",
"xsi": "http:\/\/www.w3.org\/2001\/XMLSchema-instance"
},
"schemaLocations": {
"http:\/\/www.imsglobal.org\/xsd\/imsqti_v2p2": "http:\/\/www.imsglobal.org\/xsd\/qti\/qtiv2p2\/imsqti_v2p2.xsd"
},
"stylesheets": {},
"outcomes": {
"outcomedeclaration_666ad7e04ed97862805920": {
"identifier": "OUTCOME_1",
"serial": "outcomedeclaration_666ad7e04ed97862805920",
"qtiClass": "outcomeDeclaration",
"attributes": {
"identifier": "OUTCOME_1",
"cardinality": "single",
"baseType": "float",
"longInterpretation": "",
"externalScored": "human",
"normalMaximum": 1,
"normalMinimum": 0
},
"debug": {
"relatedItem": "item_666ad7e04dbfa162481935"
},
"defaultValue": null
},
"outcomedeclaration_666ad7e04f8bc562427531": {
"identifier": "SCORE",
"serial": "outcomedeclaration_666ad7e04f8bc562427531",
"qtiClass": "outcomeDeclaration",
"attributes": {
"identifier": "SCORE",
"cardinality": "single",
"baseType": "float",
"longInterpretation": "",
"normalMaximum": 0,
"normalMinimum": 0
},
"debug": {
"relatedItem": "item_666ad7e04dbfa162481935"
},
"defaultValue": null
},
"outcomedeclaration_666ad7e04f8de522536800": {
"identifier": "MAXSCORE",
"serial": "outcomedeclaration_666ad7e04f8de522536800",
"qtiClass": "outcomeDeclaration",
"attributes": {
"identifier": "MAXSCORE",
"cardinality": "single",
"baseType": "float"
},
"debug": {
"relatedItem": "item_666ad7e04dbfa162481935"
},
"defaultValue": "1"
}
},
"responses": {
"responsedeclaration_666ad7e04eaa4288513651": {
"identifier": "RESPONSE",
"serial": "responsedeclaration_666ad7e04eaa4288513651",
"qtiClass": "responseDeclaration",
"attributes": {
"identifier": "RESPONSE",
"cardinality": "multiple",
"baseType": "identifier"
},
"debug": {
"relatedItem": "item_666ad7e04dbfa162481935"
},
"defaultValue": [],
"mapping": [],
"areaMapping": [],
"howMatch": "no_response_processing",
"correctResponses": [],
"mappingAttributes": {
"defaultValue": 0
},
"feedbackRules": {}
},
"responsedeclaration_666ad7e04ec84171775656": {
"identifier": "RESPONSE_1",
"serial": "responsedeclaration_666ad7e04ec84171775656",
"qtiClass": "responseDeclaration",
"attributes": {
"identifier": "RESPONSE_1",
"cardinality": "multiple",
"baseType": "identifier"
},
"debug": {
"relatedItem": "item_666ad7e04dbfa162481935"
},
"defaultValue": [],
"mapping": [],
"areaMapping": [],
"howMatch": "no_response_processing",
"correctResponses": [],
"mappingAttributes": {
"defaultValue": 0
},
"feedbackRules": {}
}
},
"feedbacks": {},
"responseProcessing": {
"serial": "response_templatesdriven_666ad7e051b2d784211813",
"qtiClass": "responseProcessing",
"attributes": {},
"debug": {
"relatedItem": "item_666ad7e04dbfa162481935"
},
"processingType": "templateDriven",
"responseRules": []
},
"apipAccessibility": ""
}
26 changes: 22 additions & 4 deletions test/qtiItem/maxScore/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ define([
'json!taoQtiItem/test/qtiItem/maxScore/data/graphic-associate-matchmax.json',
'json!taoQtiItem/test/qtiItem/maxScore/data/response-none.json',
'json!taoQtiItem/test/qtiItem/maxScore/data/external-scored.json',
'json!taoQtiItem/test/qtiItem/maxScore/data/external-scored-none.json'
'json!taoQtiItem/test/qtiItem/maxScore/data/external-scored-none.json',
'json!taoQtiItem/test/qtiItem/maxScore/data/external-scored-outcome.json'
], function(
_,
Element,
Expand Down Expand Up @@ -117,9 +118,12 @@ define([
dataResponseNone,
dataResponseExternalScored,
dataResponseExternalScoredNone,
dataResponseExternalScoredOutcome
) {
'use strict';

const externalScoredValues = ['human', 'externalMachine'];

var cases = [
{ title: 'single choice correct', data: dataChoiceCorrectMultiple, expectedMaximum: 1, maxScore: 1 },
{
Expand Down Expand Up @@ -412,6 +416,7 @@ define([
{ title: 'response - none', data: dataResponseNone, expectedMaximum: 5, maxScore: 5 },
{ title: 'external scored', data: dataResponseExternalScored, expectedMaximum: 6, maxScore: 6 },
{ title: 'removed MAXSCORE and SCORE', data: dataResponseExternalScoredNone, expectedMaximum: undefined, maxScore: undefined },
{ title: 'MAXSCORE and SCORE are NOT removed when outcome has external scored', data: dataResponseExternalScoredOutcome, expectedMaximum: 0, maxScore: 1 },
];

QUnit.cases.init(cases).test('setNormalMaximum', function(settings, assert) {
Expand All @@ -431,11 +436,24 @@ define([
assert.ok(Element.isA(item, 'assessmentItem'), 'item loaded');

outcomeScore = item.getOutcomeDeclaration('SCORE');
const customOutcomes = _(item.getOutcomes()).filter(function (outcome) {
return outcome.id() !== 'SCORE' && outcome.id() !== 'MAXSCORE';
});
const outcomesWithExternalScored = customOutcomes.filter(outcome => {
return externalScoredValues.includes(outcome.attr('externalScored'));
});


if (outcomeScore && outcomeScore.getAttributes()['externalScored']) {
assert.ok(outcomeScore.attr('normalMaximum'), 'normalMaximum defined');
if (
(outcomeScore && outcomeScore.getAttributes()['externalScored']) ||
outcomesWithExternalScored.size() !== 0
) {
assert.ok(_.isNumber(outcomeScore.attr('normalMaximum')), 'normalMaximum defined');
} else {
assert.ok(_.isUndefined(outcomeScore && outcomeScore.attr('normalMaximum')), 'normalMaximum initially undefined');
assert.ok(
_.isUndefined(outcomeScore && outcomeScore.attr('normalMaximum')),
'normalMaximum initially undefined'
);
}

maxScore.setNormalMaximum(item);
Expand Down

0 comments on commit 5b4b76f

Please sign in to comment.