Utilities for building question banks and generating exam documents.
Project description
PromptuKit
Utilities for building and managing multiple-choice question banks and generating exam PDFs and slide decks.
Install from PyPI
pip install promptukit
The package is published at https://pypi.org/project/promptukit/. After installing you get the CLI entry points on your PATH:
add-question
extract-question --help
validate-question
question-bank --help
create-exam-md --help # export question bank to editable Markdown (optional PDF via pandoc)
create-pptx --help # generate a PPTX deck from a question bank
promptukit-gui # launch the browser-based authoring GUI
promptukit-claude-commands # list/show/install bundled Claude Code slash commands (multi-subcommand CLI)
promptukit-claude-commands-install # alias for `promptukit-claude-commands install`
You can also import the library in Python or a Jupyter notebook:
import promptukit as pk
# Top-level helpers: pk.load(path), pk.save(path, data), pk.pick(), pk.confirm()
# Launch the authoring GUI: pk.launch_gui()
# Subpackages are available as `pk.exams`, `pk.questions`, and `pk.utils`.
Quick Notebook Walkthrough
Here are short, copy-pasteable examples you can run inside a Jupyter notebook to load a question bank, validate it, and generate a PDF exam.
# 1) Import helpers
import promptukit as pk
from promptukit.questions import validate_question
from promptukit.exams import create_exam
# 2) Load a question bank (path relative to the repository root). If you're
# running outside the repository (for example from an installed package),
# fall back to the packaged sample dataset that ships with `promptukit`.
import os
bank_path = 'promptukit/data/question_banks/crb-water-management-sample.json'
if os.path.exists(bank_path):
data = pk.load(bank_path)
else:
# load packaged sample included with the installed package
data = pk.load_resource('question_banks/crb-water-management-sample.json')
# 3) Inspect the file (section-based vs flat list)
if 'sections' in data:
print('Sections:', [s.get('title') for s in data['sections']])
print('First question:', data['sections'][0]['questions'][0])
elif 'questions' in data:
print('Total questions:', len(data['questions']))
print('First question:', data['questions'][0])
else:
print('Unexpected file shape:', type(data))
# 4) Validate programmatically
errors, warnings = validate_question.validate(data)
if errors:
print('Validation errors:', errors)
else:
print('Bank valid — warnings:', warnings)
# 5) Generate a PDF exam from the same bank. We already have `data` loaded
# above as a dict, so pass it directly.
create_exam.build_exam_pdf(data, 'notebooks/output_exam.pdf')
Notes
- If you only want to run the library functions without Poetry activation, you
can run modules with
python -m promptukit.questions.extract_questionorpython -m promptukit.exams.create_examas shown elsewhere in this README. - PDF generation uses
reportlab, which is installed automatically withpromptukit.
Try the interactive Colab demo
If you'd like a runnable notebook that demonstrates the Quick Notebook Walkthrough, open the Colab demo:
https://colab.research.google.com/drive/1vzaUML_8nkWKhOfauv5MXPE-dQ5sXFF_?usp=sharing
Quick tips for Colab:
- To use the published package on PyPI:
!pip install promptukit
- To run the repository version (latest changes), clone and install from GitHub:
!git clone https://github.com/jrkasprzyk/promptukit.git
%cd promptukit
!pip install -e .
- The Colab notebook includes cells that use
pk.load_resource(...)as a fallback when localcontent/files aren't available.
Getting started (Poetry, for development)
-
Install Poetry (if you don't have it):
pip install --user poetry
-
Use a compatible Python interpreter (
>=3.12,<4.0) and create the virtual environment with dependencies:poetry install -
Run the CLI tools via Poetry (console scripts / entry points):
poetry run add-question poetry run extract-question --help poetry run validate-question poetry run question-bank --help poetry run create-pptx --help poetry run promptukit-gui --help poetry run promptukit-claude-commands --help
If you change dependencies or [tool.poetry.scripts] in pyproject.toml, run
poetry install again so Poetry refreshes the local environment and console
scripts.
Activating the virtualenv
If Poetry is configured to create an in-project virtualenv, it will be
placed in a .venv folder at the repository root. Activate that
environment from the project root using the command for your shell.
PowerShell (Windows):
.\.venv\Scripts\Activate.ps1
If script execution is blocked, temporarily allow it then activate:
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
.\.venv\Scripts\Activate.ps1
Command Prompt (cmd.exe):
.\.venv\Scripts\activate.bat
Git Bash / MSYS (Windows):
source .venv/Scripts/activate
macOS / Linux (POSIX):
source .venv/bin/activate
Alternatives (no manual activation required):
# Run a single command inside the virtualenv without activating it
poetry run <cmd> # e.g. poetry run pytest
For development from a source checkout, prefer poetry run <cmd> unless you
have activated the Poetry virtualenv. After activation, the bare console-script
names work in that shell.
Poetry 2.x note:
- The
poetry shellcommand (which previously spawned a new shell) is not installed by default in Poetry 2.0+. You can either usepoetry env activate(then evaluate the printed activation command in your shell) or install the shell plugin to restorepoetry shell.
Notes
- The package entry points are defined in
pyproject.tomlunder[tool.poetry.scripts]and map console script names to themain()functions in the modules under thepromptukitpackage.
Usage Examples
Quick (Poetry):
poetry run add-question path/to/mybank.json
poetry run extract-question --list-categories
poetry run validate-question
poetry run question-bank extract --help
poetry run create-pptx --help
Extracting data:
# List categories and available fields
poetry run extract-question --list-categories
# Print prompt and answer fields for the 'music' category
poetry run extract-question --file promptukit/data/question_banks/block-doku-sample.json --category music --fields prompt,answer
# Interactive picker
poetry run extract-question -i
Add questions:
# Interactive add; prompts for question type unless --type is provided
poetry run add-question path/to/mybank.json
poetry run add-question --type TrueFalse path/to/mybank.json
# Create a new bank while adding
poetry run add-question --create path/to/new-bank.json
# Batch mode
poetry run add-question --batch new_questions.json promptukit/data/question_banks/mybank.json
Validate a trivia file:
# Validate the default question bank
poetry run validate-question
# Validate a specific file
poetry run validate-question promptukit/data/question_banks/block-doku-sample.json
Manage files with question-bank (create/copy/extract):
# Create a new template JSON file
poetry run question-bank create --dest promptukit/data/question_banks/new.json --categories music,film-and-tv
# Copy an existing file
poetry run question-bank copy --src promptukit/data/question_banks/block-doku-sample.json --dest promptukit/data/question_banks/backup.json
# Extract a subset (easy music questions)
poetry run question-bank extract --src promptukit/data/question_banks/block-doku-sample.json --dest promptukit/data/question_banks/music_easy.json --categories music --difficulty easy
# Interactive extract
poetry run question-bank extract -i --src promptukit/data/question_banks/block-doku-sample.json --dest promptukit/data/question_banks/pick.json
Additional maintenance subcommands are available for schema and text cleanup:
poetry run question-bank migrate --help
poetry run question-bank audit-text --help
poetry run question-bank fix-text --help
poetry run question-bank render-audit --help
Alternative: run modules with python -m when not using Poetry:
python -m promptukit.questions.add_question path/to/mybank.json
python -m promptukit.questions.extract_question --help
python -m promptukit.questions.question_bank create --dest promptukit/data/question_banks/new.json
Authoring GUI (NiceGUI)
promptukit ships a lightweight browser GUI for authoring multiple-choice
question banks without touching Python. It reads and writes the same JSON
format used by the rest of the package (the one validated by
validate-question), so you can open any existing bank in
promptukit/data/question_banks/ (or your own) and edit it in place.
Launch from the shell after installing the package or activating the Poetry virtualenv:
promptukit-gui # opens http://localhost:8080 in a browser tab
promptukit-gui -f my_bank.json # load (or create) this working file
promptukit-gui -p 9000 --no-browser # custom port, don't auto-open a tab
From a source checkout without activation, prefix the same commands with
poetry run, for example:
poetry run promptukit-gui -f my_bank.json
-f/--file points at the GUI's working file — if it exists it's loaded on
startup; when you click Save all to file it gets overwritten with the
current in-memory list. You can also change the working file from inside the
GUI via the top-bar Open… button.
Or from Python:
from promptukit import launch_gui
launch_gui() # defaults: ./promptukit_questions.json, port 8080
launch_gui(file_path="my_bank.json", port=9000, show=False)
The GUI is a single page with a resizable splitter: question list on the left,
editor on the right. Each list row shows the full prompt (wrapping as needed)
plus id, category, and a color-coded difficulty badge. The editor exposes
every field in the schema:
id(text)category(text with autocomplete from the file'scategorieslist)difficulty(easy / medium / hard)prompt(autosizing textarea)choices(four inputs A–D, with a radio to pick which one isanswer)quip_correct,quip_wrong(optional textareas — omitted from the saved file when blank, matching the existing banks' convention)
Top-bar buttons:
- Open… — switch the working file (loads it if it exists, or starts empty with that path queued for the next save).
- Reload from file — discard in-memory edits and re-read the working file.
- Save all to file — the only action that writes to the current working file, so you can discard a session.
- Save as… — write the in-memory bank to a new path and switch the working file to it (existing files are not overwritten unless you opt in).
- Copy all as JSON — full bank (including
categories/_schema_notes). - Copy selected as JSON — the single-question dict, ready to paste into
another bank's
questionsarray.
The editor's Apply button commits edits to the in-memory list (the top-bar Save writes them to disk).
On-disk format (unchanged from the rest of the package):
{
"_schema_notes": ["optional free-form notes"],
"categories": ["music", "motorsport"],
"questions": [
{
"id": "music_001",
"category": "music",
"difficulty": "easy",
"prompt": "Which instrument has a keyboard and strings?",
"choices": ["Guitar", "Piano", "Violin", "Drums"],
"answer": 1,
"quip_correct": "Yep.",
"quip_wrong": "Nope."
}
]
}
Unknown top-level keys and unknown per-question keys are preserved verbatim on round-trip, so the GUI is safe to point at files with extra metadata it doesn't understand.
Requires nicegui (installed automatically as a dependency).
Create exam PDF
The create_exam.py script can generate a printable exam PDF. It accepts
an external JSON question bank so you can build exams from your existing
promptukit/data/question_banks/ files.
Usage (from the repository root with Poetry):
# Use the built-in hard-coded exam
poetry run python -m promptukit.exams.create_exam
# Load questions from a JSON bank and write a PDF
poetry run python -m promptukit.exams.create_exam -q promptukit/data/question_banks/block-doku-sample.json -o cven4333_from_json.pdf
# Save reproducible artifacts while creating the PDF
poetry run python -m promptukit.exams.create_exam \
-q promptukit/data/question_banks/block-doku-sample.json \
--save-questions exam_questions.json \
--save-setup exam_setup.json \
-o cven4333_from_json.pdf
You can also create the editable two-file artifact pair with the extraction tool, then render from those files:
poetry run question-bank extract \
--src promptukit/data/question_banks/block-doku-sample.json \
--dest exam_questions.json \
--categories music \
--setup-dest exam_setup.json \
--artifact-kind exam \
-f
poetry run python -m promptukit.exams.create_exam \
-q exam_questions.json \
-m exam_setup.json \
-o exam.pdf
Supported JSON formats
-
Top-level
sections(preferred):{ "sections": [ { "title": "Section title", "questions": [ { "prompt": "...", "choices": ["...", "..."] }, ... ] } ] }
-
categoriesis an alias forsectionsand is also accepted. -
Flat list of questions (top-level array) or top-level object with
questionsarray:{ "questions": [ { "prompt": "...", "choices": ["...", "..."], "category": "Section title" }, ... ] }
-
Question objects support multiple common field names:
prompt,q,question, ortextfor the question text;choicesoranswersfor the answer options; optionalcategoryto group flat lists into sections. -
If choices are not already labeled (for example "Oceans" instead of "A) Oceans"), the script will prefix them with
A),B), etc. Prompts without a leading number will be auto-numbered sequentially.
Create exam Markdown
The create_exam_md.py module exports a question bank to an editable
Markdown file. Because the Markdown is the intermediate format, a professor
can tweak questions, reorder sections, or adjust wording before producing the
final PDF — without being constrained by what promptukit's PDF renderer
directly supports.
Typical workflow:
JSON bank → Markdown (.md) → [edit] → PDF
Usage:
# Basic export
create-exam-md -q bank.json -o exam.md
# Include a numbered answer key at the end
create-exam-md -q bank.json -o exam.md --answers key
# Show answers inline after each question (instructor preview)
create-exam-md -q bank.json -o exam.md --answers inline
# Export Markdown and convert to PDF in one step (requires pandoc)
create-exam-md -q bank.json -o exam.md --to-pdf
# Specify a custom PDF output path
create-exam-md -q bank.json -o exam.md --to-pdf /path/to/final.pdf
# With a metadata/setup file
create-exam-md -q bank.json -m setup.json -o exam.md --to-pdf
# Using python -m (no Poetry activation required)
python -m promptukit.exams.create_exam_md -q bank.json -o exam.md --answers key
--to-pdf shells out to pandoc, which
must be installed separately (pip install promptukit does not install
pandoc). The .md file is always kept — it is the editable intermediate.
You can also call the public API directly:
from promptukit.exams.create_exam_md import build_exam_md
# Returns the Path to the written .md file
build_exam_md("bank.json", "exam.md", answers="key")
# With metadata dict
build_exam_md(data, "exam.md", metadata={"title": "Midterm 1"}, answers="none")
Supported answer modes:
--answers |
Output |
|---|---|
none (default) |
No answers — student-facing exam |
inline |
Answer printed after each question |
key |
Numbered answer key appended at the end |
All question types are supported: MultipleChoice, TrueFalse,
ShortAnswer, FillInTheBlank, Matching, Calculation.
Student-facing Markdown uses scannable answer markers: open circles for
choose-one questions and empty boxes for choose-multiple style records. The
legacy default Multiple Choice Examination subtitle is not printed unless a
metadata/setup file explicitly sets a different exam_type.
Create pub quiz PDF
The create_pub_quiz.py script generates a pub-quiz style group trivia PDF,
with one printable sheet per round so a grader can split a stack of rounds
and score them in parallel. Each sheet carries its own team-name / date /
score header.
Usage (from the repository root with Poetry):
poetry run python -m promptukit.exams.create_pub_quiz \
-q promptukit/data/question_banks/pub-quiz-sample.json \
-o pub_quiz.pdf
# With custom metadata (title, host, instructions, labels)
poetry run python -m promptukit.exams.create_pub_quiz \
-q my_quiz.json -m my_quiz_meta.json -o pub_quiz.pdf
# Extract an editable subset and setup file, then render from them
poetry run question-bank extract \
--src promptukit/data/question_banks/pub-quiz-sample.json \
--dest pub_quiz_questions.json \
--setup-dest pub_quiz_setup.json \
--artifact-kind pub_quiz \
-f
poetry run python -m promptukit.exams.create_pub_quiz \
-q pub_quiz_questions.json \
-m pub_quiz_setup.json \
-o pub_quiz.pdf
Input JSON layout — top-level rounds (aliases: sections, categories):
{
"title": "JRB Industries Pub Quiz",
"rounds": [
{
"title": "Motorsport",
"theme": "Open wheel, closed wheel, and everything in between.",
"questions": [
{"prompt": "Which series uses the Dallara IR-18 as its spec chassis?"},
{"prompt": "Which flag color signals an F1 race is stopped?",
"choices": ["Yellow", "Blue", "White", "Red"]},
{"prompt": "True or false: a full-course yellow bunches the field.",
"question_type": "TrueFalse"}
]
}
]
}
A flat questions list with a round or category key per item is also
grouped into rounds. Questions without choices render as free-answer
(blank line); questions with choices render as multiple-choice (team
writes the letter); question_type: "TrueFalse" renders T / F.
See promptukit/data/question_banks/pub-quiz-sample.json for a 3-round example.
Create PPTX deck
The create-pptx console script generates a PowerPoint deck from a question
bank. It accepts the same section-based and flat question layouts as the exam
PDF generator.
Usage (from the repository root with Poetry):
# Generate one slide per question
poetry run create-pptx \
-q promptukit/data/question_banks/block-doku-sample.json \
-o questions.pptx
# Add answer reveal slides after each question
poetry run create-pptx \
-q promptukit/data/question_banks/block-doku-sample.json \
-o questions_with_answers.pptx \
--answers after
# Save reproducible artifacts while creating the deck
poetry run create-pptx \
-q promptukit/data/question_banks/block-doku-sample.json \
--save-questions pptx_questions.json \
--save-setup pptx_setup.json \
-o questions.pptx
After installing the package or activating the Poetry virtualenv, the same
commands can be run without the poetry run prefix.
Question types
Beyond multiple-choice (MultipleChoice), add-question (interactive or
batch mode) and validate-question accept these non-MCQ types:
TrueFalse- booleananswerShortAnswer- free-textanswerFillInTheBlank-promptwith[blank]placeholders + orderedanswersMatching- orderedpairsas[left, right]Calculation- numericanswerwith optionaltoleranceandunit
See promptukit/data/question_banks/mixed-types-sample.json
for one of each. The OO model lives in promptukit.questions.question_models.
Bundled Claude Code slash commands
The package ships canonical add-trivia and audit-trivia slash-command
prompts under promptukit.claude_commands. Use them via the
promptukit-claude-commands CLI:
promptukit-claude-commands list # show available command names
promptukit-claude-commands show add-trivia # print markdown to stdout
promptukit-claude-commands install # copy into ./.claude/commands/
promptukit-claude-commands install --dest ~/.claude/commands # user-level install
# Equivalent direct alias (no subcommand needed):
promptukit-claude-commands-install # same as `... install`
promptukit-claude-commands-install --dest ~/.claude/commands
For local development, scripts/sync_claude_commands.py mirrors the same
files into the repo's .claude/commands/ directory and supports --check
for CI drift detection.
PATH note: pip install promptukit inside a Poetry-managed virtualenv
puts the script on the venv's PATH only — the bare promptukit-claude-commands
command will not resolve from a fresh shell. Pick one:
# 1. Prefix every call (no install changes)
poetry run promptukit-claude-commands install --dest ~/.claude/commands
# 2. Activate the venv first using one of the commands above
promptukit-claude-commands install --dest ~/.claude/commands
# 3. Install globally so the command is on PATH everywhere
pipx install promptukit # recommended
pip install --user promptukit # alternative
Example files
-- Example section-based bank: promptukit/data/question_banks/crb-water-management-sample.json -- Mixed question-type sample: promptukit/data/question_banks/mixed-types-sample.json -- JSON Schema describing accepted layouts: promptukit/data/question_banks/question_schema.json
Behavior notes
- If no
-q/--questionsfile is provided to the exam generator, the script falls back to the built-in hard-coded 60-question exam and preserves its original 8-section breakdown. - When you provide a section-based JSON file the PDF's section headings will
be taken from each section's
title(orname/labelif present). When you provide a flat list withcategoryfields, the loader will group questions by category to build sections automatically.
Running Tests
The test suite lives under dev/checks. The file
dev/checks/test_question_tool.py contains unit tests that exercise the
question-bank helpers and CLI-style interfaces.
Run the tests:
Using Poetry (recommended):
poetry install
poetry run pytest -q
Or run a single file directly:
poetry run pytest dev/checks/test_question_tool.py -q
Notes:
- Tests use pytest's
tmp_pathfixture and do not modify your repository files.
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 promptukit-0.5.6.tar.gz.
File metadata
- Download URL: promptukit-0.5.6.tar.gz
- Upload date:
- Size: 160.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ad69f5551dba87c89a4895a07d2b415dd94e3457500b4301642df87434af32c6
|
|
| MD5 |
09e8d296815812e0ecc62dfa0ee605e7
|
|
| BLAKE2b-256 |
04edf8716896b2981146ec7c02a7972e0911d0ed0598d196372b9a814efa9a78
|
Provenance
The following attestation bundles were made for promptukit-0.5.6.tar.gz:
Publisher:
python-publish.yml on jrkasprzyk/promptukit
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
promptukit-0.5.6.tar.gz -
Subject digest:
ad69f5551dba87c89a4895a07d2b415dd94e3457500b4301642df87434af32c6 - Sigstore transparency entry: 1411640649
- Sigstore integration time:
-
Permalink:
jrkasprzyk/promptukit@3d2bd40c7941e6d81f636f0524a2e9bec2420647 -
Branch / Tag:
refs/tags/v0.5.6 - Owner: https://github.com/jrkasprzyk
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@3d2bd40c7941e6d81f636f0524a2e9bec2420647 -
Trigger Event:
release
-
Statement type:
File details
Details for the file promptukit-0.5.6-py3-none-any.whl.
File metadata
- Download URL: promptukit-0.5.6-py3-none-any.whl
- Upload date:
- Size: 170.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
48559b152c3be2a34b3b05d5c1c69f798fce77fe562cdaa9cfe7f69e1342f4f9
|
|
| MD5 |
125b3a05da1ede52172aa4abd07188bc
|
|
| BLAKE2b-256 |
253a87ea706e7aae875bfe05a35989153d4e2cd8f098d676e17aec92fb99e943
|
Provenance
The following attestation bundles were made for promptukit-0.5.6-py3-none-any.whl:
Publisher:
python-publish.yml on jrkasprzyk/promptukit
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
promptukit-0.5.6-py3-none-any.whl -
Subject digest:
48559b152c3be2a34b3b05d5c1c69f798fce77fe562cdaa9cfe7f69e1342f4f9 - Sigstore transparency entry: 1411640720
- Sigstore integration time:
-
Permalink:
jrkasprzyk/promptukit@3d2bd40c7941e6d81f636f0524a2e9bec2420647 -
Branch / Tag:
refs/tags/v0.5.6 - Owner: https://github.com/jrkasprzyk
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@3d2bd40c7941e6d81f636f0524a2e9bec2420647 -
Trigger Event:
release
-
Statement type: