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 \
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 bare forms (cst334, memory).
Use --practice-tag-source explicit if you want strict explicit-only tag matching.
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
CLI Completion
quizgen --help
quizgen --install-completion
quizgen test 3 --test-question MLFQQuestion
The CLI supports shell completion (bash, zsh, fish, PowerShell) through Typer.
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(orcontext["rng"]) for deterministic randomness. - Avoid
refresh(); it is no longer part of the API.
Built-in Question Types
Operating Systems (CST334)
FIFOScheduling,SJFScheduling,RoundRobinSchedulingPagingQuestion,TLBQuestionSemaphoreQuestion,MutexQuestion
Machine Learning / Math (CST463)
VectorAddition,VectorDotProduct,VectorMagnitudeMatrixAddition,MatrixMultiplication,MatrixTransposeDerivativeBasic,DerivativeChainGradientDescentStep
General
FromText- Custom text questionsFromGenerator- Programmatically generated questions (requires--allow-generatororQUIZGEN_ALLOW_GENERATOR=1)
Documentation
Canvas Setup
- Create a
~/.envfile 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
- Use
--prodflag 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
FromGeneratorwith YAML files you completely trust - Never run
--allow-generatoron 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
--latexwith 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 for repository hygiene checks and a local alias:
git bump patch
The hook reports Ruff issues but does not block commits on lint failures.
git bump bumps pyproject.toml via uv version, stages pyproject.toml and uv.lock, commits, tags, and pushes by default.
Use git bump patch --no-tag --no-push for a local-only bump.
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
└── 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
- Issues: https://github.com/OtterDen-Lab/QuizGenerator/issues
- Documentation: https://github.com/OtterDen-Lab/QuizGenerator/tree/main/documentation
Note: This tool is designed for educational use. Ensure compliance with your institution's academic integrity policies when using automated quiz generation.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file quizgenerator-0.25.0.tar.gz.
File metadata
- Download URL: quizgenerator-0.25.0.tar.gz
- Upload date:
- Size: 233.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9b7593a4986064bb937d4fbc1d4a1d380fc2ad739b337ed35d8b68405bda5a86
|
|
| MD5 |
e4d43da866bfb7f3ee30c2c3dc22e41c
|
|
| BLAKE2b-256 |
95671999b30f27c7673886c52fe0a6b5dd3aa36367d9148e0294c7864f5525da
|
File details
Details for the file quizgenerator-0.25.0-py3-none-any.whl.
File metadata
- Download URL: quizgenerator-0.25.0-py3-none-any.whl
- Upload date:
- Size: 263.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3a88639d45d6d0cd41a14bbce511c6fd7f928da23118a0155408e833c1f5b0b4
|
|
| MD5 |
f32a64b8c462767d5ff98b020aff4657
|
|
| BLAKE2b-256 |
75b1159d1d8d9758d5668b50d83170571d604c7c9e0ae1f143096e998fa4e7c3
|