Skip to content

Commit

Permalink
2138 add summary (#3902)
Browse files Browse the repository at this point in the history
* Get exercises working in docker

- Add new compose file for exercises
- Update dockerfile to use node 18
- Listen on 0.0.0.0 instead of localhost
- Use node-sass 9
- Fix build and serve commands

* Update bin scripts to use npx

* Add "summary" solution type to question model

* Add summary to question component

* Only display "detailed" solutions in question preview component

* Add tests and update snapshots

* Check solution_type in unit test

Also update the docker-quickstart
  • Loading branch information
TylerZeroMaster authored Dec 20, 2023
1 parent 5f30239 commit b5958ce
Show file tree
Hide file tree
Showing 17 changed files with 696 additions and 172 deletions.
21 changes: 17 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
FROM node:10.9-slim as builder
FROM node:18-slim as base

RUN apt-get update && apt-get install -y \
build-essential \
g++ \
gcc \
git \
python2.7 \
&& rm -rf /var/lib/apt/lists/*
curl \
# Additional packages required by python build
zlib1g \
zlib1g-dev \
libssl-dev \
libbz2-dev \
libsqlite3-dev \
&& rm -rf /var/lib/apt/lists/*

FROM base as builder

ENV PATH=/root/.pyenv/bin:$PATH
RUN curl https://pyenv.run | bash \
&& eval "$(pyenv virtualenv-init -)" \
&& pyenv install 2.7 \
&& ln -s /root/.pyenv/shims/python /usr/bin/python

RUN ln -s /usr/bin/python2.7 /usr/bin/python2

FROM builder as build
ARG PUBLIC_PATH
Expand Down
2 changes: 1 addition & 1 deletion bin/build
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ if [ "$(version "$npm_version")" -lt "$(version "$required_version")" ]; then
fi

[ -d $OX_PROJECT/dist ] && rm -r $OX_PROJECT/dist
$(npm bin)/webpack --config webpack.config.js
NODE_OPTIONS="--openssl-legacy-provider" npx webpack --config webpack.config.js

# we're done unless the "archive" flag from the ansible build is set
if [ "$2" != "archive" ]; then
Expand Down
10 changes: 5 additions & 5 deletions bin/lint
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@ if [ $# -ne 0 ]; then
if [ "$1" == "staged" ]; then
files=`git status --short */{src,specs} | egrep '^[^D|^R].*([j|t]sx?)$' | awk '{ print $2 }'`
if [ ! -z "$files" ]; then
$(npm bin)/eslint $2 $files
npx eslint $2 $files
fi
elif [ "$1" == "fix" ]; then
$(npm bin)/eslint --ext $EXT {tutor,shared,exercises}/{src,specs} --fix
npx eslint --ext $EXT {tutor,shared,exercises}/{src,specs} --fix
else
$(npm bin)/eslint --ext $EXT $1/{src,specs}
npx eslint --ext $EXT $1/{src,specs}
fi
exit $?
fi

if [[ ! -z "${OX_PROJECT}" ]]; then
$(npm bin)/eslint --ext $EXT $OX_PROJECT/{src,specs}
npx eslint --ext $EXT $OX_PROJECT/{src,specs}
exit $?
fi

$(npm bin)/eslint --ext $EXT {tutor,shared,exercises}/{src,specs}
npx eslint --ext $EXT {tutor,shared,exercises}/{src,specs}
2 changes: 1 addition & 1 deletion bin/serve
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ echo Serving: $OX_PROJECT
if [ x$2 == x"standalone" ]; then
node tutor/specs/acceptance/server/index.js
else
node --max_old_space_size=2048 $(npm bin)/webpack-dev-server --debug --progress --config webpack.config.js
NODE_OPTIONS="--openssl-legacy-provider --max_old_space_size=2048" npx webpack-dev-server --debug --progress --config webpack.config.js
fi
4 changes: 2 additions & 2 deletions bin/test
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ done
echo Test: $OX_PROJECT

if [[ "$OX_PROJECT" = "e2e" ]]; then
$(npm bin)/playwright test --config configs/playwright.config.ts "$OTHER_ARGS"
npx playwright test --config configs/playwright.config.ts "$OTHER_ARGS"
if [[ "$WATCH" = "t" ]]; then
$(npm bin)/chokidar ./tutor/specs/e2e/*.ts -d 1500 -c '$(npm bin)/playwright test --config configs/playwright.config.ts {path}'
npx chokidar ./tutor/specs/e2e/*.ts -d 1500 -c 'npx playwright test --config configs/playwright.config.ts {path}'
fi
else
$DIR/test-unit $OX_PROJECT $OTHER_ARGS
Expand Down
2 changes: 1 addition & 1 deletion bin/test-unit
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ if [[ -z "$NODE_OPTIONS" ]]; then
export NODE_OPTIONS="--max-old-space-size=768"
fi

$(npm bin)/jest --verbose false --config ./configs/test/jest.$1.js $args
npx jest --verbose false --config ./configs/test/jest.$1.js $args
14 changes: 14 additions & 0 deletions docker-compose.exercises.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
version: '3.5'
services:
build:
command: bash -c "if [ -z \"`ls -A node_modules`\" ]; then yarn; fi && yarn serve exercises"
build:
context: .
target: builder
working_dir: /code
environment:
- "OX_PROJECT_HOST=0.0.0.0"
volumes:
- .:/code:cached
ports:
- "8001:8001"
4 changes: 3 additions & 1 deletion docker-compose.override.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
version: '3.5'
services:
build:
command: bash -c "if [ -z \"`ls -A node_modules`\" ]; then yarn; fi && yarn serve tutor"
command: bash -c "if [ -z \"`ls -A node_modules`\" ]; then yarn; fi && yarn run serve tutor"
build:
context: .
target: builder
working_dir: /code
environment:
- "OX_PROJECT_HOST=0.0.0.0"
volumes:
- .:/code:cached
ports:
Expand Down
20 changes: 18 additions & 2 deletions docker-quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ the following projects must be separtely cloned and run for this project to work

[openstax/tutor-server](https://github.com/openstax/tutor-server)

## Run
## Run Tutor

``` bash
docker-compose up
Expand All @@ -26,6 +26,16 @@ the js files will be served at `http://localhost:8000`

the ui is available through the tutor-server project at `http://localhost:3001`

## Run Exercises frontend

``` bash
docker-compose -f docker-compose.exercises.yml up
```

the js files will be served at `http://localhost:8001`

You will need to start the exercises backend independently

## node_modules

if you need to update node modules run:
Expand All @@ -34,8 +44,14 @@ if you need to update node modules run:
docker-compose exec build yarn
```

in general you can run anythin in the build container with
in general you can run anything in the build container with

```
docker-compose exec build <command>
```

## Troubleshooting

if you run into problems, try the following:
* rebuilding the build image with `docker-compose build`
* remove your node_modules directory
32 changes: 32 additions & 0 deletions exercises/specs/components/__snapshots__/exercise.spec.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,13 @@ exports[`Exercises component renders and matches snapshot 1`] = `
onChange={[Function]}
value="four"
/>
<label>
Summary
</label>
<textarea
onChange={[Function]}
value=""
/>
</div>
</div>
</div>
Expand Down Expand Up @@ -2893,6 +2900,13 @@ exports[`Exercises component renders with intro and a multiple questions when ex
onChange={[Function]}
value="four"
/>
<label>
Summary
</label>
<textarea
onChange={[Function]}
value=""
/>
</div>
</div>
</div>
Expand Down Expand Up @@ -3689,6 +3703,13 @@ exports[`Exercises component renders with intro and a multiple questions when ex
onChange={[Function]}
value="four"
/>
<label>
Summary
</label>
<textarea
onChange={[Function]}
value=""
/>
</div>
</div>
</div>
Expand Down Expand Up @@ -4114,6 +4135,13 @@ exports[`Exercises component renders with intro and a multiple questions when ex
onChange={[Function]}
value="four"
/>
<label>
Summary
</label>
<textarea
onChange={[Function]}
value=""
/>
</div>
</div>
</div>
Expand Down Expand Up @@ -6665,6 +6693,10 @@ exports[`Exercises component resets fields when model is new 1`] = `
Detailed Solution
</label>
<textarea onChange={[Function: updateSolution] { isMobxAction: true }} value=\\"four\\" />
<label>
Summary
</label>
<textarea onChange={[Function: updateSummary] { isMobxAction: true }} value=\\"\\" />
</div>
</div>
</Question>
Expand Down
9 changes: 9 additions & 0 deletions exercises/src/components/exercise/question.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ class Question extends React.Component {
question.collaborator_solution_html = event.target.value;
}

@action.bound updateSummary(event) {
const { question } = this.props;
question.summary_html = event.target.value;
}

@action.bound addAnswer() {
this.props.question.answers.push(new AnswerModel());
}
Expand Down Expand Up @@ -139,6 +144,10 @@ class Question extends React.Component {
Detailed Solution
</label>
<textarea onChange={this.updateSolution} value={question.collaborator_solution_html} />
<label>
Summary
</label>
<textarea onChange={this.updateSummary} value={question.summary_html} />
</div>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@
"jsdom": "^16.5.0",
"json-server": "^0.15.0",
"lighthouse": "^5.2.0",
"node-sass": "^7.0.0",
"node-sass": "^9.0.0",
"object-factory-bot": "0.8.2",
"pixelmatch": "^5.0.2",
"pngjs": "^3.4.0",
Expand Down
30 changes: 30 additions & 0 deletions shared/specs/model/exercise/question.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,36 @@ describe('Exercise Question', () => {
question.collaborator_solution_html = '';
expect(question.collaborator_solutions).toHaveLength(0);
});
it('#summary_html', () => {
question.collaborator_solutions.clear()
expect(question.collaborator_solutions).toHaveLength(0);
question.summary_html = 'one, two, three';
expect(question.collaborator_solutions).toHaveLength(1);
expect(question.collaborator_solutions[0].content_html).toEqual('one, two, three');
question.summary_html = 'four';
expect(question.collaborator_solutions[0].content_html).toEqual('four');
question.summary_html = '';
expect(question.collaborator_solutions).toHaveLength(0);
});
it('can handle both summary and detailed solution types', () => {
question.collaborator_solutions.clear()
expect(question.collaborator_solutions).toHaveLength(0);
question.summary_html = 'one, two, three';
question.collaborator_solution_html = 'four, five, six';
expect(question.collaborator_solutions).toHaveLength(2);
expect(question.collaborator_solutions[0].content_html).toEqual('one, two, three');
expect(question.collaborator_solutions[1].content_html).toEqual('four, five, six');
expect(question.collaborator_solutions[0].solution_type).toEqual('summary');
expect(question.collaborator_solutions[1].solution_type).toEqual('detailed');
question.summary_html = 'four';
expect(question.collaborator_solutions[0].content_html).toEqual('four');
question.summary_html = '';
expect(question.collaborator_solutions).toHaveLength(1);
expect(question.collaborator_solutions[0].content_html).toEqual('four, five, six');
expect(question.collaborator_solutions[0].solution_type).toEqual('detailed');
question.collaborator_solution_html = '';
expect(question.collaborator_solutions).toHaveLength(0);
});
it('uses answers for detecting multi-choice', () => {
expect(question.isMultipleChoice).toBe(true);
question.answers.clear()
Expand Down
4 changes: 3 additions & 1 deletion shared/src/components/question/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,9 @@ class Question extends React.Component {
{...htmlAndMathProps}
className="solution"
block={true}
html={map(collaborator_solutions, 'content_html').join('')} />
html={map(collaborator_solutions.filter(
(sol) => sol.solution_type === 'detailed'
), 'content_html').join('')} />
</div>;
}

Expand Down
50 changes: 38 additions & 12 deletions shared/src/model/exercise/question.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,34 @@ class ExerciseQuestion extends BaseModel {
modelize(this)
}

private findSolutionByType(solution_type: string) {
return this.collaborator_solutions.findIndex(
(sol) => sol.solution_type === solution_type
);
}

private setSolutionByType(solution_type: string, val: string) {
let idx = this.findSolutionByType(solution_type);
if (!val) {
if (idx !== -1) {
this.collaborator_solutions.splice(idx, 1);
}
} else {
if (idx === -1) {
const newSolution = new Solution;
newSolution.solution_type = solution_type;
idx = this.collaborator_solutions.push(newSolution) - 1;
}
this.collaborator_solutions[idx].content_html = val;
}
}

private getSolutionHTMLByType(solution_type: string) {
const idx = this.findSolutionByType(solution_type)
return idx !== -1 ?
this.collaborator_solutions[idx].content_html : '';
}

@computed get allowedFormatTypes() {
const type = this.exercise.tags.withType('type');
if (type && type.value != 'practice') {
Expand Down Expand Up @@ -90,21 +118,19 @@ class ExerciseQuestion extends BaseModel {
}

@computed get collaborator_solution_html() {
return this.collaborator_solutions.length ?
this.collaborator_solutions[0].content_html : '';
return this.getSolutionHTMLByType('detailed');
}

set collaborator_solution_html(val) {
if (!val) {
if (this.collaborator_solutions.length) {
this.collaborator_solutions.clear()
}
} else {
if (!this.collaborator_solutions.length) {
this.collaborator_solutions.push(new Solution);
}
this.collaborator_solutions[0].content_html = val;
}
this.setSolutionByType('detailed', val);
}

@computed get summary_html() {
return this.getSolutionHTMLByType('summary');
}

set summary_html(val) {
this.setSolutionByType('summary', val);
}

@computed get formatId() {
Expand Down
2 changes: 1 addition & 1 deletion webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ const config = {
quiet: false,
noInfo: false,
clientLogLevel: 'warning',
host: 'localhost',
host,
filename: '[name].js',
hot: !isCI,
liveReload: !isCI,
Expand Down
Loading

0 comments on commit b5958ce

Please sign in to comment.