Files
obsidian-vault/personal/plans/ohotnichy-bilety-interaktiv.md
T

81 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Интерактивные билеты охотника (a1-tir.ru/bilety)
Проект: скачать страницу билетов для экзамена на гражданское оружие и превратить в интерактивный тренажёр.
**Возможности:** номер правильного ответа скрыт · ответы кликабельны · выбранный неправильный → красный, правильный → зелёный.
**Файлы (исходники в `~/Downloads/`):**
- `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/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`.
---
## Источник и структура (Tilda)
Страница — Tilda-лендинг, весь исходный HTML на одной строке (`a1-tir_bilety_raw.html`, строка 228 содержит контент).
Экзамен разложен по **4 блокам** `data-record-type="106"` (Tilda text-блок `div.field-text.t-text`):
- `rec465410787` — «Правовая подготовка», вопросы 1.1–1.49
- `rec1120437831` — без заголовка (продолжение правовой), 1.50–1.93
- `rec1120438216` — «Огневая подготовка», 2.1–2.41
- `rec465410788` — без заголовка (правила охоты), 58–103 · **вопросы из нескольких `<strong>`**
- Итого ~180 вопросов.
### Структура одного вопроса в DOM (проверено по сырым данным)
Каждый блок — один большой div; внутри `childNodes`:
- `<br/>` — разделители (шум)
- `<strong>Вопрос</strong>` — текст вопроса; может занимать **несколько подряд** `<strong>` (напр. вопрос 58); внутри возможен вложенный `<a>` (consultantplus)
- шапка раздела — `<p><strong>Заголовок</strong></p>` (strong вложен в p → не ловится как вопрос)
- текстовые узлы ответов: `"1. Текст"`, `"2. ..."`, `"3. ..."` (бывают ведущие пробелы)
- `<em>N</em>`**номер правильного ответа** (чистое число)
- примечание — `<em><u>Примечание</u>: …</em>` (НЕ номер, сохранить)
- шум: `<strong>&nbsp;</strong>`, `<em>&nbsp;</em>`, пустой `<em>` с `<strong data-redactor-tag>`
Контентных `<img>`/`<svg>` в блоках вопросов нет — только текст.
---
## Решение — чисто клиентский JS (без библиотек/backend)
`bilety.js` на `DOMContentLoaded` обрабатывает каждый `[data-record-type="106"] .t-text`:
**Парсер `parseQuiz` (конечный автомат по childNodes):**
- `<strong>` с непустым текстом, не `&nbsp;`: если у текущего вопроса ещё нет ответов и фаза `question`**мержим** текст (многострочный вопрос); иначе → **новый вопрос**.
- текстовый узел, матчащий `^\d+[\.\s:]` и при отсутствии заданного `correct` → ответ `{n, text}` (фаза → `answers`); иначе — продолжение последнего ответа.
- `<em>` с чистым числом → `question.correct = N` (только в фазе `answers`, чтобы не спутать с «Примечание»); с текстом → `question.note = innerHTML` (сохранить); пустой/`&nbsp;` → шум.
- `<p>` при `cur === null` и нет вопросов → `headerHTML` (заголовок раздела сохраняем).
- фильтр: оставляем только вопросы с `correct !== null`.
**Рендер `renderQuestion`:** карточка `.whale-q``.whale-q-text` (вопрос, `textContent`, жирный) + `.whale-answers` с кнопками `.whale-a` (`data-n` = номер ответа) + опционально `.whale-note`.
**Click-логика:** первый клик блокируется (`data-locked`). Если `n === correct` → класс `correct` (зелёный); иначе класс `wrong` (красный) **и** правильный ответ получает `correct` (чтобы показать верный). Управляется `data-correct` на карточке.
**Инъекция в страницу:**
- `<link rel="stylesheet" href="bilety.css">` перед `</head>`
- `<script src="bilety.js" charset="utf-8"></script>` перед `</body>`
- мета для дашборда в `<head>`: `eagle-test-purpose`, `eagle-test-contains`.
**Дизайн .whale-a:** карточки-кнопки (flex column), `#f5f5f5`, hover `#ececec`; `.correct`=`#d4edda`/`#28a745`, `.wrong`=`#f8d7da`/`#dc3545` (с `!important`).
---
## Pitfall: локальное открытие `file://`
Страница Tilda использует `sessionStorage` для анимации появления `.t-records` — при открытии локально это работает. Просто открыть `bilety.html` двойным кликом достаточно для проверки функционала.
---
## Связанное
- Доставка в дашборд — механика Pages: `personal/projects/personal-os/eagle-dashboard.md`.