@khanacademy/perseus-score 0.0.0-PR3120-20251211235249 → 0.0.0-PR3120-20251217222451
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 +16 -8
- package/dist/es/index.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +16 -6
- package/dist/index.js.map +1 -1
- package/dist/score.d.ts +3 -8
- package/dist/util/flatten-scores.d.ts +4 -0
- package/dist/util/get-scoreable-widgets.d.ts +9 -0
- package/dist/util/is-widget-scoreable.d.ts +6 -0
- package/dist/util/score-is-empty.d.ts +6 -0
- package/dist/validate.d.ts +12 -2
- package/dist/widgets/deprecated-standin/score-deprecated-standin.d.ts +10 -0
- package/package.json +3 -3
- package/dist/util/score-noop.d.ts +0 -11
package/dist/index.d.ts
CHANGED
|
@@ -27,8 +27,10 @@ export { default as validateSorter } from "./widgets/sorter/validate-sorter";
|
|
|
27
27
|
export { default as scoreTable } from "./widgets/table/score-table";
|
|
28
28
|
export { default as validateTable } from "./widgets/table/validate-table";
|
|
29
29
|
export { default as scoreInputNumber, inputNumberAnswerTypes, } from "./widgets/input-number/score-input-number";
|
|
30
|
-
export { scorePerseusItem, scoreWidgetsFunctional
|
|
31
|
-
export {
|
|
30
|
+
export { scorePerseusItem, scoreWidgetsFunctional } from "./score";
|
|
31
|
+
export { default as flattenScores } from "./util/flatten-scores";
|
|
32
|
+
export { validateUserInput, emptyWidgetsFunctional } from "./validate";
|
|
32
33
|
export { default as hasEmptyDINERWidgets } from "./has-empty-diner-widgets";
|
|
34
|
+
export { default as isWidgetScoreable } from "./util/is-widget-scoreable";
|
|
33
35
|
export type { PerseusMockWidgetRubric, PerseusMockWidgetUserInput, } from "./widgets/mock-widget/mock-widget-validation.types";
|
|
34
36
|
export * from "./widgets/widget-registry";
|
package/dist/index.js
CHANGED
|
@@ -90,17 +90,21 @@ function scoreTable(userInput,rubric){const validationResult=validateTable(userI
|
|
|
90
90
|
|
|
91
91
|
const inputNumberAnswerTypes={number:{name:"Numbers",forms:"integer, decimal, proper, improper, mixed"},decimal:{name:"Decimals",forms:"decimal"},integer:{name:"Integers",forms:"integer"},rational:{name:"Fractions and mixed numbers",forms:"integer, proper, improper, mixed"},improper:{name:"Improper numbers (no mixed)",forms:"integer, proper, improper"},mixed:{name:"Mixed numbers (no improper)",forms:"integer, proper, mixed"},percent:{name:"Numbers or percents",forms:"integer, decimal, proper, improper, mixed, percent"},pi:{name:"Numbers with pi",forms:"pi"}};function scoreInputNumber(userInput,rubric,locale){if(userInput==null){return {type:"invalid",message:null}}if(rubric.answerType==null){rubric.answerType="number";}const stringValue=`${rubric.value}`;const val=KhanAnswerTypes.number.createValidatorFunctional(stringValue,{simplify:rubric.simplify,inexact:rubric.inexact||undefined,maxError:rubric.maxError,forms:inputNumberAnswerTypes[rubric.answerType].forms,...locale&&{decimal_separator:perseusCore.getDecimalSeparator(locale)}});const currentValue=parseTex(userInput.currentValue);const result=val(currentValue);if(result.empty){return {type:"invalid",message:result.message}}return {type:"points",earned:result.correct?1:0,total:1,message:result.message}}
|
|
92
92
|
|
|
93
|
-
function
|
|
93
|
+
const noScore={type:"points",earned:0,total:0,message:null};function combineScores(scoreA,scoreB){let message;if(scoreA.type==="points"&&scoreB.type==="points"){if(scoreA.message&&scoreB.message&&scoreA.message!==scoreB.message){message=null;}else {message=scoreA.message||scoreB.message;}return {type:"points",earned:scoreA.earned+scoreB.earned,total:scoreA.total+scoreB.total,message:message}}if(scoreA.type==="points"&&scoreB.type==="invalid"){return scoreB}if(scoreA.type==="invalid"&&scoreB.type==="points"){return scoreA}if(scoreA.type==="invalid"&&scoreB.type==="invalid"){if(scoreA.message&&scoreB.message&&scoreA.message!==scoreB.message){message=null;}else {message=scoreA.message||scoreB.message;}return {type:"invalid",message:message}}throw new perseusCore.PerseusError("PerseusScore with unknown type encountered",perseusCore.Errors.InvalidInput,{metadata:{scoreA:JSON.stringify(scoreA),scoreB:JSON.stringify(scoreB)}})}function flattenScores(widgetScoreMap){return Object.values(widgetScoreMap).reduce(combineScores,noScore)}
|
|
94
94
|
|
|
95
|
-
function
|
|
95
|
+
function scoreDeprecatedStandin(userInput,rubric,locale){return {type:"points",earned:1,total:1,message:null}}
|
|
96
|
+
|
|
97
|
+
function scoreFreeResponse(userInput,rubric,locale){return {type:"points",earned:0,total:0,message:null}}
|
|
96
98
|
|
|
97
99
|
function validateFreeResponse(userInput,widgetOptions){const userInputLength=userInput?.currentValue.trim().length??0;if(userInputLength===0){return {type:"invalid",message:perseusCore.ErrorCodes.USER_INPUT_EMPTY}}if(!widgetOptions.allowUnlimitedCharacters&&userInputLength>widgetOptions.characterLimit){return {type:"invalid",message:perseusCore.ErrorCodes.USER_INPUT_TOO_LONG}}return null}
|
|
98
100
|
|
|
99
101
|
function scoreGroup(userInput,rubric,locale){if(userInput==null){return {type:"invalid",message:null}}const scores=scoreWidgetsFunctional(rubric.widgets,Object.keys(rubric.widgets),userInput,locale);return flattenScores(scores)}
|
|
100
102
|
|
|
101
|
-
function
|
|
103
|
+
function scoreIsEmpty(score){return score.type==="invalid"&&(!score.message||score.message.length===0)}
|
|
104
|
+
|
|
105
|
+
function validateUserInput(perseusRenderData,userInputMap,locale){const{upgradedWidgets,scoreableWidgetIds}=getScoreableWidgets(perseusRenderData);const validationErrors={};scoreableWidgetIds.forEach(id=>{const widget=upgradedWidgets[id];const userInput=userInputMap[id];const validator=getWidgetValidator(widget.type);const validationError=validator?.(userInput,widget.options,locale);if(validationError!=null){validationErrors[id]=validationError;}});return Object.keys(validationErrors).length>0?flattenScores(validationErrors):null}function emptyWidgetsFunctional(widgets,widgetIds,userInputMap,locale){return widgetIds.filter(id=>{const widget=widgets[id];if(!isWidgetScoreable(widget)){return false}const validator=getWidgetValidator(widget.type);const userInput=userInputMap[id];const validationData=widget.options;const score=validator?.(userInput,validationData,locale);if(score){return scoreIsEmpty(score)}return false})}
|
|
102
106
|
|
|
103
|
-
function validateGroup(userInput,validationData,locale){if(userInput==null){return {type:"invalid",message:null}}const
|
|
107
|
+
function validateGroup(userInput,validationData,locale){if(userInput==null){return {type:"invalid",message:null}}const widgetIds=perseusCore.getWidgetIdsFromContent(validationData.content);const emptyWidgets=emptyWidgetsFunctional(validationData.widgets,widgetIds,userInput,locale);if(emptyWidgets.length===0){return null}return {type:"invalid",message:null}}
|
|
104
108
|
|
|
105
109
|
function validateLabelImage(userInput){if(userInput==null){return {type:"invalid",message:null}}let numAnswered=0;for(let i=0;i<userInput.markers.length;i++){const userSelection=userInput.markers[i].selected;if(userSelection&&userSelection.length>0){numAnswered++;}}if(numAnswered!==userInput.markers.length){return {type:"invalid",message:null}}return null}
|
|
106
110
|
|
|
@@ -108,9 +112,13 @@ function validateMockWidget(userInput){if(userInput?.currentValue==null||userInp
|
|
|
108
112
|
|
|
109
113
|
function scoreMockWidget(userInput,rubric){const validationResult=validateMockWidget(userInput);if(validationResult!=null){return validationResult}return {type:"points",earned:userInput?.currentValue===rubric.value?1:0,total:1,message:""}}
|
|
110
114
|
|
|
111
|
-
const widgets=new perseusCore.Registry("Score widget registry");function registerWidget(type,scorer,validator){const logic={scorer,validator};widgets.set(type,logic);}const getWidgetValidator=type=>{return widgets.get(type)?.validator??null};const getWidgetScorer=type=>{return widgets.get(type)?.scorer??null};registerWidget("categorizer",scoreCategorizer,validateCategorizer);registerWidget("cs-program",scoreCSProgram);registerWidget("dropdown",scoreDropdown,validateDropdown);registerWidget("expression",scoreExpression,validateExpression);registerWidget("free-response",scoreFreeResponse,validateFreeResponse);registerWidget("grapher",scoreGrapher);registerWidget("group",scoreGroup,validateGroup);registerWidget("iframe",scoreIframe);registerWidget("input-number",scoreInputNumber);registerWidget("interactive-graph",scoreInteractiveGraph);registerWidget("label-image",scoreLabelImage,validateLabelImage);registerWidget("matcher",scoreMatcher);registerWidget("matrix",scoreMatrix,validateMatrix);registerWidget("mock-widget",scoreMockWidget,scoreMockWidget);registerWidget("number-line",scoreNumberLine);registerWidget("numeric-input",scoreNumericInput);registerWidget("orderer",scoreOrderer,validateOrderer);registerWidget("plotter",scorePlotter,validatePlotter);registerWidget("radio",scoreRadio,validateRadio);registerWidget("sorter",scoreSorter,validateSorter);registerWidget("table",scoreTable,validateTable);
|
|
115
|
+
const widgets=new perseusCore.Registry("Score widget registry");function registerWidget(type,scorer,validator){const logic={scorer,validator};widgets.set(type,logic);}const getWidgetValidator=type=>{return widgets.get(type)?.validator??null};const getWidgetScorer=type=>{return widgets.get(type)?.scorer??null};registerWidget("categorizer",scoreCategorizer,validateCategorizer);registerWidget("cs-program",scoreCSProgram);registerWidget("deprecated-standin",scoreDeprecatedStandin);registerWidget("dropdown",scoreDropdown,validateDropdown);registerWidget("expression",scoreExpression,validateExpression);registerWidget("free-response",scoreFreeResponse,validateFreeResponse);registerWidget("grapher",scoreGrapher);registerWidget("group",scoreGroup,validateGroup);registerWidget("iframe",scoreIframe);registerWidget("input-number",scoreInputNumber);registerWidget("interactive-graph",scoreInteractiveGraph);registerWidget("label-image",scoreLabelImage,validateLabelImage);registerWidget("matcher",scoreMatcher);registerWidget("matrix",scoreMatrix,validateMatrix);registerWidget("mock-widget",scoreMockWidget,scoreMockWidget);registerWidget("number-line",scoreNumberLine);registerWidget("numeric-input",scoreNumericInput);registerWidget("orderer",scoreOrderer,validateOrderer);registerWidget("plotter",scorePlotter,validatePlotter);registerWidget("radio",scoreRadio,validateRadio);registerWidget("sorter",scoreSorter,validateSorter);registerWidget("table",scoreTable,validateTable);
|
|
116
|
+
|
|
117
|
+
function isWidgetScoreable(widget){if(!widget){return false}const widgetIsGraded=widget.graded==null||widget.graded;const widgetIsStatic=!!widget.static;const widgetHasScorer=getWidgetScorer(widget.type)!==null;return widgetIsGraded&&!widgetIsStatic&&widgetHasScorer}
|
|
118
|
+
|
|
119
|
+
function getScoreableWidgets(perseusRenderData){const usedWidgetIds=perseusCore.getWidgetIdsFromContent(perseusRenderData.content);const upgradedWidgets=perseusCore.applyDefaultsToWidgets(perseusRenderData.widgets);const scoreableWidgetIds=usedWidgetIds.filter(id=>isWidgetScoreable(upgradedWidgets[id]));return {upgradedWidgets,scoreableWidgetIds}}
|
|
112
120
|
|
|
113
|
-
|
|
121
|
+
function scorePerseusItem(perseusRenderData,userInputMap,locale){const{upgradedWidgets,scoreableWidgetIds}=getScoreableWidgets(perseusRenderData);const scores=scoreWidgetsFunctional(upgradedWidgets,scoreableWidgetIds,userInputMap,locale);return flattenScores(scores)}function scoreWidgetsFunctional(widgets,widgetIds,userInputMap,locale){const upgradedWidgets=perseusCore.applyDefaultsToWidgets(widgets);const gradedWidgetIds=widgetIds.filter(id=>isWidgetScoreable(upgradedWidgets[id]));const widgetScores={};gradedWidgetIds.forEach(id=>{const widget=upgradedWidgets[id];const userInput=userInputMap[id];const validator=getWidgetValidator(widget.type);const scorer=getWidgetScorer(widget.type);const score=validator?.(userInput,widget.options,locale)??scorer?.(userInput,widget.options,locale);if(score!=null){widgetScores[id]=score;}});return widgetScores}
|
|
114
122
|
|
|
115
123
|
function hasEmptyDINERWidgets(itemData,userInputMap){const usedWidgetIds=perseusCore.getWidgetIdsFromContent(itemData.question.content);const widgets=itemData.question.widgets;for(const widgetId of usedWidgetIds){const widget=widgets[widgetId];const input=userInputMap[widgetId];switch(widget.type){case "dropdown":{if(input.value===0){return true}break}case "interactive-graph":{break}case "numeric-input":{if(!input.currentValue&&!widget.options.coefficient){return true}break}case "expression":{if(!input){return true}break}case "radio":{if(input.selectedChoiceIds.length===0){return true}break}}}return false}
|
|
116
124
|
|
|
@@ -121,6 +129,7 @@ exports.getWidgetScorer = getWidgetScorer;
|
|
|
121
129
|
exports.getWidgetValidator = getWidgetValidator;
|
|
122
130
|
exports.hasEmptyDINERWidgets = hasEmptyDINERWidgets;
|
|
123
131
|
exports.inputNumberAnswerTypes = inputNumberAnswerTypes;
|
|
132
|
+
exports.isWidgetScoreable = isWidgetScoreable;
|
|
124
133
|
exports.registerWidget = registerWidget;
|
|
125
134
|
exports.scoreCSProgram = scoreCSProgram;
|
|
126
135
|
exports.scoreCategorizer = scoreCategorizer;
|
|
@@ -152,4 +161,5 @@ exports.validatePlotter = validatePlotter;
|
|
|
152
161
|
exports.validateRadio = validateRadio;
|
|
153
162
|
exports.validateSorter = validateSorter;
|
|
154
163
|
exports.validateTable = validateTable;
|
|
164
|
+
exports.validateUserInput = validateUserInput;
|
|
155
165
|
//# sourceMappingURL=index.js.map
|