@khanacademy/perseus-score 2.3.7 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/index.js +12 -5
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +37 -30
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -7,10 +7,10 @@ var kmath = require('@khanacademy/kmath');
|
|
|
7
7
|
var perseusCore = require('@khanacademy/perseus-core');
|
|
8
8
|
var _ = require('underscore');
|
|
9
9
|
|
|
10
|
-
function
|
|
10
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
11
11
|
|
|
12
|
-
function
|
|
13
|
-
if (e && e
|
|
12
|
+
function _interopNamespaceCompat(e) {
|
|
13
|
+
if (e && typeof e === 'object' && 'default' in e) return e;
|
|
14
14
|
var n = Object.create(null);
|
|
15
15
|
if (e) {
|
|
16
16
|
Object.keys(e).forEach(function (k) {
|
|
@@ -23,12 +23,12 @@ function _interopNamespace(e) {
|
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
|
-
n
|
|
26
|
+
n.default = e;
|
|
27
27
|
return Object.freeze(n);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
var KAS__namespace = /*#__PURE__*/
|
|
31
|
-
var ___default = /*#__PURE__*/
|
|
30
|
+
var KAS__namespace = /*#__PURE__*/_interopNamespaceCompat(KAS);
|
|
31
|
+
var ___default = /*#__PURE__*/_interopDefaultCompat(_);
|
|
32
32
|
|
|
33
33
|
const MISSING_PERCENT_ERROR = "MISSING_PERCENT_ERROR";
|
|
34
34
|
const NEEDS_TO_BE_SIMPLIFIED_ERROR = "NEEDS_TO_BE_SIMPLIFIED_ERROR";
|
|
@@ -135,7 +135,7 @@ const KhanAnswerTypes = {
|
|
|
135
135
|
defaultForms: "integer, proper, improper, mixed, decimal",
|
|
136
136
|
createValidatorFunctional: function (predicate, options) {
|
|
137
137
|
// Extract the options from the given solution object
|
|
138
|
-
options = ___default
|
|
138
|
+
options = ___default.default.extend({
|
|
139
139
|
simplify: "required",
|
|
140
140
|
ratio: false,
|
|
141
141
|
forms: KhanAnswerTypes.predicate.defaultForms
|
|
@@ -143,7 +143,7 @@ const KhanAnswerTypes = {
|
|
|
143
143
|
let acceptableForms;
|
|
144
144
|
// this is maintaining backwards compatibility
|
|
145
145
|
// TODO(merlob) fix all places that depend on this, then delete
|
|
146
|
-
if (!___default
|
|
146
|
+
if (!___default.default.isArray(options.forms)) {
|
|
147
147
|
acceptableForms = options.forms.split(/\s*,\s*/);
|
|
148
148
|
} else {
|
|
149
149
|
acceptableForms = options.forms;
|
|
@@ -164,8 +164,8 @@ const KhanAnswerTypes = {
|
|
|
164
164
|
// in the list so we don't prematurely complain about not having
|
|
165
165
|
// a percent sign when the user entered the correct answer in a
|
|
166
166
|
// different form (such as a decimal or fraction)
|
|
167
|
-
if (___default
|
|
168
|
-
acceptableForms = ___default
|
|
167
|
+
if (___default.default.contains(acceptableForms, "percent")) {
|
|
168
|
+
acceptableForms = ___default.default.without(acceptableForms, "percent");
|
|
169
169
|
acceptableForms.push("percent");
|
|
170
170
|
}
|
|
171
171
|
|
|
@@ -323,7 +323,7 @@ const KhanAnswerTypes = {
|
|
|
323
323
|
} else if (match = text.match(/^(.+)\s*\*?\s*(\\?pi|p|\u03c0|\\?tau|t|\u03c4|pau)$/i)) {
|
|
324
324
|
possibilities = forms.decimal(match[1]);
|
|
325
325
|
} else {
|
|
326
|
-
possibilities = ___default
|
|
326
|
+
possibilities = ___default.default.reduce(KhanAnswerTypes.predicate.defaultForms.split(/\s*,\s*/), function (memo, form) {
|
|
327
327
|
return memo.concat(forms[form](text));
|
|
328
328
|
}, []);
|
|
329
329
|
|
|
@@ -348,7 +348,7 @@ const KhanAnswerTypes = {
|
|
|
348
348
|
approximatesPi = true;
|
|
349
349
|
}
|
|
350
350
|
if (approximatesPi) {
|
|
351
|
-
___default
|
|
351
|
+
___default.default.each(possibilities, function (possibility) {
|
|
352
352
|
possibility.piApprox = true;
|
|
353
353
|
});
|
|
354
354
|
}
|
|
@@ -552,9 +552,9 @@ const KhanAnswerTypes = {
|
|
|
552
552
|
});
|
|
553
553
|
if (score.correct === false) {
|
|
554
554
|
let interpretedGuess = false;
|
|
555
|
-
___default
|
|
556
|
-
const anyAreNaN = ___default
|
|
557
|
-
return t.value != null && !___default
|
|
555
|
+
___default.default.each(forms, function (form) {
|
|
556
|
+
const anyAreNaN = ___default.default.any(form(guess), function (t) {
|
|
557
|
+
return t.value != null && !___default.default.isNaN(t.value);
|
|
558
558
|
});
|
|
559
559
|
if (anyAreNaN) {
|
|
560
560
|
interpretedGuess = true;
|
|
@@ -845,8 +845,8 @@ function validateDropdown(userInput) {
|
|
|
845
845
|
* - Otherwise, pass through the resulting points and message.
|
|
846
846
|
*/
|
|
847
847
|
function scoreExpression(userInput, rubric, locale) {
|
|
848
|
-
const options = ___default
|
|
849
|
-
___default
|
|
848
|
+
const options = ___default.default.clone(rubric);
|
|
849
|
+
___default.default.extend(options, {
|
|
850
850
|
decimal_separator: perseusCore.getDecimalSeparator(locale)
|
|
851
851
|
});
|
|
852
852
|
const createValidator = answer => {
|
|
@@ -866,7 +866,7 @@ function scoreExpression(userInput, rubric, locale) {
|
|
|
866
866
|
}
|
|
867
867
|
});
|
|
868
868
|
}
|
|
869
|
-
return KhanAnswerTypes.expression.createValidatorFunctional(expression.expr, ___default
|
|
869
|
+
return KhanAnswerTypes.expression.createValidatorFunctional(expression.expr, ___default.default({}).extend(options, {
|
|
870
870
|
simplify: answer.simplify,
|
|
871
871
|
form: answer.form
|
|
872
872
|
}));
|
|
@@ -887,8 +887,10 @@ function scoreExpression(userInput, rubric, locale) {
|
|
|
887
887
|
let matchMessage;
|
|
888
888
|
let allEmpty = true;
|
|
889
889
|
let firstUngradedResult;
|
|
890
|
+
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
890
891
|
for (const answerForm of rubric.answerForms || []) {
|
|
891
892
|
const validator = createValidator(answerForm);
|
|
893
|
+
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
892
894
|
if (!validator) {
|
|
893
895
|
continue;
|
|
894
896
|
}
|
|
@@ -1203,8 +1205,8 @@ function scoreInteractiveGraph(userInput, rubric) {
|
|
|
1203
1205
|
} else if (userInput.type === "segment" && rubric.correct.type === "segment" && userInput.coords != null) {
|
|
1204
1206
|
let guess = perseusCore.deepClone(userInput.coords);
|
|
1205
1207
|
let correct = perseusCore.deepClone(rubric.correct.coords);
|
|
1206
|
-
guess = ___default
|
|
1207
|
-
correct = ___default
|
|
1208
|
+
guess = ___default.default.invoke(guess, "sort").sort();
|
|
1209
|
+
correct = ___default.default.invoke(correct, "sort").sort();
|
|
1208
1210
|
if (perseusCore.approximateDeepEqual(guess, correct)) {
|
|
1209
1211
|
return {
|
|
1210
1212
|
type: "points",
|
|
@@ -1248,7 +1250,8 @@ function scoreInteractiveGraph(userInput, rubric) {
|
|
|
1248
1250
|
const guess = shouldReverseCoords ? coords.slice().reverse() : coords;
|
|
1249
1251
|
let match;
|
|
1250
1252
|
if (rubric.correct.match === "congruent") {
|
|
1251
|
-
const angles = ___default
|
|
1253
|
+
const angles = ___default.default.map([guess, correct], function (coords) {
|
|
1254
|
+
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
1252
1255
|
if (!coords) {
|
|
1253
1256
|
return false;
|
|
1254
1257
|
}
|
|
@@ -1274,7 +1277,7 @@ function scoreInteractiveGraph(userInput, rubric) {
|
|
|
1274
1277
|
|
|
1275
1278
|
// The input wasn't correct, so check if it's a blank input or if it's
|
|
1276
1279
|
// actually just wrong
|
|
1277
|
-
if (!hasValue || ___default
|
|
1280
|
+
if (!hasValue || ___default.default.isEqual(userInput, rubric.graph)) {
|
|
1278
1281
|
// We're where we started.
|
|
1279
1282
|
return {
|
|
1280
1283
|
type: "invalid",
|
|
@@ -1329,7 +1332,7 @@ function scoreLabelImage(userInput, rubric) {
|
|
|
1329
1332
|
}
|
|
1330
1333
|
|
|
1331
1334
|
function scoreMatcher(userInput, rubric) {
|
|
1332
|
-
const correct = ___default
|
|
1335
|
+
const correct = ___default.default.isEqual(userInput.left, rubric.left) && ___default.default.isEqual(userInput.right, rubric.right);
|
|
1333
1336
|
return {
|
|
1334
1337
|
type: "points",
|
|
1335
1338
|
earned: correct ? 1 : 0,
|
|
@@ -1347,8 +1350,8 @@ function scoreMatrix(userInput, rubric) {
|
|
|
1347
1350
|
const createValidator = KhanAnswerTypes.number.createValidatorFunctional;
|
|
1348
1351
|
let message = null;
|
|
1349
1352
|
let incorrect = false;
|
|
1350
|
-
___default
|
|
1351
|
-
___default
|
|
1353
|
+
___default.default(suppliedSize[0]).times(row => {
|
|
1354
|
+
___default.default(suppliedSize[1]).times(col => {
|
|
1352
1355
|
if (!incorrectSize) {
|
|
1353
1356
|
const validator = createValidator(
|
|
1354
1357
|
// @ts-expect-error - TS2345 - Argument of type 'number' is not assignable to parameter of type 'string'.
|
|
@@ -1411,7 +1414,9 @@ function scoreNumberLine(userInput, rubric) {
|
|
|
1411
1414
|
const start = rubric.initialX != null ? rubric.initialX : range[0];
|
|
1412
1415
|
const startRel = rubric.isInequality ? "ge" : "eq";
|
|
1413
1416
|
const correctRel = rubric.correctRel || "eq";
|
|
1414
|
-
const correctPos = kmath.number.equal(userInput.numLinePosition,
|
|
1417
|
+
const correctPos = kmath.number.equal(userInput.numLinePosition,
|
|
1418
|
+
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
1419
|
+
rubric.correctX || 0);
|
|
1415
1420
|
if (correctPos && correctRel === userInput.rel) {
|
|
1416
1421
|
return {
|
|
1417
1422
|
type: "points",
|
|
@@ -1559,6 +1564,7 @@ function walkTex(tex, handler) {
|
|
|
1559
1564
|
currentIndex = secondParsedExpression.endpoint + 1;
|
|
1560
1565
|
|
|
1561
1566
|
// Add expressions to running total of parsed expressions
|
|
1567
|
+
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
1562
1568
|
if (parsedString.length) {
|
|
1563
1569
|
parsedString += " ";
|
|
1564
1570
|
}
|
|
@@ -1710,9 +1716,7 @@ function scoreNumericInput(userInput, rubric) {
|
|
|
1710
1716
|
const result = matchedAnswer?.status === "correct" ? matchedAnswer.score : {
|
|
1711
1717
|
empty: matchedAnswer?.status === "ungraded",
|
|
1712
1718
|
correct: matchedAnswer?.status === "correct",
|
|
1713
|
-
message: matchedAnswer?.message ?? null
|
|
1714
|
-
guess: localValue
|
|
1715
|
-
};
|
|
1719
|
+
message: matchedAnswer?.message ?? null};
|
|
1716
1720
|
if (result.empty) {
|
|
1717
1721
|
return {
|
|
1718
1722
|
type: "invalid",
|
|
@@ -1728,7 +1732,7 @@ function scoreNumericInput(userInput, rubric) {
|
|
|
1728
1732
|
}
|
|
1729
1733
|
|
|
1730
1734
|
function scoreOrderer(userInput, rubric) {
|
|
1731
|
-
const correct = ___default
|
|
1735
|
+
const correct = ___default.default.isEqual(userInput.current, rubric.correctOptions.map(option => option.content));
|
|
1732
1736
|
return {
|
|
1733
1737
|
type: "points",
|
|
1734
1738
|
earned: correct ? 1 : 0,
|
|
@@ -1892,6 +1896,8 @@ function validateTable(userInput) {
|
|
|
1892
1896
|
return cell === "";
|
|
1893
1897
|
});
|
|
1894
1898
|
});
|
|
1899
|
+
|
|
1900
|
+
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
1895
1901
|
if (hasEmptyCell || !supplied.length) {
|
|
1896
1902
|
return {
|
|
1897
1903
|
type: "invalid",
|
|
@@ -2034,6 +2040,7 @@ function scoreNoop() {
|
|
|
2034
2040
|
}
|
|
2035
2041
|
|
|
2036
2042
|
// The `group` widget is basically a widget hosting a full Perseus system in
|
|
2043
|
+
|
|
2037
2044
|
// it. As such, scoring a group means scoring all widgets it contains.
|
|
2038
2045
|
function scoreGroup(userInput, rubric, locale) {
|
|
2039
2046
|
const scores = scoreWidgetsFunctional(rubric.widgets, Object.keys(rubric.widgets), userInput, locale);
|