- Implemented the schedule import feature with a dedicated page for importing schedules from the transport website. - Added a new QualityPage to check the quality of routes, stops, schedules, and notifications without making automatic changes. - Created CSS styles for both the ScheduleImportPage and QualityPage to enhance the user interface. - Introduced API calls for fetching schedule data and quality reports. - Added error handling and loading states for better user experience during data fetching. - Included functionality for filtering quality issues and displaying detailed reports.
11 KiB
Импорт расписаний автопарка
В админке: Транспорт → Импорт расписаний (/schedule-import). Раздел и его API доступны только администратору с JWT; ключ мобильного приложения доступа не даёт.
Как пользоваться
- Выбрать номер маршрута на сайте. Все направления в админке с этим номером выбираются автоматически; ненужные можно снять.
- Нажать Загрузить таблицы. Каждое направление и его упорядоченные остановки уже должны существовать в базе.
- Проверить строку отправлений сайта для каждого направления. Она выбирается автоматически по имени первой остановки; если имена отличаются, сопоставить вручную. Времена разных направлений не смешиваются. Начальная и конечная остановки берутся из порядка остановок выбранного направления.
- Выбрать дату начала. Промежуточные строки сайта показаны отдельно: например, у №2 это «Швейная фабрика» и «Автовокзал». Они сохраняются в снимке источника, но не создают рейсы и не меняют время проезда между остановками.
- Нажать Показать изменения. Для каждого направления проверить добавления, удаления и особые условия старых рейсов.
- Подтвердить просмотр и нажать Применить к выбранным направлениям. Все выбранные направления сохраняются вместе.
Для представленных в источнике типов дней расписание заменяется целиком. Старые ручные ограничения и заметки показаны среди удалений: они не переносятся автоматически на новые отправления. «Выходной день» разворачивается в субботу и воскресенье. Праздничное расписание не меняется. Отсутствующие на сайте времена промежуточных остановок не вычисляются парсером.
Предыдущая версия сохраняется до дня перед началом новой. При повторном импорте на ту же дату существующая версия обновляется, а исходный снимок сохраняется в журнале. Версии доступны через селектор в разделе Расписания. Конфликтующие будущие версии блокируют импорт, чтобы не затереть запланированные изменения.
Новости
Кнопка Проверить новости загружает текущие публикации главной страницы. Это ручной запуск, фонового опроса и обхода архивных страниц в этой версии нет. Загруженные публикации сохраняются; в интерфейсе отображаются последние 50. Полный текст с табличными временами и ссылка доступны для проверки. Дата публикации не считается датой начала изменений: её необходимо прочитать в содержимом.
Отметка Проверено не применяет отмены или переносы. Если текст новости изменится при следующей проверке, отметка сбросится. Новость никогда не подменяет основное расписание.
Ограничения первой версии
Поддерживаются обычные HTML-таблицы с блоками «Рабочий день», «Выходной день», «Суббота», «Воскресенье» и временами через |. Поддерживаются примечания у конкретного времени: «по пятницам», «только по понедельникам и средам», «кроме пятницы» и «через «Мариненко»», в том числе их сочетания. Дни недели сохраняются в daysOfWeek / exceptDaysOfWeek (ISO 1–7). Остановка «через» сопоставляется по имени с остановками направления и сохраняется в viaStopIds. Если совпадения нет или оно неоднозначно, условие сохраняется в note, а предпросмотр сообщает об отсутствии привязки. Это не добавляет остановку, геометрию или время проезда в маршрут.
Неизвестные условия, отдельные сноски, зачёркивания, неизвестные заголовки и неоднозначные строки блокируют применение всей страницы и показываются администратору. Это касается, например, отдельных разделов по пятницам и отправлений из автопарка. Их следует редактировать вручную через Расписания.
Проверка живого сайта 16.09.2026: каталог содержит 15 маршрутов. Без замечаний разобраны №1, 2, 4, 6, 8, 9, 11, 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. Не запускать все старые миграции поверх существующей базы: первые миграции проекта не идемпотентны.
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.