UNPKG

@coorpacademy/progression-engine

Version:

150 lines (148 loc) 6.47 kB
"use strict"; exports.__esModule = true; exports.default = checkAnswerCorrectness; var _zip2 = _interopRequireDefault(require("lodash/fp/zip")); var _trim2 = _interopRequireDefault(require("lodash/fp/trim")); var _toLower2 = _interopRequireDefault(require("lodash/fp/toLower")); var _split2 = _interopRequireDefault(require("lodash/fp/split")); var _some2 = _interopRequireDefault(require("lodash/fp/some")); var _reverse2 = _interopRequireDefault(require("lodash/fp/reverse")); var _pipe2 = _interopRequireDefault(require("lodash/fp/pipe")); var _maxBy2 = _interopRequireDefault(require("lodash/fp/maxBy")); var _map2 = _interopRequireDefault(require("lodash/fp/map")); var _join2 = _interopRequireDefault(require("lodash/fp/join")); var _includes2 = _interopRequireDefault(require("lodash/fp/includes")); var _get2 = _interopRequireDefault(require("lodash/fp/get")); var _filter2 = _interopRequireDefault(require("lodash/fp/filter")); var _every2 = _interopRequireDefault(require("lodash/fp/every")); var _fuzzyMatching = _interopRequireDefault(require("fuzzy-matching")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } 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, noFuzzy) { const allowedAnswers = _allowedAnswers.map(_trim2.default); const fm = new _fuzzyMatching.default(allowedAnswers); const maxTypos = _maxTypos === 0 ? _maxTypos : _maxTypos || config.maxTypos; if (noFuzzy) { return (0, _some2.default)(allowedAnswer => allowedAnswer === answerWithCase, allowedAnswers); } const answerWithoutCase = (0, _toLower2.default)(answerWithCase); return checkFuzzyAnswer(maxTypos, fm, answerWithCase) || maxTypos !== 0 && (0, _some2.default)(allowedAnswer => containsAnswer(config, (0, _toLower2.default)(allowedAnswer), answerWithoutCase), 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, false); 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]], answer, (0, _get2.default)(['content', 'choices', `${index}`, 'type'], question) === 'text' ? question.content.maxTypos : 0, (0, _get2.default)(['content', 'choices', `${index}`, 'type'], question) !== 'text')) })); const missingAnswers = question.content.answers[0].slice(result.length).map(() => ({ answer: undefined, isCorrect: false })); return [result.concat(missingAnswers)]; } function matchAnswerForUnorderedItems(allowedAnswers, givenAnswer) { return allowedAnswers.map(allowedAnswer => { const givenAnswersMap = (0, _map2.default)(answer => ({ answer, isCorrect: (0, _includes2.default)(answer, allowedAnswer) }), givenAnswer); if (allowedAnswer.some(answer => !(0, _includes2.default)(answer, givenAnswer))) { 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: givenAnswerPart === 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.map(answer => answer.map(_trim2.default)); 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