"""Inline ask placement inside a booth's VERBATIM index.html. A booth that ships its own `index.html` is served untouched, so the auto-gallery template's asks panel never renders there. The first fix was a chip linking to a separate `/asks` page; the operator's verdict on that (2026-09-09) was that the question belongs WITH the artifacts it is about — a four-voice audition wants the radio group for each voice under that voice's audio, not on another page. So the report author marks where each piece goes, with a placeholder element:
the whole ask: every question + submit
just that question's radios
the notes field + submit button Per-question fragments bind to ONE form via the HTML5 `form=` attribute, so four groups scattered down a page still submit as a single POST — which is what a multi-question ask requires (every question or 400). No JavaScript. An `` comment works the same way, for authors who would rather not put an empty div in their markup. Placement is OPTIONAL. A page with no placeholders gets the whole ask appended at the end of its body, so an ask is never invisible — that guarantee is the point, and marking it up only moves it somewhere better. """ from __future__ import annotations import re #
/ _EL_RE = re.compile( r"<(?P[A-Za-z][\w-]*)\b[^>]*?\bdata-booth-ask=\"(?P[^\"]+)\"[^>]*?>" r"(?:\s*)?", re.IGNORECASE, ) _SUBMIT_EL_RE = re.compile( r"<(?P[A-Za-z][\w-]*)\b[^>]*?\bdata-booth-ask-submit=\"(?P[^\"]+)\"[^>]*?>" r"(?:\s*)?", re.IGNORECASE, ) # / / _COMMENT_RE = re.compile(r"", re.IGNORECASE) _COMMENT_SUBMIT_RE = re.compile(r"", re.IGNORECASE) def split_spec(spec: str) -> tuple[str, str | None]: """`"anchors:lawson"` -> `("anchors", "lawson")`; `"anchors"` -> `("anchors", None)`.""" stem, sep, key = spec.strip().partition(":") return stem.strip(), (key.strip() or None) if sep else None def has_placeholders(html: str) -> bool: return bool( _EL_RE.search(html) or _SUBMIT_EL_RE.search(html) or _COMMENT_RE.search(html) or _COMMENT_SUBMIT_RE.search(html) ) def form_id(stem: str) -> str: return f"bk-ask-form-{re.sub(r'[^A-Za-z0-9_-]', '-', stem)}" def place(html: str, asks: list[dict], render) -> tuple[str, dict[str, set], set[str]]: """Substitute every placeholder with rendered ask HTML. `render(kind, ask, key)` returns the fragment for kind in {"whole", "question", "submit"}. Returns the new html; a map of stem -> the set of question keys placed inline (with `None` in the set meaning the WHOLE ask was placed); and the set of stems whose submit block was placed explicitly. The caller needs the per-key detail, not just "this stem appeared somewhere": a multi-question ask requires EVERY question on submit, so a page that marks up two of four questions must still be handed the other two or the form is unsubmittable — a 400 the operator would meet only after filling it in. A placeholder naming an ask this booth does not have is left ALONE, not blanked: silently eating the author's markup would hide a typo'd stem, and an untouched empty div is invisible anyway. """ by_stem = {a["stem"]: a for a in asks} placed: dict[str, set] = {} submitted: set[str] = set() def sub_main(m: re.Match) -> str: stem, key = split_spec(m.group("spec")) ask = by_stem.get(stem) if ask is None: return m.group(0) if key is None: placed.setdefault(stem, set()).add(None) submitted.add(stem) return render("whole", ask, None) q = next((q for q in ask.get("questions", []) if q.get("key") == key), None) if q is None: return m.group(0) placed.setdefault(stem, set()).add(key) return render("question", ask, key) def sub_submit(m: re.Match) -> str: stem, _ = split_spec(m.group("spec")) ask = by_stem.get(stem) if ask is None: return m.group(0) placed.setdefault(stem, set()) submitted.add(stem) return render("submit", ask, None) for pat, fn in ((_EL_RE, sub_main), (_COMMENT_RE, sub_main), (_SUBMIT_EL_RE, sub_submit), (_COMMENT_SUBMIT_RE, sub_submit)): html = pat.sub(fn, html) return html, placed, submitted