Skip to content
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
12 changes: 9 additions & 3 deletions docs/source/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,23 +56,29 @@ A list of available filters can be found [here](filters/index).
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):

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

:::{important}
File conversion lives under the `convert` subcommand (`in2lambda convert ...`,
not `in2lambda ...`). This leaves room for other subcommands, such as one that
turns unstructured documents into markdown.
:::

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

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

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

If you would rather write the questions yourself, the [`Markdown` filter](filters/_autosummary/Markdown) reads a plain markdown file where `#` starts a question, `##` starts a part, and `## Solution` gives a worked solution:

```bash
$ in2lambda questions.md Markdown
$ in2lambda convert questions.md Markdown
```

By default, this generates an `out` directory in the same place that the command was run in. It contains the zipped question files.
Expand Down
13 changes: 9 additions & 4 deletions in2lambda/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,10 +171,15 @@ def runner(
return set_obj


@click.command(
@click.group(
no_args_is_help=True,
epilog="See the docs at https://lambda-feedback.github.io/in2lambda/ for more details.",
)
def cli() -> None:
"""Convert documents into Lambda Feedback compatible question sets."""


@cli.command(no_args_is_help=True)
@click.argument( # Use resolve_path to get absolute path
"question_file", type=click.Path(exists=True, readable=True, resolve_path=True)
)
Expand Down Expand Up @@ -207,11 +212,11 @@ def runner(
help="File containing solutions for QUESTION_FILE.",
type=click.Path(resolve_path=True, exists=True, dir_okay=False),
)
def cli(
def convert(
question_file: str, chosen_filter: str, output_dir: str, answer_file: Optional[str]
) -> None:
"""Takes in a QUESTION_FILE for a given SUBJECT and produces Lambda Feedback compatible json/zip files."""
# main() is made separate from click() so that it can be easily imported as part of a library.
"""Take a QUESTION_FILE and CHOSEN_FILTER and produce Lambda Feedback json/zip files."""
# Kept separate from runner() so runner() can be imported as part of the library.
runner(question_file, chosen_filter, output_dir, answer_file)


Expand Down
57 changes: 57 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""Tests for the ``in2lambda`` command-line interface (the ``convert`` subcommand)."""

import os

from click.testing import CliRunner

from in2lambda.main import cli


def test_bare_invocation_shows_usage() -> None:
result = CliRunner().invoke(cli, [])
assert "Usage:" in result.output
assert "convert" in result.output


def test_help_lists_the_convert_command() -> None:
result = CliRunner().invoke(cli, ["--help"])
assert result.exit_code == 0
assert "convert" in result.output


def test_convert_writes_output_files(filters_dir: str, tmp_path) -> None:
example = os.path.join(filters_dir, "PartsSepSol", "example.tex")
out_dir = tmp_path / "out"

result = CliRunner().invoke(
cli, ["convert", example, "PartsSepSol", "-o", str(out_dir)]
)

assert result.exit_code == 0, result.output
assert (out_dir / "set").is_dir()
assert (out_dir / "set.zip").is_file()


def test_convert_accepts_case_insensitive_filter_and_markdown(
filters_dir: str, tmp_path
) -> None:
example = os.path.join(filters_dir, "Markdown", "example.md")
out_dir = tmp_path / "out"

result = CliRunner().invoke(
cli, ["convert", example, "markdown", "-o", str(out_dir)]
)

assert result.exit_code == 0, result.output
assert (out_dir / "set" / "set_set.json").is_file()


def test_convert_rejects_unknown_filter(filters_dir: str, tmp_path) -> None:
example = os.path.join(filters_dir, "PartsSepSol", "example.tex")

result = CliRunner().invoke(
cli, ["convert", example, "NotAFilter", "-o", str(tmp_path / "out")]
)

assert result.exit_code != 0
assert "NotAFilter" in result.output
Loading