Skip to content
Merged
37 changes: 19 additions & 18 deletions CHANGELOG.md

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions docs/source/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ og:title: in2lambda
:::{grid-item}
\
\
Automagically uploads questions to [Lambda Feedback](https://lambda-feedback.github.io/user-documentation/) so you don't have to.
Converts a document of questions into a question set that [Lambda Feedback](https://lambda-feedback.github.io/user-documentation/) imports.

```{button-ref} quickstart
:ref-type: doc
Expand All @@ -39,19 +39,19 @@ Get Started
:::{grid-item-card} {octicon}`tools;1.5em` Highly Configurable
:link: filters/index
:link-type: doc
Can be used to process numerous file formats with a variety of different structures by using [pandoc filters](https://pandoc.org/filters.html).
Reads many file formats, and documents of many structures, through [pandoc filters](https://pandoc.org/filters.html).
:::

:::{grid-item-card} {octicon}`terminal;1.5em` Accessible Command Line Tool
:link: reference/command-line
:link-type: doc
Just provide the question file and select one of the in-built file parsers.
Name the question file and one of the built-in filters.
:::

:::{grid-item-card} {octicon}`gear;1.5em` Powerful API
:link: reference/library
:link-type: doc
A fully type-annotated extensively documented Python library is available for those that need a bit more control.
A type-annotated, documented Python library builds a question set without a source document.
:::

::::
Expand Down
75 changes: 37 additions & 38 deletions docs/source/question-format.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describes that JSON, and how to build it from Python without a source document.
A {class}`~in2lambda.api.set.Set` holds {class}`~in2lambda.api.question.Question` objects, each
holding {class}`~in2lambda.api.part.Part` objects, each holding the
{class}`~in2lambda.api.response_area.ResponseArea` boxes students type into.
{meth}`~in2lambda.api.set.Set.to_json` writes the lot.
{meth}`~in2lambda.api.set.Set.to_json` writes the set as JSON.

```pycon
>>> from in2lambda.api.part import Part
Expand Down Expand Up @@ -66,8 +66,8 @@ holding {class}`~in2lambda.api.part.Part` objects, each holding the

```

{meth}`~in2lambda.api.set.Set.to_json` writes a folder named after the set, and a zip of it to
upload:
{meth}`~in2lambda.api.set.Set.to_json` writes a folder named after the set, and a zip of that
folder to upload:

```pycon
>>> import json, os, tempfile
Expand All @@ -87,24 +87,23 @@ upload:

```

A few things the example shows in passing:
The example also shows the following:

- **Building parts directly beats the incremental helpers.** [Filters](filters/index)
read a document in order, so they call
- **Pass `Part` objects to `Question` where the script holds the whole question.**
[Filters](filters/index) read a document in order, so they call
{meth}`~in2lambda.api.question.Question.add_part_text` and
{meth}`~in2lambda.api.question.Question.add_solution`, which fill in whichever part comes next.
A script that already knows the whole question should pass `Part` objects to `Question`, as
above; only those give a part a final answer or an answer box.
- **A line holding only `---` (or `***`) splits a worked solution** into the steps students go
through one at a time in the structured tutorial.
- **Unset question settings are left out of the JSON** rather than guessed at, so `skill`,
`guidance` and the two durations only appear when set. `publish` and the four `display_*`
settings always do, defaulting to `True`.
- **Images** go in `Question.images` as paths on disk; they are copied into `media/` under the file
name they already had, and every reference to one in the question's markdown is rewritten to that
name, which is all Lambda Feedback looks an image up by.
A `Part` object gives a part a final answer and an answer box, which those two methods do not.
- **A line holding only `---` (or `***`) splits a worked solution** into the steps the
structured tutorial shows students one at a time.
- **Unset question settings are left out of the JSON**, so `skill`, `guidance` and the two
durations appear only when set. `publish` and the four `display_*` settings always appear, and
default to `True`.
- **Images** are paths on disk listed in `Question.images`. `to_json` copies each image into
`media/` under its own file name, and rewrites every reference to that image in the question's
markdown to the same name. Lambda Feedback looks an image up by that name alone.
- **{meth}`Set.from_json <in2lambda.api.set.Set.from_json>`** reads an existing export, as a folder
or a zip, so an edit to a real set can start from what Lambda Feedback produced.
or a zip, so an edit to a real set starts from the export Lambda Feedback produced.

## The JSON in2lambda writes

Expand All @@ -116,12 +115,13 @@ A few things the example shows in passing:
<set name>.zip # the folder, zipped, to upload
```

A question's filename is its title with spaces and the characters Windows and path separators
forbid (`/ \ < > : " | ? *`) each replaced by an underscore. An image keeps the file name it
already had, so `images=["figures/rocket-momentum.png"]` gives `media/rocket-momentum.png`, and the
references to it are rewritten to that name. `media/` is one flat folder for the whole set, so a
file two questions use is copied once, and a second file of a name already taken is named as Lambda
Feedback names one, `question_001_<Title>_0001.png`. Files are written on a single line.
A question's filename is its title, with spaces and the characters Windows and path separators
forbid (`/ \ < > : " | ? *`) each replaced by an underscore. An image keeps its own file name, so
`images=["figures/rocket-momentum.png"]` gives `media/rocket-momentum.png`, and every reference to
that image is rewritten to `rocket-momentum.png`. `media/` is one flat folder for the whole set: a
file two questions use is copied once, and a second file whose name is already taken is named as
Lambda Feedback names an image, `question_001_<Title>_0001.png`. Each JSON file is written on a
single line.

### Set

Expand Down Expand Up @@ -154,33 +154,32 @@ The three types in2lambda writes:
| `NUMERIC_UNITS` | `comparePhysicalQuantities` | a number and a unit, e.g. `0.106 kg` | `gradeParams` holds `rtol` (and `strict_syntax`); `config` is null |
| `MULTIPLE_CHOICE` | `arrayEqual` | a list of booleans, one per option | `config` holds `single`, `options` and `randomise`; `gradeParams` is null |

The three lists an area carries, each a dataclass in
A response area holds three lists, each of a dataclass in
{mod}`in2lambda.api.response_area`:

- `inputSymbols` — `{"symbol", "code", "aliases", "isVisible"}` from
{class}`~in2lambda.api.response_area.InputSymbol`. `symbol` is what students see
(e.g. `\(\rho\)`), `code` what the evaluation function reads.
{class}`~in2lambda.api.response_area.InputSymbol`. Lambda Feedback displays `symbol` to students
(e.g. `\(\rho\)`), and the evaluation function reads `code`.
- `tests` — `{"id", "payload", "expectedResponse": {"isCorrect"}}` from
{class}`~in2lambda.api.response_area.Test`: the author's own checks of the marking.
- `cases` — `{"id", "answer", "feedback", "isCorrect", "params"}` from
{class}`~in2lambda.api.response_area.Case`: a response matching `answer` is shown `feedback`,
and may be marked correct.

An `id` left unset is a fresh UUID, which is what import needs.
An `id` left unset is written as a fresh UUID, which import requires.

### Markdown

Maths is `$...$` inline and `$$` on its own lines for display, rendered by
[KaTeX](https://katex.org/): commands KaTeX lacks do not display — degrees, for example, are
written `^\circ`. An image is written `![pictureTag](rocket-momentum.png)`, naming the file as it
sits in `media/`. A filter passes through whatever path the source document used, so
`\includegraphics{figures/rocket-momentum.png}` becomes `![pictureTag](figures/rocket-momentum.png)`
in the set; writing the set out rewrites it to `![pictureTag](rocket-momentum.png)`, which is the
image as `media/` holds it. A reference naming no image of the question is left as written, and
{func}`~in2lambda.validation.validate` reports it.
[KaTeX](https://katex.org/) renders maths written `$...$` inline and `$$` on its own lines for
display. KaTeX does not display the commands it lacks, so a degree is written `^\circ`. An image
is written `![pictureTag](rocket-momentum.png)`, naming the file as `media/` holds it. A filter
passes the source document's path through, so `\includegraphics{figures/rocket-momentum.png}`
becomes `![pictureTag](figures/rocket-momentum.png)` in the set, and writing the set out rewrites
that reference to `![pictureTag](rocket-momentum.png)`. A reference naming no image of the question
is written as it stands, and {func}`~in2lambda.validation.validate` reports that reference.

:::{note}
Lambda Feedback's own exports carry a few keys in2lambda neither reads nor writes, among them
`isSurvey` and `releasedAt` on the set. Diffing a written set against a real export will show
them missing; the platform fills them in on import.
Lambda Feedback's own exports hold a few keys in2lambda neither reads nor writes, among them
`isSurvey` and `releasedAt` on the set. A diff of a written set against a real export shows those
keys missing. Lambda Feedback fills them in on import.
:::
40 changes: 20 additions & 20 deletions docs/source/quickstart.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
# 🚀 Quickstart

This page gives a quick overview of how to get started with in2lambda to quickly add documents to Lambda Feedback.
This page describes how to install in2lambda and convert a document into a Lambda Feedback question set.

## 1. Installation

### Docker

[![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/lambda-feedback/in2lambda/docker-publish.yml?style=flat-square&logo=docker&label=Docker)](https://github.com/lambda-feedback/in2lambda/pkgs/container/in2lambda)

The following creates an interactive container which includes in2lambda and mounts the current working directory into `/files`:
The following command starts an interactive container holding in2lambda, with the current working directory mounted at `/files`:

```bash
$ docker run -it --rm -v $(pwd):/files ghcr.io/lambda-feedback/in2lambda sh
```

Within the container, we can access the files and run in2lambda as normal.
Run in2lambda over those files inside the container.

```bash
$ cd files
Expand All @@ -23,15 +23,15 @@ $ ...
$ exit
```

The container is stopped and deleted after exiting, although the image remains downloaded for future use.
Docker stops and deletes the container on exit. The image stays on disk for the next run.

### PyPi

[![PyPI - Version](https://img.shields.io/pypi/v/in2lambda?logo=pypi&logoColor=white&color=blue&style=flat-square)](https://pypi.org/project/in2lambda/)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/in2lambda?style=flat-square&logo=python&logoColor=white)](https://pypi.org/project/in2lambda/)


in2lambda can be installed via [pip](https://pip.pypa.io/en/stable/). To author questions in Python:
[pip](https://pip.pypa.io/en/stable/) installs in2lambda. To write questions in Python:

```shell
$ pip install in2lambda
Expand All @@ -44,50 +44,50 @@ $ pip install 'in2lambda[convert]'
$ in2lambda --help
```

This can also be done through [pipx](https://pypa.github.io/pipx/).
[pipx](https://pypa.github.io/pipx/) installs in2lambda as well.

## 2. Choose a Document

`in2lambda convert` takes in two arguments:
`in2lambda convert` takes two arguments:

- The path to a document.
- A filter describing how to parse it.
- A filter describing how to parse that document.

A list of available filters can be found [here](filters/index).
The [filters page](filters/index) lists every filter.

For instance, the following takes in `questions.tex` and uses a filter that expects [each part to be directly followed by the solution](filters/_autosummary/PartSolPartSol):
The following command reads `questions.tex` with a filter that expects [each part to be followed by its solution](filters/_autosummary/PartSolPartSol):

```bash
$ in2lambda convert questions.tex PartSolPartSol
```

:::{note}
The filter name is case-insensitive. Don't worry about the capital letters.
The filter name is case-insensitive.
:::

Another filter might be used if [the answers are in a separate file](filters/_autosummary/PartsSepSol):
A different filter reads [answers held in a separate file](filters/_autosummary/PartsSepSol):

```bash
$ in2lambda convert questions.tex -a solutions.tex PartsSepSol
```

By default, this generates an `out` directory in the same place that the command was run in. It contains the zipped question files.
`in2lambda convert` writes an `out` directory in the directory the command ran in, holding the zipped question files.

Before writing anything, in2lambda prints the problems it can detect that would stop the set importing or make it render wrongly — an answer that doesn't fit the box marking it, a figure the export won't contain, maths that KaTeX can't display. Each names the question, part and field to go and look at. They are warnings rather than errors: the `out` directory is written either way, since a problem found here may well be deliberate.
Before writing that directory, in2lambda prints the problems that would stop Lambda Feedback importing the set or would render it wrongly: an answer that does not fit the box marking it, a figure the export would not contain, maths KaTeX cannot display. Each problem names the question, the part and the field holding it. Each problem is a warning, and in2lambda writes the `out` directory whatever it finds, because an author may have intended the problem.

The maths is checked by rendering it with KaTeX itself, the way Lambda Feedback will, which needs [Node.js](https://nodejs.org) installed. Without Node.js everything else is still checked and in2lambda says the maths was not.
in2lambda checks the maths by rendering it with KaTeX, as Lambda Feedback renders it, which needs [Node.js](https://nodejs.org). Without Node.js, in2lambda runs the other checks and reports that it did not check the maths.

With [xelatex](https://tug.org/texlive/) installed alongside pandoc, the set is also compiled the way Lambda Feedback makes a PDF of it, and any LaTeX error names the field it is in. Without it, one warning says which packages to install instead.
With [xelatex](https://tug.org/texlive/) installed alongside pandoc, in2lambda also compiles the set as Lambda Feedback compiles a PDF of it, and names the field holding each LaTeX error. Without xelatex, in2lambda prints one warning naming the packages to install.

Check the [command line tool reference](reference/command-line) for more information.
The [command line reference](reference/command-line) describes every command and option.

## 3. Import into Lambda Feedback

Click on a set in teacher mode. The arrow next to the "Add Question" button allows you to import a question from a file.
Open a set in teacher mode. The arrow beside the "Add Question" button imports a question from a file.

Choose the zip file you wish to upload, and the question should appear! 🎉
Choose the zip file to upload, and Lambda Feedback adds the question to the set.

Imported questions arrive published with every display setting on, and the set's own visibility settings still apply. The Python API can set each of these per question — see the
An imported question arrives published, with every display setting on, and the set's own visibility settings apply to it. The Python API sets each of these per question; see the
[question format](question-format).

![Importing Question from file in Teacher Mode](_static/images/import-teacher.png)
Loading
Loading