diff --git a/src/ProblemLayout/ProblemInput/ProblemInput.js b/src/ProblemLayout/ProblemInput/ProblemInput.js
index b1904247d77..078588bacf8 100644
--- a/src/ProblemLayout/ProblemInput/ProblemInput.js
+++ b/src/ProblemLayout/ProblemInput/ProblemInput.js
@@ -5,6 +5,7 @@ import TextField from "@material-ui/core/TextField";
import MultipleChoice from "./MultipleChoice";
import GridInput from "./GridInput";
import MatrixInput from "./MatrixInput";
+import TableInput from "./TableInput";
import { renderText } from "../../ProblemLogic/renderText";
import clsx from "clsx";
import './ProblemInput.css'
@@ -12,6 +13,7 @@ import { shuffleArray } from "../../util/shuffleArray";
import { EQUATION_EDITOR_AUTO_COMMANDS, EQUATION_EDITOR_AUTO_OPERATORS, ThemeContext } from "../../config/config";
import { stagingProp } from "../../util/addStagingProperty";
import { parseMatrixTex } from "../../util/parseMatrixTex";
+import { parseTableTex } from "../../util/parseTableTex";
class ProblemInput extends React.Component {
static contextType = ThemeContext;
@@ -22,6 +24,8 @@ class ProblemInput extends React.Component {
this.equationRef = createRef()
this.onEquationChange = this.onEquationChange.bind(this)
+
+ this.questionAnswer = this.props.stepAnswer
}
componentDidMount() {
@@ -30,6 +34,11 @@ class ProblemInput extends React.Component {
console.log('automatically determined matrix input to be the correct problem type')
}
+ console.debug('problem', this.props.step, 'seed', this.props.seed)
+ if (this.isTableInput()) {
+ console.log('automatically determined table input to be the correct problem type')
+ }
+
const mqDisplayArea = this.equationRef?.current?.querySelector(".mq-editable-field > .mq-root-block")
if (mqDisplayArea != null) {
mqDisplayArea.ariaHidden = true
@@ -52,6 +61,17 @@ class ProblemInput extends React.Component {
}
}
+ isTableInput() {
+ if (this.props.step?.stepAnswer) {
+ return this.props.step?.problemType !== "MultipleChoice" &&
+ /\\begin{[a-zA-Z]?tabular}/.test(this.props.step.stepAnswer[0])
+ }
+ if (this.props.step?.hintAnswer) {
+ return this.props.step?.problemType !== "MultipleChoice" &&
+ /\\begin{[a-zA-Z]?tabular}/.test(this.props.step.hintAnswer[0])
+ }
+ }
+
onEquationChange(eq) {
const containerEl = this.equationRef?.current
const eqContentEl = this.equationRef?.current?.querySelector(".mq-editable-field")
@@ -75,6 +95,17 @@ class ProblemInput extends React.Component {
}
this.props.setInputValState(eq)
}
+
+
+ getDimAndHeaders(correctAnswer) {
+ const temp = correctAnswer;
+ const TotalRows = correctAnswer.split("\\\\").length;
+ const numCols = correctAnswer.split("\\\\").join("&").split("&").length / TotalRows;
+ const headers = temp.match(/([a-zA-z]+)/g).slice(2,2+numCols)
+ const numRows = TotalRows - 1;
+ return {numRows, numCols, headers}
+ }
+
render() {
const { classes, state, index } = this.props;
@@ -87,8 +118,15 @@ class ProblemInput extends React.Component {
problemType = "MatrixInput"
}
+ if (this.isTableInput()) {
+ problemType = "TableInput"
+ }
+
+
return (
+ {/* {console.log("tbl",parseTableTex(correctAnswer))} */}
+ {/* {console.log("mx", parseMatrixTex(correctAnswer))} */}
{(problemType === "TextBox" && this.props.step.answerType !== "string") && (
@@ -171,6 +209,22 @@ class ProblemInput extends React.Component {
} : {}}
/>
)}
+ {problemType === "TableInput" && (
+ this.props.setInputValState(newVal)}
+ numRows={this.getDimAndHeaders(correctAnswer).numRows}
+ numCols={this.getDimAndHeaders(correctAnswer).numCols}
+ headers= {this.getDimAndHeaders(correctAnswer).headers}
+ context={this.props.context}
+ classes={this.props.classes}
+ index={index}
+ {...(use_expanded_view && debug) ? {
+ defaultValue: parseTableTex(correctAnswer)[0]
+ } : {}}
+ />
+
+
+ )}
@@ -178,6 +232,7 @@ class ProblemInput extends React.Component {
+
)
}
diff --git a/src/ProblemLayout/ProblemInput/TableInput.js b/src/ProblemLayout/ProblemInput/TableInput.js
new file mode 100644
index 00000000000..baf913988c2
--- /dev/null
+++ b/src/ProblemLayout/ProblemInput/TableInput.js
@@ -0,0 +1,145 @@
+import React, { createRef } from "react";
+import './GridInput.css'
+import Button from "@material-ui/core/Button";
+import clsx from 'clsx';
+import EquationEditor from "equation-editor-react";
+import { Box,TextField } from "@material-ui/core";
+import { EQUATION_EDITOR_AUTO_COMMANDS, EQUATION_EDITOR_AUTO_OPERATORS } from "../../config/config";
+import { stagingProp } from "../../util/addStagingProperty";
+
+class TableInput extends React.Component {
+
+ constructor(props) {
+
+ super(props);
+ this.state = {
+ gridState: props.defaultValue || this.genEmptyGrid(props.numRows, props.numCols),
+ fer: Math.random()
+ };
+ this.gridRef = createRef()
+ this.clearCells = this.clearCells.bind(this)
+ this.numRows = props.numRows;
+ this.numCols = props.numCols;
+ this.headers = props.headers;
+ this.renderTabular = props.renderTabular;
+ }
+
+
+ genEmptyGrid = (numRows, numCols) => new Array(numRows)
+ .fill(0)
+ .map(_ => new Array(numCols).fill(""))
+
+ cellFieldChange(str, idx, jdx) {
+ const gridState = this.state.gridState
+ gridState[idx][jdx] = str;
+
+ this.setState({
+ gridState
+ }, () => {
+ this.props.onChange(JSON.stringify(this.state.gridState))
+ })
+ }
+
+ clearCells(evt) {
+ if (evt != null && evt.type === 'submit') {
+ evt.preventDefault()
+ }
+
+ this.setState({
+ gridState: this.genEmptyGrid(this.numRows, this.numCols),
+ fer: Math.random()
+ }, () => {
+ this.props.onChange(JSON.stringify(this.state.gridState))
+ })
+
+ }
+
+
+ render() {
+ const { classes, index } = this.props;
+
+ const { gridState, fer } = this.state;
+
+ const revealClearButton = gridState.reduce((acc, cur, _) =>
+ acc + cur.reduce((_acc, _cur, __) =>
+ _acc + _cur.length, 0
+ ), 0
+ ) > 0; // only reveal the clear button if there is at least something in a cell
+
+ return (
+
+
+ {console.log(gridState[0])}
+ {
+ gridState[0].map((row, idx) => {
+ return (
+
+ )
+ })
+ }
+ {
+ gridState.map((row, idx) =>
+ row.map((val, jdx) => {
+ return (
+
+ this.cellFieldChange(str, idx, jdx)}
+ style={{ width: "100%" }}
+ autoCommands={EQUATION_EDITOR_AUTO_COMMANDS}
+ autoOperatorNames={EQUATION_EDITOR_AUTO_OPERATORS}
+ />
+
+
+
+ )
+ })
+ )
+ }
+
+
+
+
+
+
+ )
+ }
+
+}
+
+
+export default TableInput;
diff --git a/src/ProblemLayout/commonStyles.js b/src/ProblemLayout/commonStyles.js
index 2af2301ee2e..836dcce79a7 100644
--- a/src/ProblemLayout/commonStyles.js
+++ b/src/ProblemLayout/commonStyles.js
@@ -145,6 +145,27 @@ const styles = theme => ({
paddingBottom: 5
}
},
+ textTblLatex: {
+ borderRadius: "0px",
+ outline: "1px solid #c4c4c4",
+ '&:hover': {
+ border: "1px solid #000000",
+ },
+ '&:focus-within': {
+ border: "2px solid #3f51b5",
+ },
+ height: 50,
+ width: '100%',
+ '& > .mq-editable-field': {
+ display: 'table',
+ tableLayout: 'fixed'
+ },
+ '& > * > *[mathquill-block-id]': {
+ height: 50,
+ display: 'table-cell',
+ paddingBottom: 5
+ }
+ },
textBoxLatexIncorrect: {
boxShadow: "0 0 0.75pt 0.75pt red",
'&:focus-within': {
diff --git a/src/ProblemLogic/checkAnswer.js b/src/ProblemLogic/checkAnswer.js
index 23ebfd48350..d4e1607aeb3 100644
--- a/src/ProblemLogic/checkAnswer.js
+++ b/src/ProblemLogic/checkAnswer.js
@@ -2,6 +2,7 @@ import { variabilize } from './variabilize.js';
import insert from "../util/strInsert";
import { parseMatrixTex } from "../util/parseMatrixTex";
import { IS_DEVELOPMENT, IS_STAGING_OR_DEVELOPMENT } from "../util/getBuildType";
+import { parseTableTex } from '../util/parseTableTex.js';
const KAS = require('../kas.js');
@@ -63,6 +64,8 @@ function parse(_string) {
function checkAnswer(attempt, actual, answerType, precision, variabilization) {
let parsed = attempt.replace(/\s+/g, '');
+ console.debug(`attempt: ${attempt} vs. actual:`, actual)
+ console.log(parsed)
if (variabilization) {
actual = actual.map((actualAns) => variabilize(actualAns, variabilization));
}
@@ -93,6 +96,26 @@ function checkAnswer(attempt, actual, answerType, precision, variabilization) {
})
return [attempt, correctAnswer]
+ } else if (/\\begin{[a-zA-Z]?tabular}/.test(actual)){
+ console.debug(`attempt: ${attempt} vs. actual:`, actual)
+ const studentMatrix = JSON.parse(attempt)
+ const solutionMatrices = parseTableTex(actual);
+
+ console.debug('solutions: ', solutionMatrices)
+ correctAnswer = solutionMatrices.some(matrix => {
+ return matrix.reduce((acc, row, idx) => acc && row.reduce((_acc, cell, jdx) => {
+ const _studentRow = studentMatrix[idx] || []
+ const _studentCell = _studentRow[jdx] || ""
+ const _studentExpr = parse(_studentCell).expr
+
+ const _solExpr = parse(cell).expr
+
+ return _acc && KAS.compare(_studentExpr, _solExpr).equal
+ }, true), true)
+ })
+
+ return [attempt, correctAnswer]
+
} else {
if (IS_STAGING_OR_DEVELOPMENT) {
console.debug("Using KAS to compare answer with solution", attempt, actual)
diff --git a/src/ProblemLogic/renderText.js b/src/ProblemLogic/renderText.js
index caacddf5dcc..05d4302b4b2 100644
--- a/src/ProblemLogic/renderText.js
+++ b/src/ProblemLogic/renderText.js
@@ -5,6 +5,8 @@ import { variabilize, chooseVariables } from './variabilize.js';
import Spacer from "../Components/_General/Spacer";
import ErrorBoundary from "../Components/_General/ErrorBoundary";
import RenderMedia from "../Components/_General/RenderMedia";
+import { Box,TextField } from "@material-ui/core";
+import { parseTableTex, parseTableHeaders } from '../util/parseTableTex.js';
function renderText(text, problemID, variabilization) {
if (typeof text !== 'string') {
@@ -18,9 +20,80 @@ function renderText(text, problemID, variabilization) {
if (variabilization) {
result = variabilize(text, variabilization);
}
-
const lines = result.split("\\n");
return lines.map((line, idx) => {
+ if (text.includes('tabular')) {
+ const headers = parseTableHeaders(text)
+ const answers = parseTableTex(text)[0]
+ const parsedResult = headers.concat(answers)
+ const numCols = headers[0].length
+ // const numRows = parsedResult.length
+ // const EmptyGrid = new Array(numRows - 1).fill(0).map(_ => new Array(numCols).fill(""))
+ return
+
+ {
+ parsedResult[0].map((row, idx) => {
+ return (
+
+ )
+ })
+ }
+
+
+ {
+ parsedResult.slice(1).map((row, idx) =>
+ row.map((val, jdx) => {
+ return (
+
+
+ )
+ })
+ )
+ }
+
+
+ }
/**
* If line has LaTeX, split by the "&&" delimiter to separate plain text from LaTeX
* @type {(string | JSX.Element)[]}
@@ -56,6 +129,7 @@ function renderText(text, problemID, variabilization) {
}
return lineParts;
})
+
}
/**
diff --git a/src/ProblemPool/agtesttbl1/agtesttbl1.json b/src/ProblemPool/agtesttbl1/agtesttbl1.json
new file mode 100644
index 00000000000..73d29c0a979
--- /dev/null
+++ b/src/ProblemPool/agtesttbl1/agtesttbl1.json
@@ -0,0 +1,9 @@
+{
+ "id": "agtesttbl1",
+ "title": "Find two way table",
+ "body": "Find two way table",
+ "variabilization": {},
+ "oer": "https://openstax.org/",
+ "lesson": "7.5 Matrices and Matrix Operations",
+ "courseName": "Openstax: College Algebra"
+}
\ No newline at end of file
diff --git a/src/ProblemPool/agtesttbl1/steps/agtesttbl1a/agtesttbl1a.json b/src/ProblemPool/agtesttbl1/steps/agtesttbl1a/agtesttbl1a.json
new file mode 100644
index 00000000000..2684c1a75c6
--- /dev/null
+++ b/src/ProblemPool/agtesttbl1/steps/agtesttbl1a/agtesttbl1a.json
@@ -0,0 +1,13 @@
+{
+ "id": "agtesttbl1a",
+ "stepAnswer": [
+ "$$\\begin{tabular} apple & sugar \\\\ -6 & -4 \\\\ 1 & 3 \\end{tabular}$$"
+ ],
+ "problemType": "TextBox",
+ "stepTitle": "enter table with -6, -4, 1, 3",
+ "stepBody": "",
+ "answerType": "arithmetic",
+ "variabilization": {},
+ "answerLatex": "enter table with -6, -4, 1, 3"
+}
+
diff --git a/src/ProblemPool/agtesttbl1/steps/agtesttbl1a/tutoring/agtesttbl1aDefaultPathway.json b/src/ProblemPool/agtesttbl1/steps/agtesttbl1a/tutoring/agtesttbl1aDefaultPathway.json
new file mode 100644
index 00000000000..65d08b4e65e
--- /dev/null
+++ b/src/ProblemPool/agtesttbl1/steps/agtesttbl1a/tutoring/agtesttbl1aDefaultPathway.json
@@ -0,0 +1,10 @@
+[
+ {
+ "id": "agtesttbl1a-h1",
+ "type": "hint",
+ "dependencies": [],
+ "title": "Multiplying a Matrix by a Scalar",
+ "text": "To multiply a matrix A by a scaler C, multiply each entry in A by C. $$\\begin{tabular} apple & sugar \\\\ -4 & \\\\ 1 & 3 \\end{tabular}$$",
+ "variabilization": {}
+ }
+]
\ No newline at end of file
diff --git a/src/ProblemPool/agtesttbl2/agtesttbl2.json b/src/ProblemPool/agtesttbl2/agtesttbl2.json
new file mode 100644
index 00000000000..2869ea1b8e0
--- /dev/null
+++ b/src/ProblemPool/agtesttbl2/agtesttbl2.json
@@ -0,0 +1,9 @@
+{
+ "id": "agtesttbl2",
+ "title": "Find two way table",
+ "body": "Find two way table",
+ "variabilization": {},
+ "oer": "https://openstax.org/",
+ "lesson": "7.5 Matrices and Matrix Operations",
+ "courseName": "Openstax: College Algebra"
+}
\ No newline at end of file
diff --git a/src/ProblemPool/agtesttbl2/steps/agtesttbl2a/agtesttbl2a.json b/src/ProblemPool/agtesttbl2/steps/agtesttbl2a/agtesttbl2a.json
new file mode 100644
index 00000000000..8a24f06883d
--- /dev/null
+++ b/src/ProblemPool/agtesttbl2/steps/agtesttbl2a/agtesttbl2a.json
@@ -0,0 +1,12 @@
+{
+ "id": "agtesttbl2a",
+ "stepAnswer": [
+ "$$\\begin{tabular} apple & sugarandhoneyand & candy \\\\ -6 & -4 & 3\\\\ 1 & 3 & 2\\end{tabular}$$"
+ ],
+ "problemType": "TextBox",
+ "stepTitle": "enter table with -6, -4, 3, 1, 3, 2",
+ "stepBody": "",
+ "answerType": "arithmetic",
+ "variabilization": {},
+ "answerLatex": "enter table with -6, -4, 3, 1, 3, 2"
+}
diff --git a/src/ProblemPool/agtesttbl2/steps/agtesttbl2a/tutoring/agtesttbl2aDefaultPathway.json b/src/ProblemPool/agtesttbl2/steps/agtesttbl2a/tutoring/agtesttbl2aDefaultPathway.json
new file mode 100644
index 00000000000..dfdf0ee8580
--- /dev/null
+++ b/src/ProblemPool/agtesttbl2/steps/agtesttbl2a/tutoring/agtesttbl2aDefaultPathway.json
@@ -0,0 +1,10 @@
+[
+ {
+ "id": "agtesttbl2a-h1",
+ "type": "hint",
+ "dependencies": [],
+ "title": "Multiplying a Matrix by a Scalar",
+ "text": "To multiply a matrix A by a scaler C, multiply each entry in A by C.",
+ "variabilization": {}
+ }
+]
\ No newline at end of file
diff --git a/src/ProblemPool/stepfiles.txt b/src/ProblemPool/stepfiles.txt
index a12abefa12b..bf40bfe5dd3 100644
--- a/src/ProblemPool/stepfiles.txt
+++ b/src/ProblemPool/stepfiles.txt
@@ -1,3 +1,5 @@
+ agtesttbl1a: ["matrices_and_matrix_operations"],
+ agtesttbl2a: ["matrices_and_matrix_operations"],
a6f9727real1a: ["realnumber"],
a6f9727real1b: ["realnumber"],
a6f9727real1c: ["realnumber"],
diff --git a/src/config/skillModel.js b/src/config/skillModel.js
index f553f2acca1..3875ba7189e 100644
--- a/src/config/skillModel.js
+++ b/src/config/skillModel.js
@@ -1,5 +1,6 @@
const skillModel = {
-
+ agtesttbl1a: [`matrices_and_matrix_operations`],
+ agtesttbl2a: [`matrices_and_matrix_operations`],
a6f9727real1a: [`classifying_a_real_number`],
a6f9727real1b: [`classifying_a_real_number`],
a6f9727real1c: [`classifying_a_real_number`],
diff --git a/src/tools/preprocessProblemPool.js b/src/tools/preprocessProblemPool.js
index ac3b894dadc..67cb337d0bb 100644
--- a/src/tools/preprocessProblemPool.js
+++ b/src/tools/preprocessProblemPool.js
@@ -110,8 +110,9 @@ const staticFiguresPath = path.join(__dirname, '..', '..', 'public', 'static', '
}
problem.steps = await Promise.all(
- stepDirs.map(
- async stepDir => {
+ stepDirs
+ .filter(s => !(s.toString().startsWith("_") || s.toString().startsWith("."))) // ignore directories that start with _ or .
+ .map(async stepDir => {
const stepName = stepDir.toString()
const stepPath = path.join(problemPath, 'steps', stepName)
diff --git a/src/util/parseTableTex.js b/src/util/parseTableTex.js
new file mode 100644
index 00000000000..c54f9e3b015
--- /dev/null
+++ b/src/util/parseTableTex.js
@@ -0,0 +1,33 @@
+function parseTableTex(solTex) {
+ const solutionTable = []
+
+ ;(Array.isArray(solTex) ? solTex : [solTex]).forEach(sol => {
+ const _start = sol.indexOf("tabular} ") + "tabular} ".length
+ const _end = sol.indexOf("\\end{")
+ let _solutionTable = sol
+ .substring(_start, _end)
+ .trim()
+ .split("\\\\")
+ .map(row => row.split("&").map(val => val.trim()))
+ solutionTable.push(_solutionTable)
+ })
+ return Array(solutionTable[0].slice(1));
+}
+
+function parseTableHeaders(solTex) {
+ const solutionTable = []
+
+ ;(Array.isArray(solTex) ? solTex : [solTex]).forEach(sol => {
+ const _start = sol.indexOf("tabular} ") + "tabular} ".length
+ const _end = sol.indexOf("\\end{")
+ let _solutionTable = sol
+ .substring(_start, _end)
+ .trim()
+ .split("\\\\")
+ .map(row => row.split("&").map(val => val.trim()))
+ solutionTable.push(_solutionTable)
+ })
+ return Array(solutionTable[0][0]);
+}
+
+export { parseTableTex, parseTableHeaders }
\ No newline at end of file