feat(booth): multi-question asks — a questions list renders one form with a radio group per question and lands as one answer sidecar keyed by question
- asks.py: single {prompt, options} and multi {title, questions:[{key, prompt, options, notes?}]} both normalise to questions[]; per-question notes; every question required on submit
- /answer reads choice.<key> / notes.<key> / notes for multi; single shape unchanged
- booth asks prints per-question picks; README + CLI header; install step symlinks the CLI to ~/.local/bin; v0.1.10; 135 tests
This commit is contained in:
@@ -121,15 +121,38 @@ EOF
|
||||
curl -sf http://10.100.10.50:8090/b/r18-ab/winner.answer.json # 404 until answered
|
||||
```
|
||||
|
||||
The answer: `{"stem", "prompt", "choice", "choice_index", "label", "notes",
|
||||
**Several questions, one form.** Give the ask a `questions` list instead of
|
||||
`prompt`+`options`; the page renders one form with a radio group per question
|
||||
and a single submit, every question required. Per-question `notes: true` adds
|
||||
a small text field under that question; the form-level `notes` stays one field
|
||||
for the whole ask. The answer is keyed by question:
|
||||
|
||||
```bash
|
||||
cat > ~/booth-data/r18-ab/batch.ask.json <<'EOF'
|
||||
{"title": "R18 batch review",
|
||||
"questions": [
|
||||
{"key": "r1", "prompt": "Render 1 — keep?", "options": ["keep", "drop"], "notes": true},
|
||||
{"key": "r2", "prompt": "Render 2 — keep?", "options": ["keep", "drop"]},
|
||||
{"key": "seed", "prompt": "Reseed the batch?", "options": ["yes", "no"]}],
|
||||
"notes": true, "notes_label": "anything else"}
|
||||
EOF
|
||||
# -> batch.answer.json: {"stem", "title", "answers": {"r1": {"prompt", "choice",
|
||||
# "choice_index", "label", "notes"}, "r2": {...}, "seed": {...}}, "notes", "answered_at", "answered_by"}
|
||||
```
|
||||
|
||||
The single-question answer: `{"stem", "prompt", "choice", "choice_index", "label", "notes",
|
||||
"answered_at", "answered_by"}` — `choice` is the option id (the label itself
|
||||
for string options), `choice_index` its 0-based position, `answered_by` the
|
||||
client address. `POST /b/<name>/answer` (form fields `ask`, `choice`, `notes`)
|
||||
is what the form submits; a bad choice is a 400, an unknown stem a 404.
|
||||
client address. `POST /b/<name>/answer` is what the form submits — fields `ask` plus
|
||||
`choice` / `notes` (single) or `choice.<key>` / `notes.<key>` / `notes` (multi);
|
||||
a missing or bad choice is a 400, an unknown stem a 404.
|
||||
|
||||
Rules of the primitive:
|
||||
|
||||
- **Radio, one pick.** ≥ 2 options, ≤ 40. No multi-select (not yet asked for).
|
||||
- **Radio, one pick per question.** ≥ 2 options, ≤ 40 per question, ≤ 30
|
||||
questions per ask. No multi-select checkboxes (not yet asked for). Many asks
|
||||
per booth are fine — each is its own form and its own sidecar; use
|
||||
`questions` when the picks belong together and should land as one answer.
|
||||
- **Re-answering overwrites.** The sidecar is the *current* answer, not a log.
|
||||
The page shows the recorded answer with a collapsed *change answer* form.
|
||||
- **Open asks are flagged** — an amber `? N asks` badge on the index card and in
|
||||
@@ -293,6 +316,7 @@ Config is env in the unit (`booth.service`):
|
||||
```bash
|
||||
cd services/booth
|
||||
uv venv && uv pip install fastapi "uvicorn[standard]" jinja2 python-multipart # runtime deps
|
||||
ln -sfn "$PWD/scripts/booth" ~/.local/bin/booth # the `booth` CLI on PATH (nh3-dev has this)
|
||||
cp booth.service ~/.config/systemd/user/booth.service
|
||||
systemctl --user daemon-reload && systemctl --user enable --now booth.service
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user