Examples

This page walks through several concrete examples using the question banks provided in examples/question_banks/. All commands below can be run from the root of the repository with the package installed.


Math quiz (MCQ only)

The simple_math.yaml bank contains 120 arithmetic questions spread across four topics: integer addition, integer subtraction, integer multiplication, and integer division.

The following command generates two randomised versions of a 20-question quiz (5 questions per topic) with a 4-column answer sheet:

pyaota build \
  -q examples/question_banks/simple_math.yaml \
  -n 2 -nq 20 -nc 4 \
  -t "integer addition" "integer subtraction" \
     "integer multiplication" "integer division" \
  -od examples/generated/math_exam \
  --seed 1 \
  --institution "Example University" \
  --course "MATH-101" --term "Spring 2026" \
  -en "Quiz 1" \
  --cleanup --odd-page-answersheet

Output files in examples/generated/math_exam/:

  • exam-<version>.pdf — one PDF per exam version (questions + answer sheet)

  • exam_version_keys.csv — answer key for every version

  • answer_keys-AnswerKeys.pdf — printable answer-key summary

  • answersheet_layout.json — machine-readable layout used by the grader

Instructions page and first questions page of a generated math quiz:

Instructions page (left) and first questions page (right) of a Math-101 quiz

Grading the math quiz

cd examples/generated/math_exam
pyaota grade \
    -i 1237_001.pdf \
    -k exam_version_keys.csv \
    -alj answersheet_layout.json \
    --interactive

Scanned answer sheet (student: Robin Banks, ID: 00245963, version: 414c343c):

Scanned answer sheet for student 00245963, exam version 414c343c

Graded answer sheet overlay:

Graded answer sheet with all correct answers marked green, score 100.0%

History exam (MCQ + True/False)

The simple_history.yaml bank mixes MCQ and True/False questions across eight topics. This example draws 4 questions from each of four topics, producing two randomised 16-question versions:

pyaota build \
  -q examples/question_banks/simple_history.yaml \
  -n 2 -nq 16 -nc 4 \
  -t "Renaissance history" "American Revolution" \
     "French Revolution" "New World Explorers" \
  -od examples/generated/history_exam \
  --seed 42 \
  --institution "Example University" \
  --course "HIST-201" --term "Spring 2026" \
  -en "Midterm" \
  --cleanup --odd-page-answersheet

True/False questions are rendered with a bold True (T) / False (F) prefix instead of a separate choices list, keeping the exam compact.

Instructions page and first questions page of a generated history midterm:

Instructions page (left) and first questions page (right) of a HIST-201 midterm

Grading the history midterm

After students complete the exam, scan all answer sheets to a single PDF and run pyaota grade. The scanned PDF for this example is 1236_001.pdf (one page, one student):

cd examples/generated/history_exam
pyaota grade \
    -i 1236_001.pdf \
    -k exam_version_keys.csv \
    -alj answersheet_layout.json \
    --interactive

Scanned answer sheet (student: Amanda Hugginkiss, ID: 10245962, version: 46685257):

Scanned answer sheet for student 10245962, exam version 46685257

With --interactive, pyaota pauses on any question where no clear bubble fill is detected and opens a matplotlib window showing a close-up of the bubble row so the operator can confirm or supply the answer:

Interactive mode prompt for Q7 — no fill detected, operator selects the answer

Graded answer sheet overlay:

Graded answer sheet with correct (green) and incorrect (red) overlays, score 56.2%

The graded overlay marks each bubble green (correct) or red (incorrect) and prints the final score. Questions left ambiguous without --interactive (Q4 and Q7 here) are scored as wrong; running with --interactive would allow the operator to resolve them and recover those points.


Programming quiz (code-block stems)

The simple_javascript.yaml bank contains questions whose stems include fenced code blocks. pyaota renders these using the lstlisting environment, making it easy to ask students to interpret or debug code snippets.

pyaota build \
  -q examples/question_banks/simple_javascript.yaml \
  -n 1 -nq 15 -nc 3 \
  -t "Basics" "Data types" "Arrays" \
  -od examples/generated/js_exam \
  --seed 7 \
  --institution "Example University" \
  --course "CS-110" --term "Spring 2026" \
  -en "Lab Quiz" \
  --cleanup --odd-page-answersheet --font-size 11pt

Instructions page and first questions page of a generated JavaScript quiz:

Instructions page (left) and first questions page (right) of a CS-110 lab quiz with code-block stems

Compile-dump (review / proofreading)

The compile-dump subcommand renders every question in a bank into a single PDF, with the correct answer highlighted. This is useful for reviewing and proofreading your question banks before exam day.

pyaota compile-dump \
  -q examples/question_banks/simple_science.yaml \
  -od examples/generated/science_dump \
  --institution "Example University" \
  --course "SCI-101" --term "Spring 2026"

The output PDF shows each question with the correct choice circled.


Blank answer sheet

You can generate a standalone blank answer sheet without building a full exam. This is useful for printing spares or for a different question count than the default:

pyaota make-answersheet \
  -nq 20 -nc 4 -o answersheet_20q \
  -od examples/generated/answersheet

Blank 20-question answer sheet:

Blank 20-question answer sheet with 4 bubble columns

Answer keys summary

After a build run, pyaota produces a printable answer-key PDF listing every version with its correct answers, suitable for use at the grading station:

Answer keys summary page for the math quiz

The companion exam_version_keys.csv can be passed directly to pyaota grade via -k.


Tips

  • Use --seed to make builds reproducible: the same seed always produces the same version ordering and question selection.

  • Increase --num-exams (-n) to generate as many unique versions as needed for a large class.

  • Add --rasterize if your printer struggles with the TikZ-heavy answer sheet.

  • Add --odd-page-answersheet for double-sided printing so the answer sheet always falls on the right-hand (odd) page.

  • Use --font-size 10pt or 11pt to fit more questions per page.