UNPKG

@coorpacademy/progression-engine

Version:

202 lines (150 loc) 6.73 kB
'use strict'; 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