Skip to content

Commit

Permalink
add properties to schema (#48)
Browse files Browse the repository at this point in the history
  • Loading branch information
dqnykamp authored Nov 21, 2023
1 parent 44d7810 commit ab681c8
Show file tree
Hide file tree
Showing 38 changed files with 42,545 additions and 10,387 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

133 changes: 91 additions & 42 deletions packages/doenetml-worker/src/Core.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ import {
verifyReplacementsMatchSpecifiedType,
} from "./utils/copy";
import { DependencyHandler } from "./Dependencies";
import { returnDefaultGetArrayKeysFromVarName } from "./utils/stateVariables";
import {
returnDefaultArrayVarNameFromPropIndex,
returnDefaultGetArrayKeysFromVarName,
} from "./utils/stateVariables";
import { nanoid } from "nanoid";
import { get as idb_get, set as idb_set } from "idb-keyval";
import axios from "axios";
Expand Down Expand Up @@ -3647,10 +3650,9 @@ export default class Core {

// add state variable definitions from component class
let newDefinitions =
componentClass.returnNormalizedStateVariableDefinitions({
attributeNames: Object.keys(stateVariableDefinitions),
numerics: this.numerics,
});
componentClass.returnNormalizedStateVariableDefinitions(
this.numerics,
);

Object.assign(stateVariableDefinitions, newDefinitions);

Expand Down Expand Up @@ -3688,7 +3690,7 @@ export default class Core {
let varName = attributeSpecification.createStateVariable;

let stateVarDef = (stateVariableDefinitions[varName] = {
isAttribute: true,
isAttribute: true, // Note: isAttribute is not accessed anywhere
hasEssential: true,
});

Expand Down Expand Up @@ -3957,7 +3959,7 @@ export default class Core {
let varName = attributeSpecification.createStateVariable;

let stateVarDef = (stateVariableDefinitions[varName] = {
isAttribute: true,
isAttribute: true, // Note: isAttribute is not accessed anywhere
hasEssential: true,
});

Expand Down Expand Up @@ -4185,7 +4187,7 @@ export default class Core {
}

let stateVarDef = (stateVariableDefinitions[varName] = {
isAttribute: true,
isAttribute: true, // Note: isAttribute is not accessed anywhere
hasEssential: true,
});

Expand Down Expand Up @@ -5261,6 +5263,7 @@ export default class Core {
if (arrayStateVarObj.shadowingInstructions) {
stateVarObj.shadowingInstructions = {};

// See description of returnWrappingComponents in initializeArrayStateVariable, below.
stateVarObj.wrappingComponents =
arrayStateVarObj.shadowingInstructions.returnWrappingComponents(
arrayEntryPrefix,
Expand Down Expand Up @@ -5500,6 +5503,19 @@ export default class Core {
stateVarObj.numDimensions = 1;
}

let entryPrefixes = stateVarObj.entryPrefixes;

if (!entryPrefixes) {
entryPrefixes = stateVarObj.entryPrefixes = [stateVariable];
}

if (!component.arrayEntryPrefixes) {
component.arrayEntryPrefixes = {};
}
for (let prefix of entryPrefixes) {
component.arrayEntryPrefixes[prefix] = stateVariable;
}

if (stateVarObj.numDimensions > 1) {
// for multiple dimensions, have to convert from arrayKey
// to multi-index when getting or setting
Expand Down Expand Up @@ -5711,21 +5727,23 @@ export default class Core {
};
}

// arrayVarNameFromPropIndex is a function that calculates the name
// an array entry state variable that corresponds to the specified propIndex.
// It is a consequence of retrofitting the ability to index an array (e.g., $a.b[1])
// onto a system that was designed with just array entry variables (e..g, $a.b1).
// arrayVarNameFromPropIndex can be specified in the definition of the array state variable.
// Since numDimensions > 1 here, the default arrayVarNameFromPropIndex
// is to turn $a.b[1][2][3] to $a.p1_2_3,
// where "p" is the first entry prefix of the array "b".

// TODO: if we redesign arrays to be based on indices (or even slices),
// then arrayVarNameFromPropIndex will be obsolete.
if (!stateVarObj.arrayVarNameFromPropIndex) {
stateVarObj.arrayVarNameFromPropIndex = function (
propIndex,
varName,
) {
return (
entryPrefixes[0] +
[
...propIndex.map((x) => Math.round(Number(x))),
...Array(
stateVarObj.numDimensions - propIndex.length,
).fill(1),
].join("_")
stateVarObj.arrayVarNameFromPropIndex =
returnDefaultArrayVarNameFromPropIndex(
stateVarObj.numDimensions,
entryPrefixes[0],
);
};
}

stateVarObj.adjustArrayToNewArraySize = async function () {
Expand Down Expand Up @@ -5809,13 +5827,20 @@ export default class Core {
};
}

// arrayVarNameFromPropIndex is a function that calculates the name
// an array entry state variable that corresponds to the specified propIndex.
// It is a consequence of retrofitting the ability to index an array (e.g., $a.b[1])
// onto a system that was designed with just array entry variables (e..g, $a.b1).
// arrayVarNameFromPropIndex can be specified in the definition of the array state variable.
// Since numDimensions = 1 here, the default arrayVarNameFromPropIndex
// is to turn $a.b[1] to $a.p1,
// where "p" is the first entry prefix of the array "b".

// TODO: if we redesign arrays to be based on indices (or even slices),
// then arrayVarNameFromPropIndex will be obsolete.
if (!stateVarObj.arrayVarNameFromPropIndex) {
stateVarObj.arrayVarNameFromPropIndex = function (
propIndex,
varName,
) {
return entryPrefixes[0] + propIndex[0];
};
stateVarObj.arrayVarNameFromPropIndex =
returnDefaultArrayVarNameFromPropIndex(1, entryPrefixes[0]);
}

stateVarObj.adjustArrayToNewArraySize = async function () {
Expand All @@ -5834,25 +5859,42 @@ export default class Core {
// dimensions, as we just want the string representation
stateVarObj.indexToKey = (index) => String(index);

let entryPrefixes = stateVarObj.entryPrefixes;

if (!entryPrefixes) {
entryPrefixes = stateVarObj.entryPrefixes = [stateVariable];
}

if (!component.arrayEntryPrefixes) {
component.arrayEntryPrefixes = {};
}
for (let prefix of entryPrefixes) {
component.arrayEntryPrefixes[prefix] = stateVariable;
}

if (!stateVarObj.returnEntryDimensions) {
stateVarObj.returnEntryDimensions = () => 1;
stateVarObj.returnEntryDimensions = () => 0;
}

if (stateVarObj.shadowingInstructions) {
// function that returns wrapping components for whole array or entries (if given prefix)
// returnWrappingComponents is a function that returns the wrapping components for
// - the whole array (if called with no arguments), or
// - an array entry (if called with an array entry prefix as the argument)
// It returns wrappingComponents, which is an array of arrays.
// Each inner array corresponds to a dimension of the array,
// starting with the inner dimension,
// so that wrappingComponents[numDimensions-1], if it exists,
// corresponds to the wrapping of the entire array (or array entry),
// leading to the return of a single component.
// Each element of the inner array indicates a wrapping of the corresponding dimension,
// and they are applied in reverse order.
// Each element can be either:
// - a string corresponding to the component type used to wrap
// - an object with fields:
// - componentType: a string corresponding to the component type used to wrap
// - isAttributeNamed: a string giving the name of the attribute that this
// wrapping component should be for the wrapping component immediately preceding
// (no effect if isAttributeNamed appears in the first wrapping component)
// Unless the subsequent wrapping component has been designated isAttributeNamed,
// each wrapping component takes as children either
// - the subsequent wrapping component if it exists,
// - else the original array components.
//
// TODO: wrapping components (like most array features) was designed before
// we had array indexing such as $a.b[1].
// Hence it is based on array entries such as $a.b1, where b is the "prefix".
// $a.b[1] has to be converted to something like $a.b1
// before calculating wrapping components.
// We should rework wrapping components (and other array features)
// to make array indexing (maybe even including slices) be the basis.

if (!stateVarObj.shadowingInstructions.returnWrappingComponents) {
stateVarObj.shadowingInstructions.returnWrappingComponents = (
prefix,
Expand Down Expand Up @@ -6578,6 +6620,13 @@ export default class Core {
});
}

// arrayEntryNamesFromPropIndex is essentially a wrapper around
// stateVarObj.arrayVarNameFromPropIndex.
// (See above description of arrayVarNameFromPropIndex for technical debt commentary.)
// It calls arrayVarNameFromPropIndex on each of an array of stateVariables,
// first creating any missing array entry state variables,
// logs warnings,
// and returns an array of the resulting state variables.
async arrayEntryNamesFromPropIndex({
stateVariables,
component,
Expand Down
3 changes: 2 additions & 1 deletion packages/doenetml-worker/src/Dependencies.js
Original file line number Diff line number Diff line change
Expand Up @@ -3829,7 +3829,8 @@ class StateVariableComponentTypeDependency extends StateVariableDependency {
stateVarObj.componentType;

if (stateVarObj.isArray) {
// if array, use componentType from wrapping components, if exist
// If array, use componentType from wrapping components, if exist.
// See description of returnWrappingComponents in Core.js.
if (stateVarObj.wrappingComponents?.length > 0) {
let wrapCT =
stateVarObj.wrappingComponents[
Expand Down
1 change: 1 addition & 0 deletions packages/doenetml-worker/src/components/Angle.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ export default class Angle extends GraphicalComponent {
isArray: true,
numDimensions: 2,
entryPrefixes: ["pointX", "point"],
returnEntryDimensions: (prefix) => (prefix === "point" ? 1 : 0),
stateVariablesDeterminingDependencies: ["betweenLinesName"],
returnArraySizeDependencies: () => ({}),
returnArraySize() {
Expand Down
3 changes: 2 additions & 1 deletion packages/doenetml-worker/src/components/BestFitLine.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ export default class BestFitLine extends Line {
"point",
{
componentType: "mathList",
isAttribute: "xs",
isAttributeNamed: "xs",
},
],
];
Expand All @@ -220,6 +220,7 @@ export default class BestFitLine extends Line {
isArray: true,
numDimensions: 2,
entryPrefixes: ["datumX", "datum"],
returnEntryDimensions: (prefix) => (prefix === "datum" ? 1 : 0),
getArrayKeysFromVarName({
arrayEntryPrefix,
varEnding,
Expand Down
10 changes: 6 additions & 4 deletions packages/doenetml-worker/src/components/Circle.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ export default class Circle extends Curve {
return GraphicalComponent.returnChildGroups();
}

static returnStateVariableDefinitions(args) {
static returnStateVariableDefinitions(numerics) {
let stateVariableDefinitions =
GraphicalComponent.returnStateVariableDefinitions(args);
GraphicalComponent.returnStateVariableDefinitions(numerics);

Object.assign(
stateVariableDefinitions,
Expand Down Expand Up @@ -436,7 +436,7 @@ export default class Circle extends Curve {
"point",
{
componentType: "mathList",
isAttribute: "xs",
isAttributeNamed: "xs",
},
],
];
Expand All @@ -446,6 +446,8 @@ export default class Circle extends Curve {
isArray: true,
numDimensions: 2,
entryPrefixes: ["throughPointX", "throughPoint"],
returnEntryDimensions: (prefix) =>
prefix === "throughPoint" ? 1 : 0,
getArrayKeysFromVarName({
arrayEntryPrefix,
varEnding,
Expand Down Expand Up @@ -2434,7 +2436,7 @@ export default class Circle extends Curve {
"point",
{
componentType: "mathList",
isAttribute: "xs",
isAttributeNamed: "xs",
},
],
];
Expand Down
21 changes: 9 additions & 12 deletions packages/doenetml-worker/src/components/Copy.js
Original file line number Diff line number Diff line change
Expand Up @@ -2763,6 +2763,7 @@ export async function replacementFromProp({
}
}

// See description of returnWrappingComponents in Core.js.
let wrappingComponents = stateVarObj.wrappingComponents;
let numWrappingComponents = wrappingComponents.length;

Expand Down Expand Up @@ -3161,7 +3162,8 @@ export async function replacementFromProp({
}
}
} else {
// have wrapping components
// Have wrapping components.
// See description of returnWrappingComponents in Core.js.

let createReplacementPiece = async function (
subArrayKeys,
Expand Down Expand Up @@ -3495,9 +3497,9 @@ export async function replacementFromProp({
let attributes = {};

for (let p of pieces) {
if (p.isAttribute) {
let attr = p.isAttribute;
delete p.isAttribute;
if (p.isAttributeNamed) {
let attr = p.isAttributeNamed;
delete p.isAttributeNamed;
attributes[attr] = { component: p };
} else {
children.push(p);
Expand All @@ -3514,14 +3516,9 @@ export async function replacementFromProp({
},
];
if (typeof wrapCs[ind] === "object") {
if (wrapCs[ind].doenetAttributes) {
pieces[0].doenetAttributes = Object.assign(
{},
wrapCs[ind].doenetAttributes,
);
}
if (wrapCs[ind].isAttribute) {
pieces[0].isAttribute = wrapCs[ind].isAttribute;
if (wrapCs[ind].isAttributeNamed) {
pieces[0].isAttributeNamed =
wrapCs[ind].isAttributeNamed;
}
}
}
Expand Down
Loading

0 comments on commit ab681c8

Please sign in to comment.