[2026-08-24] eagle: personal/plans/ohotnichy-bilety-interaktiv.md personal/tech/bilety-oxota-interaktiv.md

This commit is contained in:
Alexey Martemyanov
2026-08-24 15:47:44 +06:00
parent c8988eb058
commit 58b9e966ff
2 changed files with 31 additions and 11 deletions
@@ -5,20 +5,20 @@
**Возможности:** номер правильного ответа скрыт · ответы кликабельны · выбранный неправильный → красный, правильный → зелёный.
**Файлы (исходники в `~/Downloads/`):**
- `bilety.html`скачанная + расширенная страница (инъекция CSS/JS + метаданные)
- `bilety.js` — enhancer: парсер вопросов + click-логика
- `bilety.css` — стили подсветки (зелёный/красный)
- `a1-tir_bilety_raw.html` — оригинальная скачанная копия (667 КБ)
- `bilety.html`финальная статичная страница (кнопки вшиты в HTML, 181 карточка / 543 кнопки, ≈902 КБ); деплой в `/Library/WebServer/Documents/bilety.html`
- `origen_raw.html` — оригинальная скачанная копия (чистый источник для генерации)
- `gen2.py` — Python-генератор оригинала → `bilety.html` (статические кнопки, без JS-скрипта)
- `gen_fix.py`, `fix216.py` — точечные скрипты восстановления потерянных вопросов
- `bilety.js`, `bilety.css` — ранняя (отклонённая) версия enhancer'а на клиентском JS; в финале НЕ используются
- `a1-tir_bilety_raw.html` — оригинальная скачанная копия (альтернативное имя)
---
## Статус
- ✅ Страница скачана и переработана в интерактив.
- ⏸️ **Доставка в Eagle Dashboard Pages не завершена** — упирается в права: `/Library/WebServer/Documents/` root-owned, `sudo cp` требует пароль из неинтерактивного шелла. Нужно либо получить sudo-пароль от Alex, либо он запустит вручную:
```bash
sudo cp ~/Downloads/bilety.html ~/Downloads/bilety.js ~/Downloads/bilety.css /Library/WebServer/Documents/
```
После копирования страница появится в `http://localhost:8880/?tab=pages` под именем `bilety` (title/purpose/contains уже заполнены). Рестарт дашборда не нужен.
- **Задеплоено в Eagle Dashboard Pages**`/Library/WebServer/Documents/bilety.html` (902285 байт), URL `/local-pages/bilety.html`. Рестарт дашборда не нужен (сканируется живьём).
-**Все 181 вопрос покрыты** (1.1→1.93, 2.1→2.42, 58→103), каждая карточка ровно 3 кнопки, номер правильного скрыт, клик красит красный/зелёный.
- Изначально терялось 5 вопросов (1.93, 81, 83, 94, 2.16) из-за нестандартной обёртки `<em>` и вопроса без `<strong>`. Восстановлены скриптами `~/Downloads/gen_fix.py` и `~/Downloads/fix216.py`. Подробности — в `personal/tech/bilety-oxota-interaktiv.md`.
---
+22 -2
View File
@@ -8,14 +8,34 @@ updated: 2026-08-24
## Итог (2026-08-24)
Скачал `https://a1-tir.ru/bilety` (билеты для экзамена на гражданское оружие/охотничий минимум) и превратил в **статичную страницу с кнопками-ответами, вшитыми в HTML** (БЕЗ JS-скрипта-генератора). Задеплоено в Eagle Dashboard Pages: `/Library/WebServer/Documents/bilety.html` (≈894 КБ), URL `/local-pages/bilety.html`, вкладка Pages.
Скачал `https://a1-tir.ru/bilety` (билеты для экзамена на гражданское оружие/охотничий минимум) и превратил в **статичную страницу с кнопками-ответами, вшитыми в HTML** (БЕЗ JS-скрипта-генератора). Задеплоено в Eagle Dashboard Pages: `/Library/WebServer/Documents/bilety.html` (≈902 КБ, 902285 байт), URL `/local-pages/bilety.html`, вкладка Pages.
- **176 карточек-вопросов, 532 кнопки-ответа** (из ~180 исходных; потеряны лишь некоторые многострочные вопросы, где `<strong>`-фрагмент без номера).
- **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 сгенерированного:
```python
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 таких блока на странице). Внутри: