@oat-sa/tao-item-runner-qti
Version:
TAO QTI Item Runner modules
123 lines (105 loc) • 4.28 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) 2015-2019 (original work) Open Assessment Technlogies SA (under the project TAO-PRODUCT);
*
*/
/**
* The mapResponsePoint expression processor.
*
* @see http://www.imsglobal.org/question/qtiv2p1/imsqti_infov2p1.html#element10581
*
* @author Bertrand Chevrier <bertrand@taotesting.com>
*/
import _ from 'lodash';
import isPointInShape from 'taoQtiItem/scoring/processor/expressions/isPointInShape';
import errorHandler from 'taoQtiItem/scoring/processor/errorHandler';
/**
* The MapResponsePoint Processor
* @type {ExpressionProcesssor}
* @exports taoQtiItem/scoring/processor/expressions/mapResponsePoint
*/
var mapResponseProcessor = {
/**
* Process the expression
* @returns {ProcessingValue} the value from the expression
*/
process: function() {
var points,
defaultValue,
lowerBound,
upperBound,
filtered;
var identifier = this.expression.attributes.identifier;
var variable = this.state[identifier];
var result = {
cardinality: 'single',
baseType: 'float'
};
if (typeof variable === 'undefined' || variable === null) {
return null;
}
if (typeof variable.mapping === 'undefined' || variable.mapping.qtiClass !== 'areaMapping') {
return errorHandler.throw('scoring', new Error('The variable ' + identifier + ' has no areaMapping, how can I execute a mapResponsePoint on it?'));
}
if (variable.baseType !== 'point') {
return errorHandler.throw('scoring', new Error('The variable ' + identifier + ' must be of type point, but it is a ' + variable.baseType));
}
//cast the variable value
variable = this.preProcessor.parseVariable(variable);
//retrieve attributes
defaultValue = parseFloat(variable.mapping.attributes.defaultValue) || 0;
if (typeof variable.mapping.attributes.lowerBound !== 'undefined') {
lowerBound = parseFloat(variable.mapping.attributes.lowerBound);
}
if (typeof variable.mapping.attributes.upperBound !== 'undefined') {
upperBound = parseFloat(variable.mapping.attributes.upperBound);
}
//points are in an array
if (variable.cardinality === 'single') {
points = [variable.value];
} else {
points = variable.value;
}
//resolve the mapping
//filter entries that match
filtered = _.filter(variable.mapping.mapEntries, function(mapEntry) {
var found = _.filter(points, function(point) {
var coords = _.map(mapEntry.coords.split(','), parseFloat);
return isPointInShape(mapEntry.shape, point, coords);
});
return found.length > 0;
});
//then sum the entries values
if (filtered.length) {
result.value = _.reduce(filtered, function(acc, mapEntry) {
var value = parseFloat(mapEntry.mappedValue);
return acc + (_.isNaN(value) ? 0 : value);
}, 0);
}
// apply attributes
if (!_.isNumber(result.value)) {
result.value = defaultValue;
}
if (_.isNumber(lowerBound) && result.value < lowerBound) {
result.value = lowerBound;
}
if (_.isNumber(upperBound) && result.value > upperBound) {
result.value = upperBound;
}
return result;
}
};
export default mapResponseProcessor;