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:
+21
-8
@@ -82,6 +82,7 @@ from booth.asks import ( # noqa: E402
|
||||
is_answer_file,
|
||||
is_ask_file,
|
||||
list_asks,
|
||||
load_ask,
|
||||
valid_stem,
|
||||
write_answer,
|
||||
)
|
||||
@@ -654,18 +655,30 @@ def create_app(
|
||||
)
|
||||
|
||||
@app.post("/b/{name}/answer")
|
||||
def booth_answer(request: Request, name: str, ask: str = Form(...), choice: str = Form(...),
|
||||
notes: str = Form("")):
|
||||
"""Record the operator's answer to one ask: validates the choice
|
||||
against the ask's options and writes `<stem>.answer.json` atomically.
|
||||
Re-submitting overwrites — the sidecar is the current answer. 404 for
|
||||
an unknown/invalid stem, 400 for a choice the ask does not offer."""
|
||||
async def booth_answer(request: Request, name: str):
|
||||
"""Record the operator's answer to one ask: validates every choice
|
||||
against the ask and writes `<stem>.answer.json` atomically.
|
||||
Re-submitting overwrites — the sidecar is the current answer.
|
||||
|
||||
Form fields: `ask` (stem); single-question → `choice` + `notes`;
|
||||
multi-question → `choice.<key>` per question, optional `notes.<key>`,
|
||||
plus the form-level `notes`. 404 for an unknown/invalid stem, 400 for
|
||||
a missing choice or one the ask does not offer.
|
||||
"""
|
||||
booth = resolve_booth(name)
|
||||
if not valid_stem(ask) or not (booth / f"{ask}{ASK_SUFFIX}").is_file():
|
||||
form = await request.form()
|
||||
ask = form.get("ask")
|
||||
if not isinstance(ask, str) or not valid_stem(ask) or not (booth / f"{ask}{ASK_SUFFIX}").is_file():
|
||||
raise HTTPException(status_code=404, detail="no such ask")
|
||||
who = request.client.host if request.client else ""
|
||||
try:
|
||||
write_answer(booth, ask, choice, notes, who=who)
|
||||
spec = load_ask(booth, ask)
|
||||
if spec["multi"]:
|
||||
choice = {q["key"]: form.get(f"choice.{q['key']}") for q in spec["questions"]}
|
||||
qnotes = {q["key"]: form.get(f"notes.{q['key']}") for q in spec["questions"]}
|
||||
write_answer(booth, ask, choice, form.get("notes", ""), who=who, qnotes=qnotes)
|
||||
else:
|
||||
write_answer(booth, ask, form.get("choice"), form.get("notes", ""), who=who)
|
||||
except AskError as exc:
|
||||
raise HTTPException(status_code=400, detail=str(exc))
|
||||
return RedirectResponse(url=f"/b/{quote(name, safe='')}/#ask-{quote(ask, safe='')}", status_code=303)
|
||||
|
||||
Reference in New Issue
Block a user