9.0 KiB
created, tags, updated
| created | tags | updated | ||||||
|---|---|---|---|---|---|---|---|---|
| 2026-08-24 |
|
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 вопросов). Причины потери и фикс:
- Номер правильного в нестандартной обёртке — парсер ждал только простой
<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). - Вопрос без
<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> </strong><br/>
<strong>1.2. ...</strong>
...
- Вопрос может занимать несколько подряд идущих
<strong>(фрагмент без номера — продолжение). - Ответы — текстовые узлы после
</strong>между<br/>, каждый начинается сN.. <em>N</em>— чистый номер правильного. Исключение:<em><u>Примечание</u>: …</em>— это примечание, не номер.- Мусор:
<strong> </strong>,<em> </em>, пустые<em>/<strong data-redactor-tag="strong">.
Генератор: ~/Downloads/gen2.py
Python (без внешних зависимостей), превращает origen_raw.html → bilety.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
- Блоки с
<p>-заголовком: если не вырезать<p>.../p>первым шагом, такие блоки дают 0 карточек (вопросы теряются). Симптом: в собранном файлеcardsвдвое меньше (~86 вместо ~180). - Скрытие номера: не выводить
<em>в разметку — толькоdata-correctна кнопке. grep -cпо однострочному HTML вернёт 1 (файл — одна гигантская строка), даже если совпадений сотни. Считать черезpython(s.count(...)), не через grep.- Файл после генерации большой (~894 КБ): на каждую кнопку инлайн-
onclick— приемлемо для статики. - 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) → страница подхватывается живьём, рестарт дашборда не нужен.