@coorpacademy/progression-engine
Version:
150 lines (148 loc) • 6.47 kB
JavaScript
;
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