Files
obsidian-vault/personal/tech/bilety-oxota-interaktiv.md
T

9.0 KiB
Raw Blame History

created, tags, updated
created tags updated
2026-08-24
html
quiz
tilda
static-page
python
eagle-dash
2026-08-24

Билеты охотника — статичный интерактив (a1-tir.ru/bilety → bilety.html)

Итог (2026-08-24)

Скачал https://a1-tir.ru/bilety (билеты для экзамена на гражданское оружие/охотничий минимум) и превратил в статичную страницу с кнопками-ответами, вшитыми в HTML (БЕЗ JS-скрипта-генератора). Задеплоено в Eagle Dashboard Pages: /Library/WebServer/Documents/bilety.html (≈902 КБ, 902285 байт), URL /local-pages/bilety.html, вкладка Pages.

  • 181 карточка-вопрос, 543 кнопки-ответавсе вопросы покрыты (1.1→1.93, 2.1→2.42, 58→103), каждая карточка ровно 3 кнопки. Нет дублей и пропусков.
  • Каждый ответ — <button class="whale-a" data-correct="N" data-n="M" onclick="...">.
  • Номер правильного ответа скрыт — живёт только в data-correct, не рендерится на экран.
  • Клик (инлайн onclick в каждой кнопке): выбрал неверно → кнопка красная wrong + правильная подсвечивается зелёной correct; верно → зелёная.
  • CSS (.whale-answers, .whale-a, .correct, .wrong) вшит в <head> страницы.

Восстановление потерянных вопросов (2026-08-24, исправление)

Изначально генератор давал 176 карточек (терял 5 вопросов). Причины потери и фикс:

  1. Номер правильного в нестандартной обёртке — парсер ждал только простой <em>N</em>, а у части вопросов был <strong><em data-redactor-tag="em">N</em></strong> (вопросы 81, 83, 94) или <p style="text-align:center;"><em>N</em></p> (1.93, последний в разделе). Вопрос без распознанного correct отбрасывался (flush() требует cur["c"] is not None).
  2. Вопрос без <strong>-обёртки — вопрос 2.16 («Отдачей оружия называется:») в оригинале записан голым текстом «2.16. …» без <strong>. Парсер не распознал его как новый вопрос и склеил его ответы с карточкой 2.15 (у 2.15 вышло 7 кнопок вместо 3, а 2.16 вообще пропал как отдельный вопрос).

Как исправлено (точечные скрипты в ~/Downloads/, без переписывания gen2):

  • gen_fix.py — вставляет 4 карточки (1.93, 81, 83, 94) в правильные позиции после своих предшественников (по поиску границ соседних карточек <div class="whale-q">, сортировка вставок по убыванию позиции, чтобы не сдвигать индексы).
  • Дальше обнаружен и 2.16 → fix216.py заменяет сегмент карточки 2.15 (по абсолютным индексам от находки 2.15. до начала 2.17.) на корректные 2.15 (3 кнопки) + 2.16 (3 кнопки, correct=2).

Pitfall при точечной вставке: первый вариант regex-патча (.*?</div>...) оказался слишком жадным и удалил соседние карточки (181→155). Надёжно — по границам: карточки в файле идут подряд без разделителя, конец одной = начало следующей <div class="whale-q">. Вставлять по позициям, а не regex-далящим патчем.

Как проверял полноту (главный урок): не полагаться на «~180», а сверять множества номеров оригинала vs сгенерированного:

orig_nums = set(...)  # номера из <strong>N. и голых N. в оригинале
gen_nums = set(re.findall(r'whale-q-text">(\d+(?:\.\d+)?\.)\s', gen))
missing = orig_nums - gen_nums

Номера «1.», «2.», «3.» в результатах — это ложные вхождения (номера ответов, не вопросов), их отфильтровывать по наличию точки-разделителя ('.' in n).

Универсальная структура исходника (Tilda text-блок)

Одна строка-контейнер на раздел, div field="text" class="t-text t-text_md "> (4 таких блока на странице). Внутри:

<p style="text-align:center;"><strong>Заголовок раздела</strong></p>  (не всегда)
<strong>1.1. Текст вопроса</strong>
<br/>1. Вариант1
<br/>2. Вариант2
<br/>3. Вариант3
<br/><em>2</em>            ← номер правильного (скрывать)
<br/><strong>&nbsp;</strong><br/>
<strong>1.2. ...</strong>
...
  • Вопрос может занимать несколько подряд идущих <strong> (фрагмент без номера — продолжение).
  • Ответы — текстовые узлы после </strong> между <br/>, каждый начинается с N. .
  • <em>N</em> — чистый номер правильного. Исключение: <em><u>Примечание</u>: …</em> — это примечание, не номер.
  • Мусор: <strong>&nbsp;</strong>, <em>&nbsp;</em>, пустые <em>/<strong data-redactor-tag="strong">.

Генератор: ~/Downloads/gen2.py

Python (без внешних зависимостей), превращает origen_raw.htmlbilety.html.

python3 gen2.py   # вывод: containers=4 cards=176 buttons=532 bytes=816979

Ключевые моменты:

  • find_end(html, start) — баланс <div>/</div> от индекса открывающего <div field="text"> до его закрытия.
  • transform_containerсначала вырезает <p[^>]*>.*?</p> (заголовки разделов). Это критично: без этого блоки с <p>-заголовком (раздел «Правовая подготовка» и «Огневая подготовка») давали 0 карточек, вопросы терялись целиком. Убирание заголовков заставляет обрабатывать каждый <strong> единообразно (как в блоках без заголовка).
  • _scan(inner) — токенизация текста/тегов; каждый N. -ответ → cur["a"]; <em>-цифра → cur["c"] (правильный); strong без номера при пустых ответах → мержится в текущий вопрос.
  • _btn(num, text, correct) — инлайн onclick, красящий красный/зелёный.

Pitfalls

  1. Блоки с <p>-заголовком: если не вырезать <p>.../p> первым шагом, такие блоки дают 0 карточек (вопросы теряются). Симптом: в собранном файле cards вдвое меньше (~86 вместо ~180).
  2. Скрытие номера: не выводить <em> в разметку — только data-correct на кнопке.
  3. grep -c по однострочному HTML вернёт 1 (файл — одна гигантская строка), даже если совпадений сотни. Считать через python (s.count(...)), не через grep.
  4. Файл после генерации большой (~894 КБ): на каждую кнопку инлайн-onclick — приемлемо для статики.
  5. Node парсеры/node --max-old-space-size тут НЕ нужны — на больших минифицированных Tilda-файлах node легко падает в OOM (heap limit) при самопальных токенизаторах. Python + str.find/regex надёжнее и не жрёт память.

Деплой в Eagle Dashboard Pages

  • Механика + права — в personal/projects/personal-os/eagle-dashboard.md (секция «Как добавить страницу в Pages»).
  • Скопировать bilety.html в /Library/WebServer/Documents/ (root-owned, нужен sudo) → страница подхватывается живьём, рестарт дашборда не нужен.