@oat-sa/tao-test-runner-qti
Version:
TAO Test Runner QTI implementation
193 lines (171 loc) • 6.19 kB
JavaScript
/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2019 (original work) Open Assessment Technologies SA ;
*/
/**
* @author Péter Halász <peter@taotesting.com>
*/
import _ from 'lodash';
import capitalize from 'util/capitalize';
import mapHelper from 'taoQtiTest/runner/helpers/map';
import navigationHelper from 'taoQtiTest/runner/helpers/navigation';
/**
* Returns the updated test context from an item in the given position.
*
* @param {Object} testContext
* @param {Object} testMap
* @param {Integer} position
* @returns {Object}
*/
function buildTestContextFromPosition(testContext, testMap, position, attempt) {
var updatedMap = mapHelper.updateItemStats(testMap, position),
item = mapHelper.getItemAt(updatedMap, position),
section = mapHelper.getItemSection(updatedMap, position),
part = mapHelper.getItemPart(updatedMap, position);
if (!item) {
return false;
}
return getTestContext(testContext, testMap, item, section, part, position, attempt);
}
/**
* Returns the updated test context from a jump table entry.
*
* @param {Object} testContext
* @param {Object} testMap
* @param {Object} jump
* @param {String} jump.item
* @param {String} jump.section
* @param {String} jump.part
* @param {Integer} jump.position
* @returns {Object}
*/
function buildTestContextFromJump(testContext, testMap, jump, attempt) {
var part = testMap.parts[jump.part],
section = part.sections[jump.section],
item = section.items[jump.item];
return getTestContext(testContext, testMap, item, section, part, jump.position, attempt);
}
/**
* Returns the updated test context.
*
* @param {Object} testContext
* @param {Object} testMap
* @param {Object} item
* @param {String} item.id
* @param {Boolean} item.answered
* @param {Integer} item.remainingAttempts
* @param {Object} item.timeConstraint
* @param {String[]} item.categories
* @param {Object} section
* @param {String} section.id
* @param {String} section.label
* @param {Object} section.timeConstraint
* @param {Object} part
* @param {String} part.id
* @param {Boolean} part.isLinear
* @param {Object} part.timeConstraint
* @param {Integer} position
* @returns {Object} the new test context
* @private
*/
function getTestContext(testContext, testMap, item, section, part, position, attempt) {
var isLeavingSection = section.id !== testContext.sectionId,
isLeavingPart = part.id !== testContext.testPartId,
newTestContext = _.defaults(
{
itemIdentifier: item.id,
itemPosition: position,
remainingAttempts: Math.max(-1, item.remainingAttempts - 1),
sectionId: section.id,
sectionTitle: section.label,
testPartId: part.id,
canMoveBackward: !part.isLinear && !navigationHelper.isFirst(testMap, item.id)
},
testContext
);
if (attempt) {
newTestContext.attempt = attempt + 1;
}
if (isLeavingSection) {
newTestContext.numberRubrics = 0;
newTestContext.rubrics = '';
}
newTestContext.timeConstraints = _.reject(testContext.timeConstraints, function(constraint) {
return (
constraint.qtiClassName === 'assessmentItemRef' ||
(isLeavingSection && constraint.qtiClassName === 'assessmentSection') ||
(isLeavingPart && constraint.qtiClassName === 'testPart')
);
});
if (item.timeConstraint) {
newTestContext.timeConstraints.push(item.timeConstraint);
}
if (isLeavingSection && section.timeConstraint) {
newTestContext.timeConstraints.push(section.timeConstraint);
}
if (isLeavingPart && part.timeConstraint) {
newTestContext.timeConstraints.push(part.timeConstraint);
}
newTestContext.options = _.defaults(
getOptionsFromCategories(item.categories || []),
_.pick(testContext.options, ['allowComment', 'allowSkipping', 'exitButton', 'logoutButton'])
);
return newTestContext;
}
/**
* Transforms the categories into context options.
*
* @param {String[]} categories - the list of categories
* @returns {Object} the options object like <optionName : Boolean>
* @private
*/
function getOptionsFromCategories(categories) {
if (!_.isArray(categories) || !categories.length) {
return {};
}
return _.reduce(
categories,
function(acc, category) {
var categoryName;
if (_.isString(category) && !_.isEmpty(category)) {
// transfrom the category name in an option name:
// x-tao-option-review-screen to reviewScreen
categoryName = category
.replace('x-tao-option-', '')
.split(/[-_]+/g)
.map(function(name, index) {
if (index === 0) {
return name;
}
if (name.length) {
return capitalize(name);
}
return '';
})
.join('');
if (categoryName) {
acc[categoryName] = true;
}
}
return acc;
},
{}
);
}
export default {
buildTestContextFromPosition: buildTestContextFromPosition,
buildTestContextFromJump: buildTestContextFromJump
};