@khanacademy/perseus 45.1.0 → 46.0.1
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.css +1 -1
- package/dist/es/index.js +166 -186
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +141 -160
- package/dist/index.js.map +1 -1
- package/dist/renderer.d.ts +0 -1
- package/dist/types.d.ts +0 -1
- package/dist/util/scoring.d.ts +20 -0
- package/dist/util.d.ts +0 -30
- package/dist/widgets/input-number/input-number.d.ts +1 -1
- package/dist/widgets/numeric-input/numeric-input.d.ts +5 -1
- package/package.json +1 -1
package/dist/index.css
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -31,6 +31,7 @@ export { default as MatrixWidget } from "./widgets/matrix";
|
|
|
31
31
|
export { default as TableWidget } from "./widgets/table";
|
|
32
32
|
export { default as PlotterWidget } from "./widgets/plotter";
|
|
33
33
|
export { default as GrapherWidget } from "./widgets/grapher";
|
|
34
|
+
import { keScoreFromPerseusScore } from "./util/scoring";
|
|
34
35
|
export declare const GrapherUtil: {
|
|
35
36
|
allTypes: any;
|
|
36
37
|
DEFAULT_GRAPHER_PROPS: any;
|
|
@@ -39,6 +40,9 @@ export declare const GrapherUtil: {
|
|
|
39
40
|
getEquationString: (props: any) => string;
|
|
40
41
|
typeToButton: (type: "linear" | "absolute_value" | "exponential" | "logarithm" | "quadratic" | "sinusoid" | "tangent") => any;
|
|
41
42
|
};
|
|
43
|
+
export declare const ScoringUtil: {
|
|
44
|
+
keScoreFromPerseusScore: typeof keScoreFromPerseusScore;
|
|
45
|
+
};
|
|
42
46
|
/**
|
|
43
47
|
* Misc
|
|
44
48
|
*/
|
package/dist/index.js
CHANGED
|
@@ -2915,12 +2915,6 @@ const rWidgetRule = /^\[\[\u2603 (([a-z-]+) ([0-9]+))\]\]/;
|
|
|
2915
2915
|
const rTypeFromWidgetId = /^([a-z-]+) ([0-9]+)$/;
|
|
2916
2916
|
const rWidgetParts = new RegExp(rWidgetRule.source + "$");
|
|
2917
2917
|
const snowman = "\u2603";
|
|
2918
|
-
const noScore = {
|
|
2919
|
-
type: "points",
|
|
2920
|
-
earned: 0,
|
|
2921
|
-
total: 0,
|
|
2922
|
-
message: null
|
|
2923
|
-
};
|
|
2924
2918
|
const seededRNG$1 = function (seed) {
|
|
2925
2919
|
let randomSeed = seed;
|
|
2926
2920
|
return function () {
|
|
@@ -2998,97 +2992,6 @@ const split = "x".split(/(.)/g).length ? function (str, r) {
|
|
|
2998
2992
|
return output;
|
|
2999
2993
|
};
|
|
3000
2994
|
|
|
3001
|
-
/**
|
|
3002
|
-
* Combine two score objects.
|
|
3003
|
-
*
|
|
3004
|
-
* Given two score objects for two different widgets, combine them so that
|
|
3005
|
-
* if one is wrong, the total score is wrong, etc.
|
|
3006
|
-
*/
|
|
3007
|
-
function combineScores(scoreA, scoreB) {
|
|
3008
|
-
let message;
|
|
3009
|
-
if (scoreA.type === "points" && scoreB.type === "points") {
|
|
3010
|
-
if (scoreA.message && scoreB.message && scoreA.message !== scoreB.message) {
|
|
3011
|
-
// TODO(alpert): Figure out how to combine messages usefully
|
|
3012
|
-
message = null;
|
|
3013
|
-
} else {
|
|
3014
|
-
message = scoreA.message || scoreB.message;
|
|
3015
|
-
}
|
|
3016
|
-
return {
|
|
3017
|
-
type: "points",
|
|
3018
|
-
earned: scoreA.earned + scoreB.earned,
|
|
3019
|
-
total: scoreA.total + scoreB.total,
|
|
3020
|
-
message: message
|
|
3021
|
-
};
|
|
3022
|
-
}
|
|
3023
|
-
if (scoreA.type === "points" && scoreB.type === "invalid") {
|
|
3024
|
-
return scoreB;
|
|
3025
|
-
}
|
|
3026
|
-
if (scoreA.type === "invalid" && scoreB.type === "points") {
|
|
3027
|
-
return scoreA;
|
|
3028
|
-
}
|
|
3029
|
-
if (scoreA.type === "invalid" && scoreB.type === "invalid") {
|
|
3030
|
-
if (scoreA.message && scoreB.message && scoreA.message !== scoreB.message) {
|
|
3031
|
-
// TODO(alpert): Figure out how to combine messages usefully
|
|
3032
|
-
message = null;
|
|
3033
|
-
} else {
|
|
3034
|
-
message = scoreA.message || scoreB.message;
|
|
3035
|
-
}
|
|
3036
|
-
return {
|
|
3037
|
-
type: "invalid",
|
|
3038
|
-
message: message
|
|
3039
|
-
};
|
|
3040
|
-
}
|
|
3041
|
-
|
|
3042
|
-
/**
|
|
3043
|
-
* The above checks cover all combinations of score type, so if we get here
|
|
3044
|
-
* then something is amiss with our inputs.
|
|
3045
|
-
*/
|
|
3046
|
-
throw new perseusCore.PerseusError("PerseusScore with unknown type encountered", perseusCore.Errors.InvalidInput, {
|
|
3047
|
-
metadata: {
|
|
3048
|
-
scoreA: JSON.stringify(scoreA),
|
|
3049
|
-
scoreB: JSON.stringify(scoreB)
|
|
3050
|
-
}
|
|
3051
|
-
});
|
|
3052
|
-
}
|
|
3053
|
-
function flattenScores(widgetScoreMap) {
|
|
3054
|
-
return Object.values(widgetScoreMap).reduce(combineScores, noScore);
|
|
3055
|
-
}
|
|
3056
|
-
function isCorrect(score) {
|
|
3057
|
-
return score.type === "points" && score.earned >= score.total;
|
|
3058
|
-
}
|
|
3059
|
-
function keScoreFromPerseusScore(score,
|
|
3060
|
-
// It's weird, but this is what we're passing it
|
|
3061
|
-
guess, state) {
|
|
3062
|
-
if (score.type === "points") {
|
|
3063
|
-
return {
|
|
3064
|
-
empty: false,
|
|
3065
|
-
correct: isCorrect(score),
|
|
3066
|
-
message: score.message,
|
|
3067
|
-
guess: guess,
|
|
3068
|
-
state: state
|
|
3069
|
-
};
|
|
3070
|
-
}
|
|
3071
|
-
if (score.type === "invalid") {
|
|
3072
|
-
return {
|
|
3073
|
-
empty: true,
|
|
3074
|
-
correct: false,
|
|
3075
|
-
message: score.message,
|
|
3076
|
-
suppressAlmostThere: score.suppressAlmostThere,
|
|
3077
|
-
guess: guess,
|
|
3078
|
-
state: state
|
|
3079
|
-
};
|
|
3080
|
-
}
|
|
3081
|
-
throw new perseusCore.PerseusError(
|
|
3082
|
-
// @ts-expect-error - TS2339 - Property 'type' does not exist on type 'never'.
|
|
3083
|
-
"Invalid score type: " + score.type, perseusCore.Errors.InvalidInput, {
|
|
3084
|
-
metadata: {
|
|
3085
|
-
score: JSON.stringify(score),
|
|
3086
|
-
guess: JSON.stringify(guess),
|
|
3087
|
-
state: JSON.stringify(state)
|
|
3088
|
-
}
|
|
3089
|
-
});
|
|
3090
|
-
}
|
|
3091
|
-
|
|
3092
2995
|
/**
|
|
3093
2996
|
* Return the first valid interpretation of 'text' as a number, in the form
|
|
3094
2997
|
* {value: 2.3, exact: true}.
|
|
@@ -3386,28 +3289,6 @@ function strongEncodeURIComponent(str) {
|
|
|
3386
3289
|
.replace(/\*/g, "%2A");
|
|
3387
3290
|
}
|
|
3388
3291
|
|
|
3389
|
-
/**
|
|
3390
|
-
* If a widget says that it is empty once it is graded.
|
|
3391
|
-
* Trying to encapsulate references to the score format.
|
|
3392
|
-
*/
|
|
3393
|
-
function scoreIsEmpty(score) {
|
|
3394
|
-
// HACK(benkomalo): ugh. this isn't great; the Perseus score objects
|
|
3395
|
-
// overload the type "invalid" for what should probably be three
|
|
3396
|
-
// distinct cases:
|
|
3397
|
-
// - truly empty or not fully filled out
|
|
3398
|
-
// - invalid or malformed inputs
|
|
3399
|
-
// - "almost correct" like inputs where the widget wants to give
|
|
3400
|
-
// feedback (e.g. a fraction needs to be reduced, or `pi` should
|
|
3401
|
-
// be used instead of 3.14)
|
|
3402
|
-
//
|
|
3403
|
-
// Unfortunately the coercion happens all over the place, as these
|
|
3404
|
-
// Perseus style score objects are created *everywhere* (basically
|
|
3405
|
-
// in every widget), so it's hard to change now. We assume that
|
|
3406
|
-
// anything with a "message" is not truly empty, and one of the
|
|
3407
|
-
// latter two cases for now.
|
|
3408
|
-
return score.type === "invalid" && (!score.message || score.message.length === 0);
|
|
3409
|
-
}
|
|
3410
|
-
|
|
3411
3292
|
/*
|
|
3412
3293
|
* The touchHandlers are used to track the current state of the touch
|
|
3413
3294
|
* event, such as whether or not the user is currently pressed down (either
|
|
@@ -3580,13 +3461,9 @@ const Util = {
|
|
|
3580
3461
|
rTypeFromWidgetId,
|
|
3581
3462
|
rWidgetParts,
|
|
3582
3463
|
snowman,
|
|
3583
|
-
noScore,
|
|
3584
3464
|
seededRNG: seededRNG$1,
|
|
3585
3465
|
shuffle: shuffle$3,
|
|
3586
3466
|
split,
|
|
3587
|
-
combineScores,
|
|
3588
|
-
flattenScores,
|
|
3589
|
-
keScoreFromPerseusScore,
|
|
3590
3467
|
firstNumericalParse: firstNumericalParse$1,
|
|
3591
3468
|
stringArrayOfSize: stringArrayOfSize$1,
|
|
3592
3469
|
gridDimensionConfig,
|
|
@@ -3602,7 +3479,6 @@ const Util = {
|
|
|
3602
3479
|
parseQueryString,
|
|
3603
3480
|
updateQueryString: updateQueryString$2,
|
|
3604
3481
|
strongEncodeURIComponent,
|
|
3605
|
-
scoreIsEmpty,
|
|
3606
3482
|
touchHandlers,
|
|
3607
3483
|
resetTouchHandlers,
|
|
3608
3484
|
extractPointerLocation,
|
|
@@ -18902,6 +18778,126 @@ class QuestionParagraph extends React__namespace.Component {
|
|
|
18902
18778
|
}
|
|
18903
18779
|
}
|
|
18904
18780
|
|
|
18781
|
+
const noScore = {
|
|
18782
|
+
type: "points",
|
|
18783
|
+
earned: 0,
|
|
18784
|
+
total: 0,
|
|
18785
|
+
message: null
|
|
18786
|
+
};
|
|
18787
|
+
|
|
18788
|
+
/**
|
|
18789
|
+
* If a widget says that it is empty once it is graded.
|
|
18790
|
+
* Trying to encapsulate references to the score format.
|
|
18791
|
+
*/
|
|
18792
|
+
function scoreIsEmpty(score) {
|
|
18793
|
+
// HACK(benkomalo): ugh. this isn't great; the Perseus score objects
|
|
18794
|
+
// overload the type "invalid" for what should probably be three
|
|
18795
|
+
// distinct cases:
|
|
18796
|
+
// - truly empty or not fully filled out
|
|
18797
|
+
// - invalid or malformed inputs
|
|
18798
|
+
// - "almost correct" like inputs where the widget wants to give
|
|
18799
|
+
// feedback (e.g. a fraction needs to be reduced, or `pi` should
|
|
18800
|
+
// be used instead of 3.14)
|
|
18801
|
+
//
|
|
18802
|
+
// Unfortunately the coercion happens all over the place, as these
|
|
18803
|
+
// Perseus style score objects are created *everywhere* (basically
|
|
18804
|
+
// in every widget), so it's hard to change now. We assume that
|
|
18805
|
+
// anything with a "message" is not truly empty, and one of the
|
|
18806
|
+
// latter two cases for now.
|
|
18807
|
+
return score.type === "invalid" && (!score.message || score.message.length === 0);
|
|
18808
|
+
}
|
|
18809
|
+
|
|
18810
|
+
/**
|
|
18811
|
+
* Combine two score objects.
|
|
18812
|
+
*
|
|
18813
|
+
* Given two score objects for two different widgets, combine them so that
|
|
18814
|
+
* if one is wrong, the total score is wrong, etc.
|
|
18815
|
+
*/
|
|
18816
|
+
function combineScores(scoreA, scoreB) {
|
|
18817
|
+
let message;
|
|
18818
|
+
if (scoreA.type === "points" && scoreB.type === "points") {
|
|
18819
|
+
if (scoreA.message && scoreB.message && scoreA.message !== scoreB.message) {
|
|
18820
|
+
// TODO(alpert): Figure out how to combine messages usefully
|
|
18821
|
+
message = null;
|
|
18822
|
+
} else {
|
|
18823
|
+
message = scoreA.message || scoreB.message;
|
|
18824
|
+
}
|
|
18825
|
+
return {
|
|
18826
|
+
type: "points",
|
|
18827
|
+
earned: scoreA.earned + scoreB.earned,
|
|
18828
|
+
total: scoreA.total + scoreB.total,
|
|
18829
|
+
message: message
|
|
18830
|
+
};
|
|
18831
|
+
}
|
|
18832
|
+
if (scoreA.type === "points" && scoreB.type === "invalid") {
|
|
18833
|
+
return scoreB;
|
|
18834
|
+
}
|
|
18835
|
+
if (scoreA.type === "invalid" && scoreB.type === "points") {
|
|
18836
|
+
return scoreA;
|
|
18837
|
+
}
|
|
18838
|
+
if (scoreA.type === "invalid" && scoreB.type === "invalid") {
|
|
18839
|
+
if (scoreA.message && scoreB.message && scoreA.message !== scoreB.message) {
|
|
18840
|
+
// TODO(alpert): Figure out how to combine messages usefully
|
|
18841
|
+
message = null;
|
|
18842
|
+
} else {
|
|
18843
|
+
message = scoreA.message || scoreB.message;
|
|
18844
|
+
}
|
|
18845
|
+
return {
|
|
18846
|
+
type: "invalid",
|
|
18847
|
+
message: message
|
|
18848
|
+
};
|
|
18849
|
+
}
|
|
18850
|
+
|
|
18851
|
+
/**
|
|
18852
|
+
* The above checks cover all combinations of score type, so if we get here
|
|
18853
|
+
* then something is amiss with our inputs.
|
|
18854
|
+
*/
|
|
18855
|
+
throw new perseusCore.PerseusError("PerseusScore with unknown type encountered", perseusCore.Errors.InvalidInput, {
|
|
18856
|
+
metadata: {
|
|
18857
|
+
scoreA: JSON.stringify(scoreA),
|
|
18858
|
+
scoreB: JSON.stringify(scoreB)
|
|
18859
|
+
}
|
|
18860
|
+
});
|
|
18861
|
+
}
|
|
18862
|
+
function flattenScores(widgetScoreMap) {
|
|
18863
|
+
return Object.values(widgetScoreMap).reduce(combineScores, noScore);
|
|
18864
|
+
}
|
|
18865
|
+
function isCorrect(score) {
|
|
18866
|
+
return score.type === "points" && score.earned >= score.total;
|
|
18867
|
+
}
|
|
18868
|
+
function keScoreFromPerseusScore(score,
|
|
18869
|
+
// It's weird, but this is what we're passing it
|
|
18870
|
+
guess, state) {
|
|
18871
|
+
if (score.type === "points") {
|
|
18872
|
+
return {
|
|
18873
|
+
empty: false,
|
|
18874
|
+
correct: isCorrect(score),
|
|
18875
|
+
message: score.message,
|
|
18876
|
+
guess: guess,
|
|
18877
|
+
state: state
|
|
18878
|
+
};
|
|
18879
|
+
}
|
|
18880
|
+
if (score.type === "invalid") {
|
|
18881
|
+
return {
|
|
18882
|
+
empty: true,
|
|
18883
|
+
correct: false,
|
|
18884
|
+
message: score.message,
|
|
18885
|
+
suppressAlmostThere: score.suppressAlmostThere,
|
|
18886
|
+
guess: guess,
|
|
18887
|
+
state: state
|
|
18888
|
+
};
|
|
18889
|
+
}
|
|
18890
|
+
throw new perseusCore.PerseusError(
|
|
18891
|
+
// @ts-expect-error - TS2339 - Property 'type' does not exist on type 'never'.
|
|
18892
|
+
"Invalid score type: " + score.type, perseusCore.Errors.InvalidInput, {
|
|
18893
|
+
metadata: {
|
|
18894
|
+
score: JSON.stringify(score),
|
|
18895
|
+
guess: JSON.stringify(guess),
|
|
18896
|
+
state: JSON.stringify(state)
|
|
18897
|
+
}
|
|
18898
|
+
});
|
|
18899
|
+
}
|
|
18900
|
+
|
|
18905
18901
|
// These are all the current widgets that require user-input and are supported for automatic scoring
|
|
18906
18902
|
const QUESTION_WIDGETS = ["numeric-input", "input-number", "radio", "interactive-graph", "interactive-number-line", "categorizer", "plotter", "orderer", "protractor", "matcher", "sorter"];
|
|
18907
18903
|
|
|
@@ -19500,7 +19496,7 @@ widgetIds, userInputMap, strings, locale) {
|
|
|
19500
19496
|
const scorer = getWidgetScorer(widget.type);
|
|
19501
19497
|
const score = scorer?.(userInputMap[id], widget.options, strings, locale);
|
|
19502
19498
|
if (score) {
|
|
19503
|
-
return
|
|
19499
|
+
return scoreIsEmpty(score);
|
|
19504
19500
|
}
|
|
19505
19501
|
});
|
|
19506
19502
|
}
|
|
@@ -19516,7 +19512,7 @@ strings, locale) {
|
|
|
19516
19512
|
// so this checks that the widgets are being used before scoring them
|
|
19517
19513
|
const usedWidgetIds = getWidgetIdsFromContent(perseusRenderData.content);
|
|
19518
19514
|
const scores = scoreWidgetsFunctional(perseusRenderData.widgets, usedWidgetIds, userInputMap, strings, locale);
|
|
19519
|
-
return
|
|
19515
|
+
return flattenScores(scores);
|
|
19520
19516
|
}
|
|
19521
19517
|
function scoreWidgetsFunctional(widgets,
|
|
19522
19518
|
// This is a port of old code, I'm not sure why
|
|
@@ -21102,7 +21098,7 @@ class Renderer extends React__namespace.Component {
|
|
|
21102
21098
|
*/
|
|
21103
21099
|
score() {
|
|
21104
21100
|
const scores = scoreWidgetsFunctional(this.state.widgetInfo, this.widgetIds, this.getUserInputMap(), this.props.strings, this.context.locale);
|
|
21105
|
-
const combinedScore =
|
|
21101
|
+
const combinedScore = flattenScores(scores);
|
|
21106
21102
|
return combinedScore;
|
|
21107
21103
|
}
|
|
21108
21104
|
|
|
@@ -21114,28 +21110,6 @@ class Renderer extends React__namespace.Component {
|
|
|
21114
21110
|
const totalScore = this.score();
|
|
21115
21111
|
return [totalGuess, totalScore];
|
|
21116
21112
|
};
|
|
21117
|
-
examples = () => {
|
|
21118
|
-
const widgetIds = this.widgetIds;
|
|
21119
|
-
const examples = widgetIds.map(widgetId => {
|
|
21120
|
-
const widget = this.getWidgetInstance(widgetId);
|
|
21121
|
-
return widget != null && widget.examples ? widget.examples() : null;
|
|
21122
|
-
}).filter(Boolean);
|
|
21123
|
-
|
|
21124
|
-
// no widgets with examples
|
|
21125
|
-
if (!examples.length) {
|
|
21126
|
-
return null;
|
|
21127
|
-
}
|
|
21128
|
-
const allEqual = ___default["default"].all(examples, function (example) {
|
|
21129
|
-
return ___default["default"].isEqual(examples[0], example);
|
|
21130
|
-
});
|
|
21131
|
-
|
|
21132
|
-
// some widgets have different examples
|
|
21133
|
-
// TODO(alex): handle this better
|
|
21134
|
-
if (!allEqual) {
|
|
21135
|
-
return null;
|
|
21136
|
-
}
|
|
21137
|
-
return examples[0];
|
|
21138
|
-
};
|
|
21139
21113
|
|
|
21140
21114
|
// TranslationLinter callback
|
|
21141
21115
|
handletranslationLintErrors = lintErrors => {
|
|
@@ -22180,7 +22154,7 @@ class InputNumber extends React__namespace.Component {
|
|
|
22180
22154
|
getPromptJSON() {
|
|
22181
22155
|
return getPromptJSON$s(this.props, this.getUserInput());
|
|
22182
22156
|
}
|
|
22183
|
-
examples
|
|
22157
|
+
examples() {
|
|
22184
22158
|
const {
|
|
22185
22159
|
strings
|
|
22186
22160
|
} = this.context;
|
|
@@ -22188,7 +22162,7 @@ class InputNumber extends React__namespace.Component {
|
|
|
22188
22162
|
const forms = answerTypes[type].forms.split(/\s*,\s*/);
|
|
22189
22163
|
const examples = ___default["default"].map(forms, form => formExamples$1[form](this.props, strings));
|
|
22190
22164
|
return [strings.yourAnswer].concat(examples);
|
|
22191
|
-
}
|
|
22165
|
+
}
|
|
22192
22166
|
render() {
|
|
22193
22167
|
if (this.props.apiOptions.customKeypad) {
|
|
22194
22168
|
// TODO(charlie): Support "Review Mode".
|
|
@@ -22474,8 +22448,11 @@ class NumericInput extends React__namespace.Component {
|
|
|
22474
22448
|
isFocused: false
|
|
22475
22449
|
};
|
|
22476
22450
|
|
|
22477
|
-
|
|
22478
|
-
|
|
22451
|
+
/**
|
|
22452
|
+
* Generates a string that demonstrates how to input the various supported
|
|
22453
|
+
* answer forms.
|
|
22454
|
+
*/
|
|
22455
|
+
examples() {
|
|
22479
22456
|
// if the set of specified forms are empty, allow all forms
|
|
22480
22457
|
const forms = this.props.answerForms?.length !== 0 ? this.props.answerForms : Object.keys(formExamples).map(name => {
|
|
22481
22458
|
return {
|
|
@@ -22490,7 +22467,7 @@ class NumericInput extends React__namespace.Component {
|
|
|
22490
22467
|
// versions of the same format
|
|
22491
22468
|
examples = ___default["default"].uniq(examples);
|
|
22492
22469
|
return [this.context.strings.yourAnswer].concat(examples);
|
|
22493
|
-
}
|
|
22470
|
+
}
|
|
22494
22471
|
shouldShowExamples = () => {
|
|
22495
22472
|
const noFormsAccepted = this.props.answerForms?.length === 0;
|
|
22496
22473
|
// To check if all answer forms are accepted, we must first
|
|
@@ -29662,7 +29639,7 @@ const getPromptJSON$f = rendererJSON => {
|
|
|
29662
29639
|
// it. As such, scoring a group means scoring all widgets it contains.
|
|
29663
29640
|
function scoreGroup(userInput, options, strings, locale) {
|
|
29664
29641
|
const scores = scoreWidgetsFunctional(options.widgets, Object.keys(options.widgets), userInput, strings, locale);
|
|
29665
|
-
return
|
|
29642
|
+
return flattenScores(scores);
|
|
29666
29643
|
}
|
|
29667
29644
|
|
|
29668
29645
|
// exports has no 'transform'
|
|
@@ -45479,7 +45456,7 @@ const init = function () {
|
|
|
45479
45456
|
|
|
45480
45457
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
45481
45458
|
const libName = "@khanacademy/perseus";
|
|
45482
|
-
const libVersion = "
|
|
45459
|
+
const libVersion = "46.0.1";
|
|
45483
45460
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
45484
45461
|
|
|
45485
45462
|
const apiVersion = {
|
|
@@ -46252,7 +46229,7 @@ class ServerItemRenderer extends React__namespace.Component {
|
|
|
46252
46229
|
// TODO(alex): Check whether we rely on the format here for
|
|
46253
46230
|
// analyzing ProblemLogs. If not, remove this layer.
|
|
46254
46231
|
const maxCompatGuess = [this.questionRenderer.getUserInput(), []];
|
|
46255
|
-
const keScore =
|
|
46232
|
+
const keScore = keScoreFromPerseusScore(score, maxCompatGuess, this.questionRenderer.getSerializedState());
|
|
46256
46233
|
const emptyQuestionAreaWidgets = this.questionRenderer.emptyWidgets();
|
|
46257
46234
|
this.setState({
|
|
46258
46235
|
questionCompleted: keScore.correct,
|
|
@@ -47348,7 +47325,7 @@ class MultiRenderer extends React__namespace.Component {
|
|
|
47348
47325
|
if (ref.getSerializedState) {
|
|
47349
47326
|
state = ref.getSerializedState();
|
|
47350
47327
|
}
|
|
47351
|
-
return
|
|
47328
|
+
return keScoreFromPerseusScore(score, guess, state);
|
|
47352
47329
|
}
|
|
47353
47330
|
|
|
47354
47331
|
/**
|
|
@@ -47377,8 +47354,8 @@ class MultiRenderer extends React__namespace.Component {
|
|
|
47377
47354
|
scores.push(data.ref.score());
|
|
47378
47355
|
return data.ref?.getUserInput();
|
|
47379
47356
|
});
|
|
47380
|
-
const combinedScore = scores.reduce(
|
|
47381
|
-
return
|
|
47357
|
+
const combinedScore = scores.reduce(combineScores);
|
|
47358
|
+
return keScoreFromPerseusScore(combinedScore, guess, state);
|
|
47382
47359
|
}
|
|
47383
47360
|
|
|
47384
47361
|
/**
|
|
@@ -50235,6 +50212,9 @@ const GrapherUtil = {
|
|
|
50235
50212
|
getEquationString,
|
|
50236
50213
|
typeToButton
|
|
50237
50214
|
};
|
|
50215
|
+
const ScoringUtil = {
|
|
50216
|
+
keScoreFromPerseusScore
|
|
50217
|
+
};
|
|
50238
50218
|
|
|
50239
50219
|
exports.ApiOptions = ApiOptions;
|
|
50240
50220
|
exports.ArticleRenderer = ArticleRenderer;
|
|
@@ -50268,6 +50248,7 @@ exports.PlotterWidget = Plotter$1;
|
|
|
50268
50248
|
exports.QUESTION_WIDGETS = QUESTION_WIDGETS;
|
|
50269
50249
|
exports.Radio = Radio;
|
|
50270
50250
|
exports.Renderer = Renderer;
|
|
50251
|
+
exports.ScoringUtil = ScoringUtil;
|
|
50271
50252
|
exports.ServerItemRenderer = ref;
|
|
50272
50253
|
exports.TableWidget = Table$1;
|
|
50273
50254
|
exports.Util = Util;
|