Metadata-Version: 2.4
Name: QuestionBankToolkit
Version: 0.2.0
Summary: Canvas QTI 1.2 question banks from YAML
Author-email: "David D. Marshall" <ddmarsha@calpoly.edu>
License-Expression: GPL-3.0-only
Project-URL: Documentation, https://ddmarshall.codeberg.page/QuestionBankToolkit/
Project-URL: Repository, https://codeberg.org/ddmarshall/QuestionBankToolkit
Project-URL: Changelog, https://codeberg.org/ddmarshall/QuestionBankToolkit/src/branch/main/CHANGELOG.md
Project-URL: Issues, https://codeberg.org/ddmarshall/QuestionBankToolkit/issues
Project-URL: License, https://codeberg.org/ddmarshall/QuestionBankToolkit/LICENSE
Keywords: assessment,canvas-lms,education,exam-generation,ims-qti,learning-management-system,multiple-choice,qti,question-bank,quiz,quiz-generator,yaml
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Education
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Education
Classifier: Topic :: Education :: Testing
Classifier: Topic :: Software Development :: Code Generators
Classifier: Topic :: Text Processing :: Markup :: XML
Classifier: Topic :: Utilities
Requires-Python: >=3.14
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: PyYAML<7,>=6.0
Requires-Dist: defusedxml<1,>=0.7.1
Dynamic: license-file

# QuestionBankToolkit

QuestionBankToolkit validates question banks written in YAML, builds QTI 1.2 ZIP packages for Canvas, and extracts supported QTI 1.2 questions into YAML.
The current scope is single-answer multiple-choice questions.
QTI imports and structural checks reject XML document type declarations (DTDs), entity declarations, and external entity references.
Python 3.14 or newer is required.

Install the released package from PyPI:

```bash
python -m pip install QuestionBankToolkit
```

See [the YAML schema](docs/schema.md) and [the sample bank](examples/sample_course/questions.yaml) for the source format.
The [documentation site](https://ddmarshall.codeberg.page/QuestionBankToolkit/) provides the complete user guide, schema, command reference, and Python API reference.
The optional bank field `default_shuffle_choices` controls answer-choice shuffling in generated QTI, and a question-level `shuffle_choices` boolean can override it.
If both fields are omitted, shuffling defaults to `false`.

## Start here

The [beginner workflow](docs/getting-started.md) explains Python installation on Windows, macOS, and Linux; provides a complete copyable bank; and walks through validation, build review, and Canvas upload.
The generated `COURSE_BANK_ID_QTI12.zip` file is the file uploaded to Canvas.

After installation, validate a YAML bank with:

```bash
coursebanks validate examples/sample_course/questions.yaml
```

The command reports invalid fields and rejects malformed YAML, duplicate mapping keys, and nonfinite grading points or time estimates.
See the [validation rules](docs/schema.md#validation-rules) for supported values and diagnostics.
See the [command reference and troubleshooting guide](docs/commands.md) for every command's output, exit status, file policy, and common errors.

## Optional: importing QTI

Import a supported QTI 1.2 ZIP package into a new YAML bank with:

```bash
coursebanks import-qti questions.zip --course COURSE123 --bank-id ImportedBank --title "Imported bank" --out imported.yaml
```

The importer supports a strict single-answer multiple-choice subset, including Canvas Common Cartridge item-bank exports, and rejects unsupported or ambiguous structures.
See [the QTI import guide](docs/importing-qti.md) for required package structure, defaults, choice-label normalization, metadata CSV overrides, and the field-by-field compatibility table.

## Optional: selecting and recording questions

Assessment names can describe exams, weekly quizzes, or homework.
A question's optional `assessments` list restricts where it can be used; an empty or omitted list allows any assessment.

```bash
coursebanks select questions.yaml --assessment "Homework 1" --count 5
coursebanks record questions.yaml --term 2027-Fall --assessment "Homework 1" --ids Q01 Q02
coursebanks plan questions.yaml --terms 2027-Fall 2028-Spring --assessment "Quiz 1" 3 --assessment "Homework 1" 2 --out plan.csv
```

Selection and planning only propose questions; record the IDs actually administered.
Counts are nonnegative integers and plan counts apply in each term.
Repeat `--assessment NAME COUNT` for each planned assessment; this replaces the former `--midterm` and `--final` plan options.
Selection and recording still accept `--exam` as an alias for `--assessment`.
All three commands accept `--history PATH` (default `usage.csv`), and selection and planning accept `--seed` for reproducible proposals.
Recorded questions cannot be reused within the same course and bank, even for a different assessment.
New history and plan CSVs use the column `assessment`; legacy histories with an `exam` column remain readable and keep that header when appended.

Existing YAML banks are converted on load without changing their files or schema version.
See [migration details](docs/schema.md#migrating-existing-banks) for the legacy-value mapping and QTI metadata behavior.

## For contributors

Check that `python --version` reports Python 3.14 or newer.
From the repository root, use that interpreter to create and activate a virtual environment, then install the package and development tools:

```bash
python -m venv .venv
```

On Linux or macOS, activate it with `source .venv/bin/activate`.
On Windows PowerShell, use `.venv\Scripts\Activate.ps1`.

```bash
python -m pip install -e . --group dev
python -m pytest
```

Tests use synthetic questions and do not require private course content or Canvas access.

With the environment activated, run all development checks using:

```bash
./scripts/run_checks.sh
```

The script runs pytest, Ruff lint and formatting, strict mypy, strict BasedPyright, and pydoclint.
See the [developer guide](docs/development.md) for individual commands, environment configuration, and the typed Python API.
For Python callers, `load_bank` returns a validated bank; use `load_yaml` to edit incomplete drafts and `parse_bank` to validate and construct a bank from draft data.
