Metadata-Version: 2.4
Name: promptukit
Version: 0.5.4
Summary: Utilities for building question banks and generating exam documents.
License: MIT
License-File: LICENSE
Keywords: questions,exams,education,pdf,question-bank
Author: Joseph Kasprzyk
Author-email: jkasprzyk@gmail.com
Requires-Python: >=3.12,<4.0
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Education
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Topic :: Education
Requires-Dist: build (>=1.4.3,<2.0.0)
Requires-Dist: nicegui (>=3.10.0,<4.0.0)
Requires-Dist: parasolpy (>=0.1.1,<0.2.0)
Requires-Dist: python-pptx (>=1.0.2,<2.0.0)
Requires-Dist: reportlab (>=4.4.10,<5.0.0)
Requires-Dist: twine (>=6.2.0,<7.0.0)
Project-URL: Homepage, https://github.com/jrkasprzyk/promptukit
Project-URL: Repository, https://github.com/jrkasprzyk/promptukit
Description-Content-Type: text/markdown

PromptuKit
==========

Utilities for building and managing multiple-choice question banks and
generating exam PDFs and slide decks.

Install from PyPI
-----------------

```bash
pip install promptukit
```

The package is published at https://pypi.org/project/promptukit/. After
installing you get the CLI entry points on your PATH:

```bash
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
```

You can also import the library in Python or a Jupyter notebook:

```python
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.

```python
# 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_question` or
  `python -m promptukit.exams.create_exam` as shown elsewhere in this README.
- PDF generation uses `reportlab`, which is installed automatically with
  `promptukit`.

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:

```python
!pip install promptukit
```

- To run the repository version (latest changes), clone and install from GitHub:

```python
!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):

   ```bash
   pip install --user poetry
   ```

2. Use a compatible Python interpreter (`>=3.12,<4.0`) and create the virtual
   environment with dependencies:

   ```bash
   poetry install
   ```

3. Run the CLI tools via Poetry (console scripts / entry points):

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

```powershell
.\.venv\Scripts\Activate.ps1
```

If script execution is blocked, temporarily allow it then activate:

```powershell
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
.\.venv\Scripts\Activate.ps1
```

Command Prompt (cmd.exe):

```cmd
.\.venv\Scripts\activate.bat
```

Git Bash / MSYS (Windows):

```bash
source .venv/Scripts/activate
```

macOS / Linux (POSIX):

```bash
source .venv/bin/activate
```

Alternatives (no manual activation required):

```bash
# 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 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):

```bash
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:

```bash
# 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:

```bash
# 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:

```bash
# 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):

```bash
# 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:

```bash
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:

```bash
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:

```bash
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:

```bash
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:

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

```json
{
  "_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):

```bash
# 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:

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

  ```json
  {
     "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:

  ```json
  {
     "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 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:

```bash
# 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](https://pandoc.org/installing.html), 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:

```python
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`.

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

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

```json
{
  "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](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):

```bash
# 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` - boolean `answer`
- `ShortAnswer` - free-text `answer`
- `FillInTheBlank` - `prompt` with `[blank]` 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](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:

```bash
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:

```bash
# 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](promptukit/data/question_banks/crb-water-management-sample.json)
-- Mixed question-type sample: [promptukit/data/question_banks/mixed-types-sample.json](promptukit/data/question_banks/mixed-types-sample.json)
-- JSON Schema describing accepted layouts: [promptukit/data/question_banks/question_schema.json](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):

```bash
poetry install
poetry run pytest -q
```

Or run a single file directly:

```bash
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.

