Skip to main content

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.

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
promptukit-gui                  # launch the browser-based authoring GUI
promptukit-claude-commands      # list/show/install bundled Claude Code slash commands

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). Note: PDF generation requires
# the `reportlab` package: `pip install reportlab`.
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_question or python -m promptukit.exams.create_exam as shown elsewhere in this README.
  • Generating PDFs requires reportlab (install with pip install reportlab).

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 reportlab
  • 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 local content/ files aren't available.

Getting started (Poetry, for development)

  1. Install Poetry (if you don't have it):

    pip install --user poetry
    
  2. Create the virtual environment and install dependencies:

    poetry install
    
  3. 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
    

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

Poetry 2.x note:

  • The poetry shell command (which previously spawned a new shell) is not installed by default in Poetry 2.0+. You can either use poetry env activate (then evaluate the printed activation command in your shell) or install the shell plugin to restore poetry shell.

Notes

  • The package entry points are defined in pyproject.toml under [tool.poetry.scripts] and map console script names to the main() functions in the modules under the promptukit package.

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

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-questions.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-questions.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-questions.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-questions.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-questions.json --dest promptukit/data/question_banks/pick.json

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:

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

-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's categories list)
  • difficulty (easy / medium / hard)
  • prompt (autosizing textarea)
  • choices (four inputs A–D, with a radio to pick which one is answer)
  • 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 questions array.

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):

# Use the built-in hard-coded exam
python -m promptukit.exams.create_exam

# Load questions from a JSON bank and write a PDF
python -m promptukit.exams.create_exam -q promptukit/data/question_banks/block-doku-questions.json -o cven4333_from_json.pdf

# Save reproducible artifacts while creating the PDF
python -m promptukit.exams.create_exam \
  -q promptukit/data/question_banks/block-doku-questions.json \
  --save-questions exam_questions.json \
  --save-setup exam_setup.json \
  -o cven4333_from_json.pdf

# With Poetry (runs the module inside the virtualenv)
poetry run python -m promptukit.exams.create_exam -q promptukit/data/question_banks/block-doku-questions.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-questions.json \
  --dest exam_questions.json \
  --categories music \
  --setup-dest exam_setup.json \
  --artifact-kind exam \
  -f

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": ["...", "..."] }, ... ]
          }
       ]
    }
    
  • categories is an alias for sections and is also accepted.

  • Flat list of questions (top-level array) or top-level object with questions array:

    {
       "questions": [ { "prompt": "...", "choices": ["...", "..."], "category": "Section title" }, ... ]
    }
    
  • Question objects support multiple common field names: prompt, q, question, or text for the question text; choices or answers for the answer options; optional category to 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 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):

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)
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

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.

Question types

Beyond multiple-choice (MultipleChoice), add-question (interactive or batch mode) and validate-question accept these non-MCQ types:

  • TrueFalse - boolean answer
  • ShortAnswer - free-text answer
  • FillInTheBlank - prompt with ___ placeholders + ordered answers
  • Matching - ordered pairs as [left, right]
  • Calculation - numeric answer with optional tolerance and unit

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

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 (then bare command works)
poetry shell
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/--questions file 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 (or name / label if present). When you provide a flat list with category fields, 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_path fixture and do not modify your repository files.

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

promptukit-0.5.2.tar.gz (152.4 kB view details)

Uploaded Source

Built Distribution

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

promptukit-0.5.2-py3-none-any.whl (162.7 kB view details)

Uploaded Python 3

File details

Details for the file promptukit-0.5.2.tar.gz.

File metadata

  • Download URL: promptukit-0.5.2.tar.gz
  • Upload date:
  • Size: 152.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for promptukit-0.5.2.tar.gz
Algorithm Hash digest
SHA256 e24381f456b8cd5cb6eab35cc4b2b1553ff3f8b69e271ccf8fd9638726b66789
MD5 9bdb39d3ceff054965828ff3bc991b93
BLAKE2b-256 e9b656cf96380506d8a6d3274219f18a87b8b950a5f5f60c9460ed322fc0639c

See more details on using hashes here.

Provenance

The following attestation bundles were made for promptukit-0.5.2.tar.gz:

Publisher: python-publish.yml on jrkasprzyk/promptukit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file promptukit-0.5.2-py3-none-any.whl.

File metadata

  • Download URL: promptukit-0.5.2-py3-none-any.whl
  • Upload date:
  • Size: 162.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for promptukit-0.5.2-py3-none-any.whl
Algorithm Hash digest
SHA256 17f9b31b937df3e8f3f5ad80bc653c7dcb46ff008eec3778491c9398403cdaff
MD5 2a0b22617ebc9ce1d00b7a7ae1920e0a
BLAKE2b-256 cf2c41ed57454e97af46b4fbc8d56ada12be9a88a1c5ed9067870a30171a8046

See more details on using hashes here.

Provenance

The following attestation bundles were made for promptukit-0.5.2-py3-none-any.whl:

Publisher: python-publish.yml on jrkasprzyk/promptukit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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