Skip to main content

Generate randomized quiz questions for Canvas LMS and PDF exams

Project description

QuizGenerator

Generate randomized quiz questions for Canvas LMS and PDF exams with support for multiple question types, automatic variation generation, and QR code-based answer keys.

Features

  • Multiple Output Formats: Generate PDFs (LaTeX or Typst) and Canvas LMS quizzes
  • Automatic Variations: Create unique versions for each student
  • Extensible: Plugin system for custom question types
  • Built-in Question Library: Memory management, process scheduling, calculus, linear algebra, and more
  • QR Code Answer Keys: Regenerate exact exam versions from QR codes
  • Canvas Integration: Direct upload to Canvas with variation support

Installation

pip install QuizGenerator

Reproducible installs (recommended)

If you want a fully pinned environment for a semester, use the lockfile:

uv sync --locked

We keep dependency ranges in pyproject.toml for flexibility and rely on uv.lock to pin exact versions when you need reproducible builds.

System Requirements

  • Python 3.12+
  • Typst (default PDF renderer)
  • Optional: LaTeX distribution with latexmk (if using --latex)
  • Recommended: Pandoc (for markdown conversion)
  • Optional (LaTeX + QR codes): Inkscape for SVG conversion

Optional Dependencies

# For QR code grading support
pip install "QuizGenerator[grading]"

# For CST463 machine learning questions
pip install "QuizGenerator[cst463]"

Quick Start

Need a 2‑minute setup? See documentation/getting_started.md.

1. Create a quiz configuration (YAML)

# my_quiz.yaml
name: "Midterm Exam"

questions:
  10:  # 10-point questions
    "Process Scheduling":
      class: FIFOScheduling

  5:   # 5-point questions
    "Memory Paging":
      class: PagingQuestion

    "Vector Math":
      class: VectorAddition

You can also provide an ordered list of questions:

name: "Midterm Exam"
question_order: yaml
questions:
  - name: "Process Scheduling"
    points: 10
    class: FIFOScheduling
  - name: "Memory Paging"
    points: 5
    class: PagingQuestion

2. Generate PDFs

quizgen generate --yaml my_quiz.yaml --num_pdfs 3

PDFs will be created in the out/ directory.

3. Upload to Canvas

# Set up Canvas credentials in ~/.env first:
# CANVAS_API_URL=https://canvas.instructure.com
# CANVAS_API_KEY=your_api_key_here

quizgen \
  generate \
  --yaml my_quiz.yaml \
  --num_canvas 5 \
  --course_id 12345

4. Generate Tag-Filtered Practice Quizzes

Create one practice quiz assignment per matching registered question type:

quizgen \
  practice \
  --generate_practice cst334 memory \
  --practice_match any \
  --practice_tag_source merged \
  --practice_question_groups 5 \
  --practice_variations 5 \
  --course_id 12345

These are uploaded as regular graded quiz assignments into the practice assignment group, which is configured with 0.0 group weight. Tag filters accept either namespaced tags (for example course:cst334, topic:memory) or legacy bare forms (cst334, memory). Use --practice_tag_source explicit if you want strict explicit-only tag matching. Legacy flat flags still work, but subcommands are the recommended interface.

5. Audit Tags

# Tag summary
quizgen tags list

# Show only question types missing explicit tags
quizgen tags list --only_missing_explicit --include_questions

# Explain tags for matching question types
quizgen tags explain sched

Creating Custom Questions

QuizGenerator supports two approaches for adding custom question types:

Option 1: Entry Points (Recommended for Distribution)

Create a pip-installable package:

# pyproject.toml
[project.entry-points."quizgenerator.questions"]
my_question = "my_package.questions:MyCustomQuestion"

After pip install, your questions are automatically available!

Option 2: Direct Import (Quick & Easy)

Add to your quiz YAML:

custom_modules:
  - my_questions  # Import my_questions.py

questions:
  10:
    "My Question":
      class: MyCustomQuestion

See documentation/custom_questions.md for complete guide.

Question Authoring Pattern (New)

All questions follow the same three‑method flow:

class MyQuestion(Question):
    @classmethod
    def _build_context(cls, *, rng_seed=None, **kwargs):
        context = super()._build_context(rng_seed=rng_seed, **kwargs)
        rng = context.rng
        context["value"] = rng.randint(1, 10)
        return context

    @classmethod
    def _build_body(cls, context):
        body = ca.Section()
        body.add_element(ca.Paragraph([f"Value: {context['value']}"]))
        body.add_element(ca.AnswerTypes.Int(context["value"], label="Value"))
        return body

    @classmethod
    def _build_explanation(cls, context):
        explanation = ca.Section()
        explanation.add_element(ca.Paragraph([f"Answer: {context['value']}"]))
        return explanation

Notes:

  • Always use context.rng (or context["rng"]) for deterministic randomness.
  • Avoid refresh(); it is no longer part of the API.

Built-in Question Types

Operating Systems (CST334)

  • FIFOScheduling, SJFScheduling, RoundRobinScheduling
  • PagingQuestion, TLBQuestion
  • SemaphoreQuestion, MutexQuestion

Machine Learning / Math (CST463)

  • VectorAddition, VectorDotProduct, VectorMagnitude
  • MatrixAddition, MatrixMultiplication, MatrixTranspose
  • DerivativeBasic, DerivativeChain
  • GradientDescentStep

General

  • FromText - Custom text questions
  • FromGenerator - Programmatically generated questions (requires --allow_generator or QUIZGEN_ALLOW_GENERATOR=1)

Documentation

Canvas Setup

  1. Create a ~/.env file with your Canvas credentials:
# For testing/development
CANVAS_API_URL=https://canvas.test.instructure.com
CANVAS_API_KEY=your_test_api_key

# For production
CANVAS_API_URL_prod=https://canvas.instructure.com
CANVAS_API_KEY_prod=your_prod_api_key
  1. Use --prod flag for production Canvas instance:
quizgen generate --prod --num_canvas 5 --course_id 12345 --yaml my_quiz.yaml

Advanced Features

Typst Support

Typst is the default for faster compilation. Use --latex to force LaTeX:

quizgen generate --latex --num_pdfs 3 --yaml my_quiz.yaml

Experimental: --typst_measurement uses Typst to measure question height for tighter layout. It can change pagination and ordering, so use with care on finalized exams.

Layout Optimization

By default, questions keep their YAML order (or point-value ordering for mapping format). Use --optimize_space to reorder questions to reduce PDF page count. This also affects Canvas order.

Deterministic Generation

Use seeds for reproducible quizzes:

quizgen generate --seed 42 --num_pdfs 3 --yaml my_quiz.yaml

Generation Controls

Limit backoff attempts for questions that retry until they are "interesting":

quizgen generate --yaml my_quiz.yaml --num_pdfs 1 --max_backoff_attempts 50

Set a default numeric tolerance for float answers (overridable per question):

quizgen generate --yaml my_quiz.yaml --num_pdfs 1 --float_tolerance 0.01

Per-answer override in custom questions:

ca.AnswerTypes.Float(value, label="Result", tolerance=0.005)

QR Code Regeneration

Each generated exam includes a QR code that stores:

  • Question types and parameters
  • Random seed
  • Version information

Use the grading tools to scan QR codes and regenerate exact exam versions.

Security Considerations

FromGenerator Warning

The FromGenerator question type executes arbitrary Python code from your YAML configuration files. This is a powerful feature for creating dynamic questions, but it carries security risks:

  • Only use FromGenerator with YAML files you completely trust
  • Never run --allow_generator on YAML files from untrusted sources
  • Be cautious when sharing question banks that contain generator code

FromGenerator is disabled by default. To enable it, use one of:

quizgen generate --allow_generator --yaml my_quiz.yaml
# or
QUIZGEN_ALLOW_GENERATOR=1 quizgen generate --yaml my_quiz.yaml

If you need dynamic question generation with untrusted inputs, consider writing a proper Question subclass instead, which provides better control and validation.

LaTeX -shell-escape Warning

When using --latex, QuizGenerator invokes latexmk -shell-escape to compile PDFs. This allows LaTeX to execute external commands (for example, via \write18). If your question content includes raw LaTeX (e.g., from custom question types or untrusted YAML sources), this can be a command‑execution vector.

Guidance:

  • Only use --latex with trusted question sources.
  • Prefer Typst (default) when possible.
  • If you need LaTeX but want to reduce risk, avoid raw LaTeX content and keep custom questions constrained to ContentAST elements.

Local Release Helper (Recommended)

Install repository-managed git hooks and alias:

bash scripts/install_git_hooks.sh

This installs a pre-commit hook that checks version-bump vendoring and a local alias:

git bump patch

git bump bumps pyproject.toml via uv version, vendors lms_interface, stages pyproject.toml/uv.lock/lms_interface, and commits. Use git bump patch --verbose for full vendoring logs (default output is a short summary).

Project Structure

QuizGenerator/
├── QuizGenerator/           # Main package
│   ├── question.py         # Question base classes and registry
│   ├── quiz.py            # Quiz generation logic
│   ├── contentast.py      # Content AST for cross-format rendering
│   ├── premade_questions/ # Built-in question library
│   └── ...               # Question types and rendering utilities
├── example_files/        # Example quiz configurations
├── documentation/        # User guides
├── lms_interface/        # Canvas LMS integration
└── quizgen             # CLI entry point

Contributing

Contributions welcome! Areas of interest:

  • New question types
  • Additional LMS integrations
  • Documentation improvements
  • Bug fixes

License

GNU General Public License v3.0 or later (GPLv3+) - see LICENSE file for details

Citation

If you use QuizGenerator in academic work, please cite:

@software{quizgenerator,
  author = {Ogden, Sam},
  title = {QuizGenerator: Automated Quiz Generation for Education},
  year = {2024},
  url = {https://github.com/OtterDen-Lab/QuizGenerator}
}

Support


Note: This tool is designed for educational use. Ensure compliance with your institution's academic integrity policies when using automated quiz generation.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

quizgenerator-0.21.2.tar.gz (202.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

quizgenerator-0.21.2-py3-none-any.whl (228.4 kB view details)

Uploaded Python 3

File details

Details for the file quizgenerator-0.21.2.tar.gz.

File metadata

  • Download URL: quizgenerator-0.21.2.tar.gz
  • Upload date:
  • Size: 202.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.3 {"installer":{"name":"uv","version":"0.10.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for quizgenerator-0.21.2.tar.gz
Algorithm Hash digest
SHA256 b6eb40e4da6a98612e0b236e28dadf49ab3784e3d478d7691893569feeb58016
MD5 9fa6eaedecf818a7d247d0eb618a43e1
BLAKE2b-256 c93e259200a5b888dcaa27b57c2cd395b0a85730655a813dc84126366fbdea69

See more details on using hashes here.

File details

Details for the file quizgenerator-0.21.2-py3-none-any.whl.

File metadata

  • Download URL: quizgenerator-0.21.2-py3-none-any.whl
  • Upload date:
  • Size: 228.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.3 {"installer":{"name":"uv","version":"0.10.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for quizgenerator-0.21.2-py3-none-any.whl
Algorithm Hash digest
SHA256 c04a561b346fcf20527decb87f638cb27e70d99ccc8b927345cc5ed6b70549e7
MD5 e3d95502acfd4985cb403f62270a38eb
BLAKE2b-256 3528aa536e00426d88faf3dffcb5c2dd3860fffa0ce24b373f969844699f48a8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page