@coorpacademy/progression-engine
Version:
202 lines (150 loc) • 6.73 kB
JavaScript
;
exports.__esModule = true;
exports.default = checkAnswerCorrectness;
var _every = require('lodash/fp/every');
var _every2 = _interopRequireDefault(_every);
var _filter = require('lodash/fp/filter');
var _filter2 = _interopRequireDefault(_filter);
var _get = require('lodash/fp/get');
var _get2 = _interopRequireDefault(_get);
var _includes = require('lodash/fp/includes');
var _includes2 = _interopRequireDefault(_includes);
var _join = require('lodash/fp/join');
var _join2 = _interopRequireDefault(_join);
var _map = require('lodash/fp/map');
var _map2 = _interopRequireDefault(_map);
var _maxBy = require('lodash/fp/maxBy');
var _maxBy2 = _interopRequireDefault(_maxBy);
var _pipe = require('lodash/fp/pipe');
var _pipe2 = _interopRequireDefault(_pipe);
var _reverse = require('lodash/fp/reverse');
var _reverse2 = _interopRequireDefault(_reverse);
var _some = require('lodash/fp/some');
var _some2 = _interopRequireDefault(_some);
var _split = require('lodash/fp/split');
var _split2 = _interopRequireDefault(_split);
var _toLower = require('lodash/fp/toLower');
var _toLower2 = _interopRequireDefault(_toLower);
var _trim = require('lodash/fp/trim');
var _trim2 = _interopRequireDefault(_trim);
var _zip = require('lodash/fp/zip');
var _zip2 = _interopRequireDefault(_zip);
var _fuzzyMatching = require('fuzzy-matching');
var _fuzzyMatching2 = _interopRequireDefault(_fuzzyMatching);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const reverseString = (0, _pipe2.default)((0, _split2.default)(''), _reverse2.default, (0, _join2.default)(''));
function checkFuzzyAnswer(maxTypos, fm, userAnswer) {
if (!userAnswer || userAnswer.length === 0) {
return false;
}
// Find a valid answer resembling userAnswer
return !!fm.get(userAnswer, { maxChanges: maxTypos }).value;
}
function containsAnswer(config, allowedAnswer, givenAnswer) {
// Find the allowed answer in the given answer
if (!(0, _includes2.default)(allowedAnswer, givenAnswer)) {
// If not present
return false;
}
// Get the non-space characters surrounding the answer and make sure that there are not too many.
const limit = config.answerBoundaryLimit;
const [first = '', second = ''] = givenAnswer.split(allowedAnswer);
const indexOfSpaceInFirst = reverseString(first).indexOf(' ');
const indexOfSpaceInSecond = second.indexOf(' ');
return (first.length <= limit || indexOfSpaceInFirst !== -1 && indexOfSpaceInFirst <= limit) && (second.length <= limit || indexOfSpaceInSecond !== -1 && indexOfSpaceInSecond <= limit);
}
function isTextCorrect(config, allowedAnswers, answerWithCase, _maxTypos) {
const fm = new _fuzzyMatching2.default(allowedAnswers);
const maxTypos = _maxTypos === 0 ? _maxTypos : _maxTypos || config.maxTypos;
const answer = (0, _toLower2.default)(answerWithCase);
return checkFuzzyAnswer(maxTypos, fm, answer) || maxTypos !== 0 && (0, _some2.default)(allowedAnswer => containsAnswer(config, (0, _toLower2.default)(allowedAnswer), answer), allowedAnswers);
}
function matchAnswerForBasic(config, question, givenAnswer) {
if (question.content.answers.length === 0) {
return [];
}
const isCorrect = isTextCorrect(config, question.content.answers.map(answers => answers[0]), givenAnswer[0], question.content.maxTypos);
return [[{ answer: givenAnswer[0], isCorrect }]];
}
function matchAnswerForTemplate(config, question, givenAnswer) {
if (question.content.answers.length === 0) {
return [];
}
const result = givenAnswer.map((answer, index) => ({
answer,
isCorrect: question.content.answers.some(allowedAnswer => isTextCorrect(config, [allowedAnswer[index]], (0, _toLower2.default)(answer), (0, _get2.default)(['content', 'choices', `${index}`, 'type'], question) === 'text' ? question.content.maxTypos : 0))
}));
const missingAnswers = question.content.answers[0].slice(result.length).map(() => ({ answer: undefined, isCorrect: false }));
return [result.concat(missingAnswers)];
}
function matchAnswerForUnorderedItems(allowedAnswers, givenAnswer) {
const lowerGivenAnswer = (0, _map2.default)(_toLower2.default, givenAnswer);
return allowedAnswers.map(allowedAnswer => {
const lowerAllowedAnswer = (0, _map2.default)(_toLower2.default, allowedAnswer);
const givenAnswersMap = (0, _map2.default)(answer => ({
answer,
isCorrect: (0, _includes2.default)((0, _toLower2.default)(answer), lowerAllowedAnswer)
}), givenAnswer);
if (lowerAllowedAnswer.some(answer => !(0, _includes2.default)(answer, lowerGivenAnswer))) {
return givenAnswersMap.concat([{ answer: undefined, isCorrect: false }]);
}
return givenAnswersMap;
});
}
function matchAnswerForOrderedItems(allowedAnswers, givenAnswer) {
return (0, _map2.default)(allowedAnswer => {
return (0, _map2.default)(([givenAnswerPart, allowedAnswerPart]) => {
return {
answer: givenAnswerPart,
isCorrect: (0, _toLower2.default)(givenAnswerPart) === (0, _toLower2.default)(allowedAnswerPart)
};
}, (0, _zip2.default)(givenAnswer, allowedAnswer));
}, allowedAnswers);
}
const findBestMatch = (0, _maxBy2.default)(correction => (0, _filter2.default)('isCorrect', correction).length);
function matchGivenAnswerToQuestion(config, question, givenAnswer) {
const allowedAnswers = question.content.answers;
switch (question.type) {
case 'basic':
{
return matchAnswerForBasic(config, question, givenAnswer);
}
case 'template':
{
return matchAnswerForTemplate(config, question, givenAnswer);
}
case 'qcm':
{
return matchAnswerForUnorderedItems(allowedAnswers, givenAnswer);
}
case 'qcmGraphic':
{
return matchAnswerForUnorderedItems(allowedAnswers, givenAnswer);
}
case 'qcmDrag':
{
return question.content.matchOrder ? matchAnswerForOrderedItems(allowedAnswers, givenAnswer) : matchAnswerForUnorderedItems(allowedAnswers, givenAnswer);
}
case 'slider':
{
return matchAnswerForOrderedItems(allowedAnswers, givenAnswer);
}
default:
return [[]];
}
}
function checkAnswerCorrectness(config, question, givenAnswer) {
const matches = matchGivenAnswerToQuestion(config, question, givenAnswer.map(_trim2.default));
if (matches.length === 0) {
return {
isCorrect: false,
corrections: []
};
}
const bestMatch = findBestMatch(matches);
return {
isCorrect: (0, _every2.default)('isCorrect', bestMatch),
corrections: (0, _filter2.default)(item => item.answer !== undefined, bestMatch)
};
}
//# sourceMappingURL=check-answer-correctness.js.map