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

Convert backgroundImage dimensions to numbers during parsing #1923

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions .changeset/smooth-taxis-mate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@khanacademy/perseus": patch
---

Internal: convert backgroundImage dimensions to numbers during parsing.
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {success} from "../result";

import {stringToNumber} from "./string-to-number";
import {ctx, parseFailureWith} from "./test-helpers";

describe("stringToNumber", () => {
it("accepts a number", () => {
expect(stringToNumber(42, ctx())).toEqual(success(42));
});

it("accepts a numeric string", () => {
expect(stringToNumber("7", ctx())).toEqual(success(7));
});

it("parses a decimal", () => {
expect(stringToNumber("5.5", ctx())).toEqual(success(5.5));
});

it("parses a negative number", () => {
expect(stringToNumber("-2", ctx())).toEqual(success(-2));
});

it("rejects the empty string", () => {
expect(stringToNumber("", ctx())).toEqual(
parseFailureWith({
badValue: "",
expected: ["a number or numeric string"],
}),
);
});

it("rejects a non-numeric string", () => {
expect(stringToNumber("3a", ctx())).toEqual(
parseFailureWith({
badValue: "3a",
expected: ["a number or numeric string"],
}),
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type {PartialParser} from "../parser-types";

export const stringToNumber: PartialParser<string | number, number> = (
Copy link
Collaborator

Choose a reason for hiding this comment

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

Sadly, there is more than one place where Perseus data contains both actual numbers and stringified numbers.

No action needed.

rawValue,
ctx,
) => {
if (typeof rawValue === "number") {
return ctx.success(rawValue);
}

const parsedNumber = +rawValue;
if (rawValue === "" || isNaN(parsedNumber)) {
return ctx.failure("a number or numeric string", rawValue);
}

return ctx.success(parsedNumber);
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,26 @@ import {
number,
object,
optional,
pipeParsers,
string,
union,
} from "../general-purpose-parsers";
import {stringToNumber} from "../general-purpose-parsers/string-to-number";

import type {Parser} from "../parser-types";
import type {PerseusImageBackground} from "@khanacademy/perseus";

const numericToNumber = pipeParsers(union(number).or(string).parser).then(
stringToNumber,
).parser;
Comment on lines +15 to +17
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't understand why we need to union number or string and then pass that result through stringToNumber. Doesn't stringToNumber handle parsing a string or number within itself already?


export const parsePerseusImageBackground: Parser<PerseusImageBackground> =
object({
url: optional(nullable(string)),
width: optional(number),
height: optional(number),
top: optional(number),
left: optional(number),
bottom: optional(number),
// TODO(benchristel): convert scale to a number
scale: optional(union(number).or(string).parser),
width: optional(numericToNumber),
height: optional(numericToNumber),
top: optional(numericToNumber),
left: optional(numericToNumber),
bottom: optional(numericToNumber),
scale: optional(numericToNumber),
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
{
"question": {
"content": "Ally is excited to compete in a $6$-mile race. The race organizers plotted the course on a coordinate map. The starting point is at $(4,3)$, and the ending point is at $(4,9)$. Ally's family decides to stand at $(4,6)$ on the map.\n\n**Plot the starting point, ending point, and place where Ally's family stands on the map.**\n\n[[☃ interactive-graph 1]]\n\n**How far along will Ally be in the race when she reaches her family?** \n[[☃ radio 1]]",
"images": {},
"widgets": {
"interactive-graph 1": {
"type": "interactive-graph",
"alignment": "default",
"static": false,
"graded": true,
"options": {
"step": [
1,
1
],
"backgroundImage": {
"url": "",
"scale": "1",
"bottom": "4",
"left": "0",
"width": 0,
"height": 0
},
"markings": "graph",
"labels": [
"x",
"y"
],
"showProtractor": false,
"showRuler": false,
"showTooltips": false,
"rulerLabel": "",
"rulerTicks": 10,
"range": [
[
-1,
10
],
[
-1,
10
]
],
"gridStep": [
1,
1
],
"snapStep": [
0.5,
0.5
],
"graph": {
"type": "point",
"numPoints": 3
},
"correct": {
"type": "point",
"coords": [
[
4,
3
],
[
4,
6
],
[
4,
9
]
],
"numPoints": 3
}
},
"version": {
"major": 0,
"minor": 0
}
},
"radio 1": {
"type": "radio",
"alignment": "default",
"static": false,
"graded": true,
"options": {
"choices": [
{
"correct": false,
"content": "Less than halfway through the race"
},
{
"correct": true,
"content": "Halfway through the race"
},
{
"content": "More than halfway through the race"
}
],
"randomize": false,
"multipleSelect": false,
"countChoices": false,
"displayCount": null,
"hasNoneOfTheAbove": false,
"deselectEnabled": false
},
"version": {
"major": 1,
"minor": 0
}
}
}
},
"answerArea": {
"calculator": false,
"chi2Table": false,
"periodicTable": false,
"tTable": false,
"zTable": false
},
"itemDataVersion": {
"major": 0,
"minor": 1
},
"hints": []
}
Loading