61 lines
11 KiB
Markdown
61 lines
11 KiB
Markdown
# Импорт расписаний автопарка
|
||
|
||
В админке: **Транспорт → Импорт расписаний** (`/schedule-import`). Раздел и его API доступны только администратору с JWT; ключ мобильного приложения доступа не даёт.
|
||
|
||
## Как пользоваться
|
||
|
||
1. Выбрать номер маршрута на сайте. Все направления в админке с этим номером выбираются автоматически; ненужные можно снять.
|
||
2. Нажать **Загрузить таблицы**. Каждое направление и его упорядоченные остановки уже должны существовать в базе.
|
||
3. Проверить строку отправлений сайта для каждого направления. Она выбирается автоматически по имени первой остановки; если имена отличаются, сопоставить вручную. Времена разных направлений не смешиваются. Начальная и конечная остановки берутся из порядка остановок выбранного направления.
|
||
4. Выбрать дату начала. Промежуточные строки сайта показаны отдельно: например, у №2 это «Швейная фабрика» и «Автовокзал». Они сохраняются в снимке источника, но не создают рейсы и не меняют время проезда между остановками.
|
||
5. Нажать **Показать изменения**. Для каждого направления проверить добавления, удаления и особые условия старых рейсов.
|
||
6. Подтвердить просмотр и нажать **Применить к выбранным направлениям**. Все выбранные направления сохраняются вместе.
|
||
|
||
Для представленных в источнике типов дней расписание заменяется целиком. Старые ручные ограничения и заметки показаны среди удалений: они не переносятся автоматически на новые отправления. «Выходной день» разворачивается в субботу и воскресенье. Праздничное расписание не меняется. Отсутствующие на сайте времена промежуточных остановок не вычисляются парсером.
|
||
|
||
Предыдущая версия сохраняется до дня перед началом новой. При повторном импорте на ту же дату существующая версия обновляется, а исходный снимок сохраняется в журнале. Версии доступны через селектор в разделе **Расписания**. Конфликтующие будущие версии блокируют импорт, чтобы не затереть запланированные изменения.
|
||
|
||
## Новости
|
||
|
||
Кнопка **Проверить новости** загружает текущие публикации главной страницы. Это ручной запуск, фонового опроса и обхода архивных страниц в этой версии нет. Загруженные публикации сохраняются; в интерфейсе отображаются последние 50. Полный текст с табличными временами и ссылка доступны для проверки. Дата публикации не считается датой начала изменений: её необходимо прочитать в содержимом.
|
||
|
||
Отметка **Проверено** не применяет отмены или переносы. Если текст новости изменится при следующей проверке, отметка сбросится. Новость никогда не подменяет основное расписание.
|
||
|
||
## Ограничения первой версии
|
||
|
||
Поддерживаются обычные HTML-таблицы с блоками «Рабочий день», «Выходной день», «Суббота», «Воскресенье», отдельными будними днями и диапазонами (например, «Понедельник – четверг», «Пятница» у №24), и временами через `|`. Поддерживаются примечания у конкретного времени: «по пятницам», «только по понедельникам и средам», «кроме пятницы» и «через «Мариненко»», в том числе их сочетания. Дни недели сохраняются в `daysOfWeek` / `exceptDaysOfWeek` (ISO 1–7). Остановка «через» сопоставляется по имени с остановками направления и сохраняется в `viaStopIds`. Если совпадения нет или оно неоднозначно, условие сохраняется в `note`, а предпросмотр сообщает об отсутствии привязки. Это не добавляет остановку, геометрию или время проезда в маршрут.
|
||
|
||
Одинаковые отправления из непересекающихся разделов объединяются с сохранением дней выполнения. Примечание рейса дополнительно сужает дни его раздела; противоречия и повтор одного рейса в один день блокируют импорт.
|
||
|
||
Неизвестные условия, отдельные сноски, зачёркивания, неизвестные заголовки и неоднозначные строки блокируют применение всей страницы и показываются администратору. Это касается, например, нестандартных отправлений из автопарка. Их следует редактировать вручную через **Расписания**.
|
||
|
||
Проверка живого сайта 16.09.2026: каталог содержит 15 маршрутов. Без замечаний разобраны №1, 2, 4, 6, 8, 9, 11, 24, 27 и 28Э. У остальных показаны блокирующие замечания; №6 требует ручного сопоставления направлений. Разметка сайта может измениться.
|
||
|
||
Каждое направление приложения использует свой порядок остановок. Импорт сохраняет `startStopId` и `endStopId` первой и последней остановки, но не создаёт недостающие направления. Корректность порядка остановок и существующих `time_offset_minutes` нужно проверять в редакторе маршрута.
|
||
|
||
## Сохранение и защита от конфликтов
|
||
|
||
- Предпросмотр действует 30 минут; клиент не передаёт готовые времена в операцию сохранения.
|
||
- Перед применением сервер повторно получает источник и сравнивает его с просмотренным снимком.
|
||
- Снимок маршрута, остановок и расписаний проверяется внутри транзакции. Ручная правка после предпросмотра вызывает `409`.
|
||
- Изменения всех выбранных направлений, дней и отметка применения записываются одной транзакцией. Конфликт или ошибка в одном направлении отменяет весь импорт. Повторный запрос применения того же ID возвращает уже сохранённый результат.
|
||
- Триггеры `sync_schedules` фиксируют изменения для синхронизации. После успешного сохранения очищается кеш каждого выбранного маршрута.
|
||
- Публичные HTTP-ссылки сайта нормализуются в HTTPS; внешние адреса и перенаправления запрещены. Загрузка ограничена 20 секундами и 2 МиБ.
|
||
|
||
Таблицы `schedule_imports` и `schedule_import_news` создаются идемпотентной миграцией `008_schedule_imports.sql`, автоматически при первом запросе к разделу. Снимки применённых импортов хранятся в `source` и `payload` (`version: 2`, массив `plans` с `before` и `changes` для каждого направления); кнопки автоматического отката пока нет. Старые неприменённые черновики удаляются через 7 дней при следующей загрузке таблиц.
|
||
|
||
## Установка и проверка
|
||
|
||
Установить backend-зависимости (`npm install`), пересобрать frontend и перезапустить backend. При использовании Docker: `docker compose up -d --build backend frontend`. Не запускать все старые миграции поверх существующей базы: первые миграции проекта не идемпотентны.
|
||
|
||
```sh
|
||
cd backend
|
||
node --test
|
||
# Только отдельная тестовая PostgreSQL с PostGIS и ролью transit_admin:
|
||
IMPORT_TEST_DATABASE_URL=postgresql://.../import_test node --test test/scheduleImport.integration.test.js
|
||
cd ../frontend
|
||
npm run build
|
||
```
|
||
|
||
Интеграционный тест создаёт временную схему в тестовой базе и удаляет её по завершении. Проверяет совместный импорт двух направлений, выбор одного направления, конкурентное применение, повторы, изменения источника и базы, откат при сбое записи, будущую версию, журнал синхронизации и повторную проверку новостей. Дополнительно проверяется сохранение примечаний «через», дней выполнения и исключений. HTML-примеры в `backend/test/fixtures/schedule-import` извлечены из публичных страниц сайта 16.09.2026.
|