@khanacademy/perseus-score 1.1.0 → 2.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/index.d.ts CHANGED
@@ -3,20 +3,30 @@ export type { Score } from "./util/answer-types";
3
3
  export { default as ErrorCodes } from "./error-codes";
4
4
  export type * from "./validation.types";
5
5
  export { default as scoreCategorizer } from "./widgets/categorizer/score-categorizer";
6
+ export { default as validateCategorizer } from "./widgets/categorizer/validate-categorizer";
6
7
  export { default as scoreCSProgram } from "./widgets/cs-program/score-cs-program";
7
8
  export { default as scoreDropdown } from "./widgets/dropdown/score-dropdown";
9
+ export { default as validateDropdown } from "./widgets/dropdown/validate-dropdown";
8
10
  export { default as scoreExpression } from "./widgets/expression/score-expression";
11
+ export { default as validateExpression } from "./widgets/expression/validate-expression";
9
12
  export { default as scoreGrapher } from "./widgets/grapher/score-grapher";
10
13
  export { default as scoreIframe } from "./widgets/iframe/score-iframe";
11
14
  export { default as scoreInteractiveGraph } from "./widgets/interactive-graph/score-interactive-graph";
12
15
  export { default as scoreLabelImage, scoreLabelImageMarker, } from "./widgets/label-image/score-label-image";
13
16
  export { default as scoreMatcher } from "./widgets/matcher/score-matcher";
14
17
  export { default as scoreMatrix } from "./widgets/matrix/score-matrix";
18
+ export { default as validateMatrix } from "./widgets/matrix/validate-matrix";
15
19
  export { default as scoreNumberLine } from "./widgets/number-line/score-number-line";
20
+ export { default as validateNumberLine } from "./widgets/number-line/validate-number-line";
16
21
  export { default as scoreNumericInput } from "./widgets/numeric-input/score-numeric-input";
17
22
  export { default as scoreOrderer } from "./widgets/orderer/score-orderer";
23
+ export { default as validateOrderer } from "./widgets/orderer/validate-orderer";
18
24
  export { default as scorePlotter } from "./widgets/plotter/score-plotter";
25
+ export { default as validatePlotter } from "./widgets/plotter/validate-plotter";
19
26
  export { default as scoreRadio } from "./widgets/radio/score-radio";
27
+ export { default as validateRadio } from "./widgets/radio/validate-radio";
20
28
  export { default as scoreSorter } from "./widgets/sorter/score-sorter";
29
+ export { default as validateSorter } from "./widgets/sorter/validate-sorter";
21
30
  export { default as scoreTable } from "./widgets/table/score-table";
31
+ export { default as validateTable } from "./widgets/table/validate-table";
22
32
  export { default as scoreInputNumber, inputNumberAnswerTypes, } from "./widgets/input-number/score-input-number";
package/dist/index.js CHANGED
@@ -12570,13 +12570,13 @@ function validateCategorizer(userInput, validationData) {
12570
12570
  return null;
12571
12571
  }
12572
12572
 
12573
- function scoreCategorizer(userInput, scoringData) {
12574
- const validationError = validateCategorizer(userInput, scoringData);
12573
+ function scoreCategorizer(userInput, rubric) {
12574
+ const validationError = validateCategorizer(userInput, rubric);
12575
12575
  if (validationError) {
12576
12576
  return validationError;
12577
12577
  }
12578
12578
  let allCorrect = true;
12579
- scoringData.values.forEach((value, i) => {
12579
+ rubric.values.forEach((value, i) => {
12580
12580
  if (userInput.values[i] !== value) {
12581
12581
  allCorrect = false;
12582
12582
  }
@@ -12589,24 +12589,23 @@ function scoreCategorizer(userInput, scoringData) {
12589
12589
  };
12590
12590
  }
12591
12591
 
12592
- // TODO: merge this with scoreIframe, it's the same code
12593
- function scoreCSProgram(state) {
12592
+ function scoreCSProgram(userInput) {
12594
12593
  // The CS program can tell us whether it's correct or incorrect,
12595
12594
  // and pass an optional message
12596
- if (state.status === "correct") {
12595
+ if (userInput.status === "correct") {
12597
12596
  return {
12598
12597
  type: "points",
12599
12598
  earned: 1,
12600
12599
  total: 1,
12601
- message: state.message || null
12600
+ message: userInput.message || null
12602
12601
  };
12603
12602
  }
12604
- if (state.status === "incorrect") {
12603
+ if (userInput.status === "incorrect") {
12605
12604
  return {
12606
12605
  type: "points",
12607
12606
  earned: 0,
12608
12607
  total: 1,
12609
- message: state.message || null
12608
+ message: userInput.message || null
12610
12609
  };
12611
12610
  }
12612
12611
  return {
@@ -12679,9 +12678,7 @@ function validateExpression(userInput) {
12679
12678
  * show the user an error. TODO(joel) - what error?
12680
12679
  * - Otherwise, pass through the resulting points and message.
12681
12680
  */
12682
- function scoreExpression(userInput, rubric,
12683
- // TODO: remove strings as a param for scorers
12684
- strings, locale) {
12681
+ function scoreExpression(userInput, rubric, locale) {
12685
12682
  const validationError = validateExpression(userInput);
12686
12683
  if (validationError) {
12687
12684
  return validationError;
@@ -12701,7 +12698,11 @@ strings, locale) {
12701
12698
  // in the function variables list for the expression.
12702
12699
  if (!expression.parsed) {
12703
12700
  /* c8 ignore next */
12704
- throw new perseusCore.PerseusError("Unable to parse solution answer for expression", perseusCore.Errors.InvalidInput);
12701
+ throw new perseusCore.PerseusError("Unable to parse solution answer for expression", perseusCore.Errors.InvalidInput, {
12702
+ metadata: {
12703
+ rubric: JSON.stringify(rubric)
12704
+ }
12705
+ });
12705
12706
  }
12706
12707
  return KhanAnswerTypes.expression.createValidatorFunctional(expression.expr, _({}).extend(options, {
12707
12708
  simplify: answer.simplify,
@@ -13095,49 +13096,57 @@ function scoreInteractiveGraph(userInput, rubric) {
13095
13096
  };
13096
13097
  }
13097
13098
 
13099
+ function validateLabelImage(userInput) {
13100
+ let numAnswered = 0;
13101
+ for (let i = 0; i < userInput.markers.length; i++) {
13102
+ const userSelection = userInput.markers[i].selected;
13103
+ if (userSelection && userSelection.length > 0) {
13104
+ numAnswered++;
13105
+ }
13106
+ }
13107
+ // We expect all question markers to be answered before grading.
13108
+ if (numAnswered !== userInput.markers.length) {
13109
+ return {
13110
+ type: "invalid",
13111
+ message: null
13112
+ };
13113
+ }
13114
+ return null;
13115
+ }
13116
+
13098
13117
  // Question state for marker as result of user selected answers.
13099
13118
 
13100
- function scoreLabelImageMarker(marker) {
13119
+ function scoreLabelImageMarker(userInput, rubric) {
13101
13120
  const score = {
13102
13121
  hasAnswers: false,
13103
13122
  isCorrect: false
13104
13123
  };
13105
- if (marker.selected && marker.selected.length > 0) {
13124
+ if (userInput && userInput.length > 0) {
13106
13125
  score.hasAnswers = true;
13107
13126
  }
13108
- if (marker.answers.length > 0) {
13109
- if (marker.selected && marker.selected.length === marker.answers.length) {
13127
+ if (rubric.length > 0) {
13128
+ if (userInput && userInput.length === rubric.length) {
13110
13129
  // All correct answers are selected by the user.
13111
- score.isCorrect = marker.selected.every(choice => marker.answers.includes(choice));
13130
+ score.isCorrect = userInput.every(choice => rubric.includes(choice));
13112
13131
  }
13113
- } else if (!marker.selected || marker.selected.length === 0) {
13132
+ } else if (!userInput || userInput.length === 0) {
13114
13133
  // Correct as no answers should be selected by the user.
13115
13134
  score.isCorrect = true;
13116
13135
  }
13117
13136
  return score;
13118
13137
  }
13119
-
13120
- // TODO(LEMS-2440): May need to pull answers out of PerseusLabelImageWidgetOptions[markers] for the rubric
13121
13138
  function scoreLabelImage(userInput, rubric) {
13122
- let numAnswered = 0;
13139
+ const validationError = validateLabelImage(userInput);
13140
+ if (validationError) {
13141
+ return validationError;
13142
+ }
13123
13143
  let numCorrect = 0;
13124
- for (const marker of userInput.markers) {
13125
- const score = scoreLabelImageMarker(marker);
13126
- if (score.hasAnswers) {
13127
- numAnswered++;
13128
- }
13144
+ for (let i = 0; i < userInput.markers.length; i++) {
13145
+ const score = scoreLabelImageMarker(userInput.markers[i].selected, rubric.markers[i].answers);
13129
13146
  if (score.isCorrect) {
13130
13147
  numCorrect++;
13131
13148
  }
13132
13149
  }
13133
-
13134
- // We expect all question markers to be answered before grading.
13135
- if (numAnswered !== userInput.markers.length) {
13136
- return {
13137
- type: "invalid",
13138
- message: null
13139
- };
13140
- }
13141
13150
  return {
13142
13151
  type: "points",
13143
13152
  // Markers with no expected answers are graded as correct if user
@@ -13148,8 +13157,8 @@ function scoreLabelImage(userInput, rubric) {
13148
13157
  };
13149
13158
  }
13150
13159
 
13151
- function scoreMatcher(state, rubric) {
13152
- const correct = _.isEqual(state.left, rubric.left) && _.isEqual(state.right, rubric.right);
13160
+ function scoreMatcher(userInput, rubric) {
13161
+ const correct = _.isEqual(userInput.left, rubric.left) && _.isEqual(userInput.right, rubric.right);
13153
13162
  return {
13154
13163
  type: "points",
13155
13164
  earned: correct ? 1 : 0,
@@ -13166,7 +13175,7 @@ function scoreMatcher(state, rubric) {
13166
13175
  *
13167
13176
  * @see `scoreMatrix()` for more details.
13168
13177
  */
13169
- function validateMatrix(userInput, validationData) {
13178
+ function validateMatrix(userInput) {
13170
13179
  const supplied = userInput.answers;
13171
13180
  const suppliedSize = perseusCore.getMatrixSize(supplied);
13172
13181
  for (let row = 0; row < suppliedSize[0]; row++) {
@@ -13183,9 +13192,9 @@ function validateMatrix(userInput, validationData) {
13183
13192
  }
13184
13193
 
13185
13194
  function scoreMatrix(userInput, rubric) {
13186
- const validationResult = validateMatrix(userInput);
13187
- if (validationResult != null) {
13188
- return validationResult;
13195
+ const validationError = validateMatrix(userInput);
13196
+ if (validationError != null) {
13197
+ return validationError;
13189
13198
  }
13190
13199
  const solution = rubric.answers;
13191
13200
  const supplied = userInput.answers;
@@ -13250,16 +13259,16 @@ function validateNumberLine(userInput) {
13250
13259
  return null;
13251
13260
  }
13252
13261
 
13253
- function scoreNumberLine(userInput, scoringData) {
13262
+ function scoreNumberLine(userInput, rubric) {
13254
13263
  const validationError = validateNumberLine(userInput);
13255
13264
  if (validationError) {
13256
13265
  return validationError;
13257
13266
  }
13258
- const range = scoringData.range;
13259
- const start = scoringData.initialX != null ? scoringData.initialX : range[0];
13260
- const startRel = scoringData.isInequality ? "ge" : "eq";
13261
- const correctRel = scoringData.correctRel || "eq";
13262
- const correctPos = kmath.number.equal(userInput.numLinePosition, scoringData.correctX || 0);
13267
+ const range = rubric.range;
13268
+ const start = rubric.initialX != null ? rubric.initialX : range[0];
13269
+ const startRel = rubric.isInequality ? "ge" : "eq";
13270
+ const correctRel = rubric.correctRel || "eq";
13271
+ const correctPos = kmath.number.equal(userInput.numLinePosition, rubric.correctX || 0);
13263
13272
  if (correctPos && correctRel === userInput.rel) {
13264
13273
  return {
13265
13274
  type: "points",
@@ -13562,9 +13571,9 @@ function validateOrderer(userInput) {
13562
13571
  }
13563
13572
 
13564
13573
  function scoreOrderer(userInput, rubric) {
13565
- const validateError = validateOrderer(userInput);
13566
- if (validateError) {
13567
- return validateError;
13574
+ const validationError = validateOrderer(userInput);
13575
+ if (validationError) {
13576
+ return validationError;
13568
13577
  }
13569
13578
  const correct = _.isEqual(userInput.current, rubric.correctOptions.map(option => option.content));
13570
13579
  return {
@@ -13591,14 +13600,14 @@ function validatePlotter(userInput, validationData) {
13591
13600
  return null;
13592
13601
  }
13593
13602
 
13594
- function scorePlotter(userInput, scoringData) {
13595
- const validationError = validatePlotter(userInput, scoringData);
13603
+ function scorePlotter(userInput, rubric) {
13604
+ const validationError = validatePlotter(userInput, rubric);
13596
13605
  if (validationError) {
13597
13606
  return validationError;
13598
13607
  }
13599
13608
  return {
13600
13609
  type: "points",
13601
- earned: perseusCore.approximateDeepEqual(userInput, scoringData.correct) ? 1 : 0,
13610
+ earned: perseusCore.approximateDeepEqual(userInput, rubric.correct) ? 1 : 0,
13602
13611
  total: 1,
13603
13612
  message: null
13604
13613
  };
@@ -13872,4 +13881,14 @@ exports.scorePlotter = scorePlotter;
13872
13881
  exports.scoreRadio = scoreRadio;
13873
13882
  exports.scoreSorter = scoreSorter;
13874
13883
  exports.scoreTable = scoreTable;
13884
+ exports.validateCategorizer = validateCategorizer;
13885
+ exports.validateDropdown = validateDropdown;
13886
+ exports.validateExpression = validateExpression;
13887
+ exports.validateMatrix = validateMatrix;
13888
+ exports.validateNumberLine = validateNumberLine;
13889
+ exports.validateOrderer = validateOrderer;
13890
+ exports.validatePlotter = validatePlotter;
13891
+ exports.validateRadio = validateRadio;
13892
+ exports.validateSorter = validateSorter;
13893
+ exports.validateTable = validateTable;
13875
13894
  //# sourceMappingURL=index.js.map