@oat-sa/tao-test-runner-qti
Version:
TAO Test Runner QTI implementation
161 lines (150 loc) • 6.63 kB
JavaScript
define(['lodash', 'taoQtiTest/runner/helpers/map', 'taoQtiTest/runner/config/states'], function (_, mapHelper, states) { 'use strict';
_ = _ && Object.prototype.hasOwnProperty.call(_, 'default') ? _['default'] : _;
mapHelper = mapHelper && Object.prototype.hasOwnProperty.call(mapHelper, 'default') ? mapHelper['default'] : mapHelper;
states = states && Object.prototype.hasOwnProperty.call(states, 'default') ? states['default'] : states;
/*
* 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) 2017 (original work) Open Assessment Technologies SA
*
*/
/**
* Get the updater
* @param {Map} testDataHolder - the data holder
* @returns {dataUpdater}
* @throws {TypeError} if the data holder is not or incorrectly set
*/
function dataUpdaterFactory(testDataHolder) {
if (!testDataHolder || !_.isFunction(testDataHolder.get) || !_.isFunction(testDataHolder.set)) {
throw new TypeError('A data holder is mandatory for the udpater');
}
/**
* Exposes the methods to update test data
* @typedef {Object} dataUpdater
*/
return {
/**
* Update test data from a dataSet (usually their raw data given by the proxy).
* If the dataSet is a collection, we use only the last matching object.
* @param {Object|Object[]} dataSet - object[s] that contains or not testData, testContext & testMap
*/
update: function update(dataSet) {
var self = this;
/**
* Check if a collection's item contains test contextual data
* @param {Object} contextualData
* @returns {Boolean}
*/
var isContextual = function isContextual(contextualData) {
return contextualData && (_.isPlainObject(contextualData.testContext) || _.isPlainObject(contextualData.testData) || _.isPlainObject(contextualData.testMap));
};
/**
* Update the test data from contextual data
* @param {Object} contextualData
*/
var updateData = function updateData(contextualData) {
var builtTestMap;
var updatedTestMap;
if (contextualData) {
if (_.isPlainObject(contextualData.testData)) {
testDataHolder.set('testData', contextualData.testData);
}
if (_.isPlainObject(contextualData.testContext)) {
testDataHolder.set('testContext', contextualData.testContext);
}
if (_.isPlainObject(contextualData.testMap)) {
//the received map is not complete and should be "built"
builtTestMap = self.buildTestMap(contextualData.testMap);
if (builtTestMap) {
testDataHolder.set('testMap', builtTestMap);
}
}
//always update the map stats
updatedTestMap = self.updateStats();
if (updatedTestMap) {
testDataHolder.set('testMap', updatedTestMap);
}
}
};
if (isContextual(dataSet)) {
updateData(dataSet);
} else {
updateData(_.findLast(dataSet, isContextual));
}
},
/**
* - reindex and build the jump table
* - patch the current testMap if a partial map is set
*
* @param {Object} testMap - the testMap to build
* @returns {Object} the built testMap
*/
buildTestMap: function buildTestMap(testMap) {
var newMap = null;
if (testMap) {
if (testMap.scope && testMap.scope !== 'test') {
newMap = mapHelper.patch(testDataHolder.get('testMap'), testMap);
} else {
newMap = mapHelper.reindex(testMap);
}
}
return newMap;
},
/**
* Update current map stats based on the context
*
* @returns {Object} the updated testMap
*/
updateStats: function updateStats() {
const testMap = testDataHolder.get('testMap');
const testContext = testDataHolder.get('testContext');
let updatedTestMap = null;
if (testMap && testContext && _.isNumber(testContext.itemPosition)) {
const item = mapHelper.getItemAt(testMap, testContext.itemPosition);
if (item && testContext.state === states.testSession.interacting) {
const testPart = mapHelper.getPart(testMap, testContext.testPartId);
//flag as viewed, always
item.viewed = true;
if (testPart && testPart.isLinear) {
item.answered = true;
}
updatedTestMap = mapHelper.updateItemStats(testMap, testContext.itemPosition);
}
}
return updatedTestMap;
},
/**
* Let's you update the plugins configuration from when filling testData
* @param {plugin[]} plugins - the test runner plugins
* @param {Objectt} [pluginsConfig] - the configuration to set on the plugins
*/
updatePluginsConfig(plugins, pluginsConfig) {
//to keep backward compatibility with the deprecated testData
if (!pluginsConfig) {
const testData = testDataHolder.get('testData');
pluginsConfig = testData && testData.config && testData.config.plugins;
}
if (plugins && pluginsConfig) {
_.forEach(pluginsConfig, (config, pluginName) => {
if (_.isPlainObject(plugins[pluginName]) && _.isFunction(plugins[pluginName].setConfig) && _.isPlainObject(config)) {
plugins[pluginName].setConfig(config);
}
});
}
}
};
}
return dataUpdaterFactory;
});