Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Init perseus-score and move answer-types and perseus-types #2086

Merged
merged 10 commits into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/twelve-timers-pay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"@khanacademy/perseus": major
"@khanacademy/perseus-score": major
"@khanacademy/kmath": minor
"@khanacademy/perseus-core": minor
"@khanacademy/perseus-dev-ui": patch
"@khanacademy/perseus-editor": patch
---

Init perseus-score, move AnswerTypes from perseus to perseus-score, move perseus-types in perseus to data-schema in perseus-core
2 changes: 1 addition & 1 deletion dev/flipbook.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import type {
PerseusScore,
PerseusWidget,
} from "../packages/perseus/src";
import type {InteractiveGraphWidget} from "../packages/perseus/src/perseus-types";
import type {InteractiveGraphWidget} from "@khanacademy/perseus-core";
import type {PropsFor} from "@khanacademy/wonder-blocks-core";

import "../packages/perseus/src/styles/perseus-renderer.less";
Expand Down
2 changes: 2 additions & 0 deletions packages/kmath/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ export * as vector from "./vector";
export * as point from "./point";
export * as line from "./line";
export * as ray from "./ray";

export {default as KhanMath, sum} from "./math";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exporting sum here introduces a duplicate function now in that vector.ts has an arraySum() that is exactly the same thing.

Maybe we can de-duplicate in a follow-up PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added to my "post-move" cleanup ticket: https://khanacademy.atlassian.net/browse/LEMS-2776

File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import {number as knumber} from "@khanacademy/kmath";
import $ from "jquery";
import _ from "underscore";

import type {MathFormat} from "../perseus-types";
import {number as knumber} from "@khanacademy/kmath";

import type {MathFormat} from "@khanacademy/perseus-core";

const KhanMath = {
// Simplify formulas before display
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a future PR, I think a small cleanup that'd fit these Khanmath functions into this library would be to move the functions to number.ts or vector.ts (as appropriate). It's a nitpicky thing, but it would make this library more consumable instead of having a grab-bag of functions left in KhanMath.

Probably low priority, but if you feel like it... :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added to my "post-move" cleanup ticket: https://khanacademy.atlassian.net/browse/LEMS-2776

Expand Down
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for renaming this on the move.

File renamed without changes.
2 changes: 2 additions & 0 deletions packages/perseus-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ export {libVersion} from "./version";

export {Errors} from "./error/errors";
export {PerseusError} from "./error/perseus-error";

export * from "./data-schema";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this should be export * as DataSchema? It would just be very painful to do without figuring out a way of automating the change because it wouldn't be a simple find/replace.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea. I think it's something we could tackle as a follow-up.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added to my "post-move" cleanup ticket: https://khanacademy.atlassian.net/browse/LEMS-2776

2 changes: 1 addition & 1 deletion packages/perseus-editor/src/components/graph-settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
/**
* Used in the editors for the Grapher and Interaction widgets.
*/
import {KhanMath} from "@khanacademy/kmath";
import {
components,
interactiveSizes,
Changeable,
Dependencies,
KhanMath,
Util,
} from "@khanacademy/perseus";
import {Checkbox} from "@khanacademy/wonder-blocks-form";
Expand Down
8 changes: 8 additions & 0 deletions packages/perseus-score/.babelrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**

Check warning on line 1 in packages/perseus-score/.babelrc.js

View workflow job for this annotation

GitHub Actions / Lint, Typecheck, Format, and Test (ubuntu-latest, 20.x)

File ignored by default.

Check warning on line 1 in packages/perseus-score/.babelrc.js

View workflow job for this annotation

GitHub Actions / Lint, Typecheck, Format, and Test (ubuntu-latest, 20.x)

File ignored by default.
* HACK(somewhatabstract): Due to https://github.com/facebook/jest/issues/11741,
* we need to have this file, or updating inline snapshots can fail rather
* cryptically.
*
* We should remove this when jest is fixed.
*/
module.exports = require("../../config/build/babel.config");
15 changes: 15 additions & 0 deletions packages/perseus-score/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/* eslint-disable @typescript-eslint/no-require-imports */
/* eslint-disable import/no-commonjs */
const path = require("path");

module.exports = {
rules: {
"import/no-extraneous-dependencies": [
"error",
{
packageDir: [__dirname, path.join(__dirname, "../../")],
includeTypes: true,
},
],
},
};
3 changes: 3 additions & 0 deletions packages/perseus-score/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# @khanacademy/perseus-score

Logic for scoring Perseus exercises.
33 changes: 33 additions & 0 deletions packages/perseus-score/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "@khanacademy/perseus-score",
"description": "Perseus score",
"author": "Khan Academy",
"license": "MIT",
"version": "0.0.0",
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "https://github.com/Khan/perseus.git",
"directory": "packages/perseus-score"
},
"bugs": {
"url": "https://github.com/Khan/perseus/issues"
},
"module": "dist/es/index.js",
"main": "dist/index.js",
"source": "src/index.ts",
"files": [
"dist"
],
"scripts": {},
"dependencies": {
"@khanacademy/kas": "^0.4.9",
"@khanacademy/kmath": "^0.1.24",
"@khanacademy/perseus-core": "3.0.5"
},
"devDependencies": {},
"peerDependencies": {},
"keywords": []
}
13 changes: 13 additions & 0 deletions packages/perseus-score/src/error-identifiers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
function mark(error: string) {
return "\u2603 " + error + " \u2603";
Copy link
Contributor Author

@handeyeco handeyeco Jan 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this is dumb, but I wanted to make it clear that these were not meant to be learner-facing. They're essentially enums. I thought about using numeric error codes, but I thought it would be clearer to use an enum-like system.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. One suggestion is to avoid using the snowman sigil. That has extremely special meaning in Perseus and I'd like to avoid folks seeing these values and getting confused, thinking they're widget references.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided to remove the symbol altogether. It was just needless functionality and my attempt at being cute.

}

export const MISSING_PERCENT_ERROR = mark("MISSING_PERCENT_ERROR");
export const NEEDS_TO_BE_SIMPLIFIED_ERROR = mark(
"NEEDS_TO_BE_SIMPLIFIED_ERROR",
);
export const APPROXIMATED_PI_ERROR = mark("APPROXIMATED_PI_ERROR");
export const EXTRA_SYMBOLS_ERROR = mark("EXTRA_SYMBOLS_ERROR");
export const WRONG_CASE_ERROR = mark("WRONG_CASE_ERROR");
export const WRONG_LETTER_ERROR = mark("WRONG_LETTER_ERROR");
export const MULTIPLICATION_SIGN_ERROR = mark("MULTIPLICATION_SIGN_ERROR");
11 changes: 11 additions & 0 deletions packages/perseus-score/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export {default as KhanAnswerTypes} from "./util/answer-types";
export type {Score} from "./util/answer-types";
export {
APPROXIMATED_PI_ERROR,
EXTRA_SYMBOLS_ERROR,
MISSING_PERCENT_ERROR,
NEEDS_TO_BE_SIMPLIFIED_ERROR,
WRONG_CASE_ERROR,
WRONG_LETTER_ERROR,
MULTIPLICATION_SIGN_ERROR,
} from "./error-identifiers";
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import {mockStrings} from "../strings";

import khanAnswerTypes from "./answer-types";

const validateFraction = (correctAnswer: string, guess: string) => {
const validator = khanAnswerTypes.number.createValidatorFunctional(
correctAnswer,
{simplified: true},
mockStrings,
);
return validator(guess);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
/* eslint-disable no-useless-escape */
import * as KAS from "@khanacademy/kas";
import {KhanMath} from "@khanacademy/kmath";
import {Errors, PerseusError} from "@khanacademy/perseus-core";
import $ from "jquery";
import _ from "underscore";

import KhanMath from "./math";

import type {PerseusStrings} from "../strings";
import {
APPROXIMATED_PI_ERROR,
EXTRA_SYMBOLS_ERROR,
MISSING_PERCENT_ERROR,
NEEDS_TO_BE_SIMPLIFIED_ERROR,
WRONG_CASE_ERROR,
WRONG_LETTER_ERROR,
MULTIPLICATION_SIGN_ERROR,
} from "../error-identifiers";

const MAXERROR_EPSILON = Math.pow(2, -42);

Expand Down Expand Up @@ -100,7 +107,6 @@ const KhanAnswerTypes = {
createValidatorFunctional: function (
predicate: Predicate,
options: any,
strings: PerseusStrings,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it's now using error placeholders instead of translated strings, we don't need to weave through the strings object.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! I was thinking about how this will work out on the frontend and I think your choice of string codes over numeric will be useful. If we ever have a code that's not represented on the frontend, we'll at least have a code that reveals something about what happened on the server.

): (arg1: Guess) => Score {
// Extract the options from the given solution object
options = _.extend(
Expand Down Expand Up @@ -571,13 +577,12 @@ const KhanAnswerTypes = {
} else if (form === "percent") {
// Otherwise, an error was returned
score.empty = true;
score.message = strings.MISSING_PERCENT_ERROR;
score.message = MISSING_PERCENT_ERROR;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So these are now the "error placeholder" vs the error message itself. There's a helper to map placeholders to translated strings.

} else {
if (options.simplify !== "enforced") {
score.empty = true;
}
score.message =
strings.NEEDS_TO_BE_SIMPLFIED_ERROR;
score.message = NEEDS_TO_BE_SIMPLIFIED_ERROR;
}
// The return false below stops the looping of the
// callback since predicate check succeeded.
Expand All @@ -586,7 +591,7 @@ const KhanAnswerTypes = {
}
if (piApprox && predicate(val, Math.abs(val * 0.001))) {
score.empty = true;
score.message = strings.APPROXIMATED_PI_ERROR;
score.message = APPROXIMATED_PI_ERROR;
}
}
});
Expand All @@ -604,7 +609,7 @@ const KhanAnswerTypes = {
});
if (!interpretedGuess) {
score.empty = true;
score.message = strings.EXTRA_SYMBOLS_ERROR;
score.message = EXTRA_SYMBOLS_ERROR;
return score;
}
}
Expand Down Expand Up @@ -637,14 +642,12 @@ const KhanAnswerTypes = {
createValidatorFunctional: function (
correctAnswer: string,
options: any,
strings: PerseusStrings,
): (arg1: Guess) => Score {
return KhanAnswerTypes.predicate.createValidatorFunctional(
...KhanAnswerTypes.number.convertToPredicate(
correctAnswer,
options,
),
strings,
);
},
},
Expand Down Expand Up @@ -725,7 +728,6 @@ const KhanAnswerTypes = {
createValidatorFunctional: function (
solution: any,
options: any,
strings: PerseusStrings,
): (arg1: Guess) => Score {
return function (guess: Guess): Score {
const score = {
Expand Down Expand Up @@ -787,8 +789,8 @@ const KhanAnswerTypes = {
score.ungraded = true;
// @ts-expect-error - TS2540 - Cannot assign to 'message' because it is a read-only property.
score.message = result.wrongVariableCase
? strings.WRONG_CASE_ERROR
: strings.WRONG_LETTER_ERROR;
? WRONG_CASE_ERROR
: WRONG_LETTER_ERROR;
// Don't tell the use they're "almost there" in this case, that may not be true and isn't helpful.
// @ts-expect-error - TS2339 - Property 'suppressAlmostThere' does not exist on type '{ readonly empty: false; readonly correct: false; readonly message: string | null | undefined; readonly guess: any; readonly ungraded: false; }'.
score.suppressAlmostThere = true;
Expand Down Expand Up @@ -821,7 +823,7 @@ const KhanAnswerTypes = {
// @ts-expect-error - TS2540 - Cannot assign to 'ungraded' because it is a read-only property.
score.ungraded = true;
// @ts-expect-error - TS2540 - Cannot assign to 'message' because it is a read-only property.
score.message = strings.MULTIPLICATION_SIGN_ERROR;
score.message = MULTIPLICATION_SIGN_ERROR;
} else if (resultX.message) {
// TODO(aasmund): I18nize `score.message`
// @ts-expect-error - TS2540 - Cannot assign to 'message' because it is a read-only property.
Expand Down
19 changes: 19 additions & 0 deletions packages/perseus-score/tsconfig-build.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"extends": "../tsconfig-shared.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "src",
"paths": {
// NOTE(kevinb): We have to repeat this here because TS doesn't do
// intelligent merge of tsconfig.json files when using `extends`.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just copy-pasta, I don't know what I'm doing.

"@khanacademy/*": [
"../*/src"
]
}
},
"references": [
{"path": "../kas/tsconfig-build.json"},
{"path": "../kmath/tsconfig-build.json"},
{"path": "../perseus-core/tsconfig-build.json"},
]
}
1 change: 1 addition & 0 deletions packages/perseus-score/types
1 change: 1 addition & 0 deletions packages/perseus/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"@khanacademy/math-input": "^22.1.0",
"@khanacademy/perseus-core": "3.0.5",
"@khanacademy/perseus-linter": "^1.2.11",
"@khanacademy/perseus-score": "^0.0.0",
"@khanacademy/pure-markdown": "^0.3.20",
"@khanacademy/simple-markdown": "^0.13.13",
"@types/classnames": "2.2.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type {PerseusRenderer} from "../perseus-types";
import type {PerseusRenderer} from "@khanacademy/perseus-core";

export const singleSectionArticle: PerseusRenderer = {
content:
Expand Down
2 changes: 1 addition & 1 deletion packages/perseus/src/__testdata__/graphie.testdata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
ItemExtras,
type PerseusAnswerArea,
type PerseusItem,
} from "../perseus-types";
} from "@khanacademy/perseus-core";

export const itemWithPieChart: PerseusItem = {
answerArea: Object.fromEntries(
Expand Down
4 changes: 2 additions & 2 deletions packages/perseus/src/__testdata__/renderer.testdata.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type {RenderProps} from "../widgets/radio";
import type {
DropdownWidget,
ImageWidget,
InputNumberWidget,
PerseusRenderer,
} from "../perseus-types";
import type {RenderProps} from "../widgets/radio";
} from "@khanacademy/perseus-core";

export const dropdownWidget: DropdownWidget = {
type: "dropdown",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
type ExpressionWidget,
type RadioWidget,
type NumericInputWidget,
} from "../perseus-types";
} from "@khanacademy/perseus-core";

export const itemWithInput: PerseusItem = {
question: {
Expand Down
2 changes: 1 addition & 1 deletion packages/perseus/src/__tests__/article-renderer.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import ArticleRenderer from "../article-renderer";
import * as Dependencies from "../dependencies";
import {ApiOptions} from "../perseus-api";

import type {PerseusRenderer} from "../perseus-types";
import type {APIOptions} from "../types";
import type {PerseusRenderer} from "@khanacademy/perseus-core";

function KeypadWithContext() {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ beforeEach(() => {
stub.mockClear();
});

import type {RadioWidget, PerseusWidgetsMap} from "../perseus-types";
import type {RadioWidget, PerseusWidgetsMap} from "@khanacademy/perseus-core";

describe("ExtractPerseusData", () => {
describe("getAnswersFromWidgets", () => {
Expand Down
4 changes: 2 additions & 2 deletions packages/perseus/src/__tests__/mock-asset-loading-widget.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {ItemExtras} from "@khanacademy/perseus-core";
import * as React from "react";

import AssetContext from "../asset-context";
import {ItemExtras} from "../perseus-types";

import type {PerseusAnswerArea, PerseusItem} from "../perseus-types";
import type {WidgetExports} from "../types";
import type {PerseusAnswerArea, PerseusItem} from "@khanacademy/perseus-core";

export const mockedAssetItem: PerseusItem = {
question: {
Expand Down
2 changes: 1 addition & 1 deletion packages/perseus/src/__tests__/renderer.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ import {simpleGroupQuestion} from "../widgets/group/group.testdata";
import InputNumberExport from "../widgets/input-number";
import RadioWidgetExport from "../widgets/radio";

import type {PerseusRenderer, DropdownWidget} from "../perseus-types";
import type {APIOptions} from "../types";
import type {PerseusRenderer, DropdownWidget} from "@khanacademy/perseus-core";
import type {UserEvent} from "@testing-library/user-event";

// NOTE(jeremy): We can't use an automatic mock for the translation linter,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ import MockAssetLoadingWidgetExport, {
} from "./mock-asset-loading-widget";

import type {MockAssetLoadingWidget} from "./mock-asset-loading-widget";
import type {PerseusItem} from "../perseus-types";
import type {APIOptions} from "../types";
import type {KeypadAPI} from "@khanacademy/math-input";
import type {PerseusItem} from "@khanacademy/perseus-core";
import type {PropsFor} from "@khanacademy/wonder-blocks-core";
import type {UserEvent} from "@testing-library/user-event";

Expand Down
2 changes: 1 addition & 1 deletion packages/perseus/src/__tests__/test-items/image-item.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type {PerseusRenderer} from "../../perseus-types";
import type {PerseusRenderer} from "@khanacademy/perseus-core";

export default {
question: {
Expand Down
Loading
Loading