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

7.2 KiB
Raw Blame History

Интерактивные билеты охотника (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.