--- type: brd work_item_id: ET-009 title: "BRD: Новые источники GPS-треков — EnduroRussia и Wikiloc" version: 1 status: draft created_at: 2026-06-01 updated_at: 2026-06-01 authors: - "agent:analyst" related: - "ET-008" --- # BRD — ET-009: Новые источники GPS-треков — EnduroRussia и Wikiloc ## 1. Цель Расширить пул реальных GPS-треков, видимых пользователю Enduro Trails, за счёт **двух новых источников** — `endurorussia.ru` и `wikiloc.com`. Pipeline сбора, БД, API и UI-слой уже построены в **ET-008**; ET-009 **не строит инфраструктуру**, а: 1. **Активирует EnduroRussia.ru** как источник в продакшне (parser-код и ADR-010 уже готовы, но source находится в `gps_sources.yaml` как `enabled: false`; конфиг ссылается на `enduro-russia.ru` с дефисом — расхождение с реальным доменом `endurorussia.ru` без дефиса требует корректировки). 2. **Включает Wikiloc** как новый источник: добавляет запись в `gps_sources.yaml`, привязывает к регионам, проверяет parser/lifecycle/ratelimit и активирует. 3. Гарантирует, что после первого продакшн-прогона в БД `data/gps_tracks.sqlite` появляются треки с обоих новых источников и они корректно отдаются пользователю через существующие endpoints и UI-фильтры. ET-009 — **«заявить, подключить, доказать что работает»**, а не новая функциональность. ## 2. Контекст - **ET-008** разработал и развернул в test: - `src/api/gps_tracks/` (модели, БД, дедуп, MVT, endpoint, parsers). - Pipeline `scripts/gps_collect.py` с поддержкой нескольких источников. - Конфиги `config/gps_sources.yaml` и `config/gps_regions.yaml`. - UI: чекбокс «Публичные треки», sheet фильтров, popup трека, halo-слой на спутнике. - ADR-009/010/011/012 (licensing OSM / EnduroRussia / ttrails / Wikiloc). - На момент старта ET-009: - `osm` — `enabled: true`, работает в проде. - `ttrails` — `enabled: false`, в задаче ET-009 не активируется. - `enduro_russia` — parser-код есть, ADR-010 `accepted`, но `gps_sources.yaml` содержит `enabled: false` и URL `enduro-russia.ru` (с дефисом). Реальный домен по бизнес-требованию — `endurorussia.ru` (без дефиса), это подтверждает и parser-код (`src/api/gps_tracks/sources/enduro_russia.py` default `https://endurorussia.ru`). - `wikiloc` — parser-код есть, ADR-012 `accepted`, но в `gps_sources.yaml` **отсутствует**. - API EnduroRussia: открытый JSON, без авторизации, 305+ треков по РФ: - `GET https://endurorussia.ru/api/tracks?page=N&limit=50` - `GET https://endurorussia.ru/api/tracks/{id}/gpx` - Wikiloc: публичного API нет, доступ только через HTML-парсинг страниц поиска и треков; rate-limit жёсткий — 10 сек между запросами; при 403/429 — graceful-stop. ## 3. Scope ### In scope | # | Функция | | ----- | ------------------------------------------------------------------------------------------------------ | | F-01 | Исправление `gps_sources.yaml`: `enduro_russia.base_url` → `https://endurorussia.ru` (без дефиса). | | F-02 | `gps_sources.yaml`: `enduro_russia.enabled` → `true`. | | F-03 | Верификация ADR-010 (`accepted`) на момент активации — pipeline-guard должен пропустить source. | | F-04 | Добавление в `gps_sources.yaml` записи `wikiloc` с `enabled: true`, `rate_limit_sec: 10`, `license_adr: docs/work-items/ET-008/06-adr/ADR-012-wikiloc-licensing.md`. | | F-05 | Обновление `config/gps_regions.yaml`: `tsfo_plus_chuvashia.sources` дополняется значением `wikiloc` (osm уже есть, enduro_russia уже есть). | | F-06 | Интеграционные тесты на parser `enduro_russia.py` с фикстурами реальных ответов API: 1 страница списка + 3 GPX-файла + edge cases. | | F-07 | Интеграционные тесты на parser `wikiloc.py` с фикстурами реальных HTML-страниц: страница поиска, страница трека, GPX. | | F-08 | Тесты dedup-merge на пару (osm-трек, enduro_russia-трек) с одной поездкой → одна запись с `sources=['osm','enduro_russia']`. | | F-09 | Тесты graceful-stop wikiloc на 403/429: парсер останавливается, не падает, `pipeline_runs.status='partial'` или `'rate_limited'`. | | F-10 | Health-эндпоинт `/api/gps-tracks/health` после прогона показывает `tracks_by_source` с ненулевыми значениями для `enduro_russia` и `wikiloc`. | | F-11 | UI: фильтр «Источник» в `#sheet-gps-filters` динамически отображает 3 чекбокса — OSM, EnduroRussia, Wikiloc — по данным API. | | F-12 | Атрибуция: в правом нижнем углу карты MapLibre Attribution содержит «EnduroRussia.ru» и «© Wikiloc contributors» при наличии треков из этих источников. | | F-13 | Цветовая палитра по источнику в `style.json`/`style-dark.json` содержит цвета для `enduro_russia` и `wikiloc` (а не только OSM). | | F-14 | Первый продакшн-прогон pipeline на test-сервере для региона `tsfo_plus_chuvashia`: собирает ≥ 200 треков с EnduroRussia и пробует Wikiloc (любое ненулевое количество приемлемо ввиду rate-limit). | ### Out of scope - **Активация ttrails** (Тропинки.ру) — отдельный work item. - **Изменение схемы БД** — структура `gps_tracks.sqlite` остаётся как в ET-008. - **Новые поля метаданных** — что собираем по каждому треку, определено ET-008. - **Wikiloc Premium / OAuth** — пользуемся только публичными HTML. - **Расширение алгоритма дедупликации** — берём как есть из ET-008. - **Запуск автоматического cron** — расписание cron включается отдельным task'ом после успешного ручного прогона (см. F-14). ET-009 ограничивается ручным `python scripts/gps_collect.py --region tsfo_plus_chuvashia`. - **Удаление stale-треков** (GC) — отдельный концерн pipeline, не активируется в ET-009. - **Расширение на новые регионы** — Северный Кавказ остаётся `enabled: false`. ## 4. Источники — детальное описание ### 4.1 EnduroRussia.ru | Параметр | Значение | | -------------------------- | ----------------------------------------------------------------------------------- | | Тип доступа | Публичный JSON API без авторизации | | Базовый URL | `https://endurorussia.ru` | | Endpoint list | `GET /api/tracks?page=&limit=50` → `{items: [{id, name, difficulty, …}], total}` | | Endpoint GPX | `GET /api/tracks/{id}/gpx` → GPX 1.1 XML | | Объём | ≥ 305 публичных треков (на момент составления BRD) | | География | Россия, преимущественно ЦФО, эндуро-категория | | Активность | enduro, мото, hard, soft, тур → MAPPING → `enduro`/`moto` | | ToS | Публичные треки; нет явного запрета на программный доступ; см. ADR-010 | | robots.txt | Не запрещает `/api/` для программного доступа с явным UA (см. ADR-010 §2) | | Attribution | «EnduroRussia.ru» в строке атрибуции карты | | Rate-limit | 5 сек между запросами (`rate_limit_sec: 5`) | | save_user_field | `false` — автор не сохраняется (ADR-010 §3) | ### 4.2 Wikiloc | Параметр | Значение | | -------------------------- | ----------------------------------------------------------------------------------------------------------------- | | Тип доступа | Парсинг публичных HTML-страниц (API недоступно) | | Базовый URL | `https://www.wikiloc.com` | | Endpoint поиска | `GET /wikiloc/find.do?act=&sw=&ne=&page=` → HTML с `` | | Endpoint трека | `GET /trails//` → HTML c ссылкой на GPX | | Endpoint GPX | `GET /wikiloc/downloadTrail.do?id=` → GPX XML | | Активности (act код) | motorcycle=19, enduro=19, mtb=3 | | ToS | Треки публичные; ADR-012 фиксирует условия некоммерческого использования | | robots.txt | Не запрещает страницы треков с явным UA (см. ADR-012 §2) | | Attribution | «© Wikiloc contributors» в строке атрибуции карты | | Rate-limit | **10 сек** между запросами (`rate_limit_sec: 10`) — жёстко | | Graceful-stop | При HTTP 403/429 — немедленный stop без ретраев, статус прогона `rate_limited` или `partial` | | Хрупкость | HTML-парсер. При смене структуры — парсер вернёт 0 треков без краша. См. риск R-1. | | save_user_field | `false` — автор не сохраняется (ADR-012 §5) | ### 4.3 Контроль licensing Pipeline-guard `_check_license_adr()` уже реализован (см. `scripts/gps_collect.py` строки 37–73): при `enabled: true` source загружается только если `license_adr.status == 'accepted'`. Перед активацией ET-009 **обязательно перечитать** ADR-010 и ADR-012 и убедиться, что обе ADR имеют `status: accepted` в YAML front-matter. Если на момент работы ET-009 одна из ADR оказалась в другом статусе — работу остановить, эскалировать архитектору. ## 5. Метрики успеха | Метрика | Критерий | | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------- | | Конфиг корректен | `gps_sources.yaml` содержит запись `enduro_russia` с `base_url: https://endurorussia.ru` (без дефиса) и `enabled: true`. | | Wikiloc заведён | `gps_sources.yaml` содержит запись `wikiloc` с `enabled: true`, `rate_limit_sec: 10`, `license_adr: …ADR-012…`. | | Регион подписан | `gps_regions.yaml` для `tsfo_plus_chuvashia` содержит `wikiloc` в `sources`. `enduro_russia` уже подписан. | | Pipeline-guard работает | При `status: proposed` в ADR-010 (искусственно) — pipeline пропускает source с `pipeline_runs.status='skipped_license'`. | | Покрытие EnduroRussia | После прогона: `tracks_by_source.enduro_russia ≥ 200` (исходим из ≥ 305 публичных треков с учётом фильтра bbox региона). | | Покрытие Wikiloc | После прогона: `tracks_by_source.wikiloc ≥ 1` (rate-limit 10 сек × ≥ 3 запроса на трек делает сбор медленным; любое ненулевое значение приемлемо для validation того, что парсер работает end-to-end). | | Дедупликация работает | Среди ≥ 200 треков EnduroRussia: записи с `sources=['osm','enduro_russia']` или `sources=['enduro_russia','wikiloc']` существуют (хотя бы 1 в выборке). | | Graceful-stop | Mock-эмуляция HTTP 403 / 429 от Wikiloc в integration-тесте → pipeline не падает, статус прогона `rate_limited` или `partial`. | | Атрибуция | В правом нижнем углу карты после включения слоя видны строки «EnduroRussia.ru» и «© Wikiloc contributors». | | UI-фильтр источников | В `#sheet-gps-filters` после первого прогона видны минимум 3 чекбокса: OSM / EnduroRussia / Wikiloc; снятие галки с источника убирает соответствующие линии. | | Производительность не деградировала | `/api/gps-tracks?bbox=…` p95 не вырос относительно ET-008 baseline (≤ 300 мс на z ≥ 10, ≤ 500 треков в bbox). | | Чистый health | `/api/gps-tracks/health` возвращает `last_run_status='ok'` или `'partial'` (не `'error'`), `errors_count == 0` или ≤ 5%. | ## 6. Риски | # | Риск | Вероятность | Влияние | Митигация | | --- | ----------------------------------------------------------------------------------- | ----------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | R-1 | Wikiloc меняет HTML → парсер возвращает 0 треков | Высокая | Среднее | Парсер уже спроектирован graceful: возвращает 0, не падает. Health-эндпоинт показывает 0 в `tracks_by_source.wikiloc` → видимый сигнал. | | R-2 | Wikiloc банит IP mva154 | Средняя | Высокое | Rate-limit 10 сек + UA с контактом + graceful-stop на 403/429. После активации мониторим первые 3 прогона; при систематических 403 — `enabled: false` и эскалация. | | R-3 | EnduroRussia API меняет схему ответа | Низкая | Среднее | Parser проверяет наличие ключевых полей (`items`, `id`); при KeyError — `tracks_new=0`, статус `error`. Контрактный тест на JSON. | | R-4 | Расхождение конфига `enduro-russia.ru` vs реального `endurorussia.ru` | Случилось | Высокое | F-01: исправляем `gps_sources.yaml` сразу. Регрессионный тест: parser отвечает на `https://endurorussia.ru` (не на `enduro-russia.ru`). | | R-5 | EnduroRussia треки уже содержат `creator=Wikiloc` в GPX → массовые дубли при включении Wikiloc | Высокая | Среднее | ADR-012 §4 явно фиксирует. Тест dedup-merge: одна и та же поездка из enduro_russia и wikiloc → одна запись, `sources` объединён. | | R-6 | Cron первого прогона превышает окно (≥ 6 часов из-за rate-limit Wikiloc 10 сек × 305 EnduroRussia × 3 запроса/трек) | Средняя | Низкое | EnduroRussia: 305 треков × 5 сек ≈ 25 минут — окей. Wikiloc: per-source максимум `max_tracks_per_run: 50` в первом прогоне (cap в конфиге). | | R-7 | UI-фильтр «Источник» не подхватывает новые ID | Низкая | Среднее | UI динамически строит фильтр из API (`/api/gps-tracks?stats=true` или из выгрузки) — изменений в коде клиента не требуется. Проверка через UI-тест TC-UI-04 (расширен в ET-009). | | R-8 | Цветовая палитра в стилях карты не содержит `enduro_russia`/`wikiloc` → линии серым | Высокая | Низкое | F-13: добавить цвета в `style.json`/`style-dark.json` (match-expression `line-color` по `get source`). | | R-9 | Дамп БД (если есть резервная копия с старым `enduro-russia.ru` URL в `external_url`) — orphan-записи | Низкая | Низкое | До первого прогона новой версии: оператор может выполнить `UPDATE tracks SET external_urls_json = REPLACE(external_urls_json, 'enduro-russia.ru', 'endurorussia.ru')`. Опционально, в `14-deploy-log.md`. | | R-10| ADR-010 / ADR-012 регрессировали в `proposed` | Низкая | Высокое | F-03: pre-check на момент активации. Если ADR не accepted — задача останавливается, эскалация архитектору. | ## 7. Зависимости ### Backend - `src/api/gps_tracks/sources/enduro_russia.py` — **код существует** (ET-008). Изменения возможны только при выявлении бага во время тестов F-06/F-08. - `src/api/gps_tracks/sources/wikiloc.py` — **код существует** (ET-008). Изменения возможны только при выявлении бага во время F-07/F-09. - `scripts/gps_collect.py` — без изменений, используется как есть. - `src/api/gps_tracks/db.py`, `dedup.py`, `endpoint.py`, `mvt.py` — без изменений. ### Конфиги - `config/gps_sources.yaml` — изменение F-01..F-04. - `config/gps_regions.yaml` — изменение F-05. ### Фронтенд - `src/web/style.json` и `src/web/style-dark.json` — F-13: расширить match-expression `line-color` для слоя `gps-tracks-layer`. - `src/web/gps_tracks.js` (или модуль ET-008) — **без изменений кода** при условии, что фильтр-список источников строится из ответа API динамически. Если в ET-008 список захардкожен — добавить `enduro_russia` и `wikiloc` в маппинг лейблов источников и палитру. Это будет уточнено в TRZ §3. ### Тестовые фикстуры - `tests/fixtures/gps-tracks/enduro-russia-api-tracks-page1.json` — реальный snapshot ответа `/api/tracks?page=0`. - `tests/fixtures/gps-tracks/enduro-russia-track-{1,2,3}.gpx` — три GPX. - `tests/fixtures/gps-tracks/wikiloc-search-page1.html` — HTML страницы поиска. - `tests/fixtures/gps-tracks/wikiloc-trail-page.html` — HTML страницы трека. - `tests/fixtures/gps-tracks/wikiloc-track.gpx` — GPX из Wikiloc. - `tests/fixtures/gps-tracks/wikiloc-rate-limited.html` — заглушка для 429-сценария. ### Инфра - mva154: исходящие HTTPS к `endurorussia.ru` и `www.wikiloc.com` (уже разрешены DevOps-политикой). - Размер `data/gps_tracks.sqlite` не превысит 100 MB после первого прогона (200 треков × ~50 KB средний размер геометрии). ### Документация - BRD/TRZ/AC/Test-plan этого work item. - Опциональный ADR `06-adr/ADR-013-domain-fix-enduro-russia.md` — если расхождение конфиг/реальность сочтено архитектурным решением, а не баг-фиксом. По умолчанию — это bugfix, ADR не нужен. - Дополнения к `14-deploy-log.md` после первого прогона: команда запуска, `tracks_by_source`, длительность. ### Связи с другими work items - **ET-008** — родительская задача; ET-009 расширяет её. Никаких изменений в артефактах ET-008 не делаем. - **ttrails** — отдельный work item на активацию третьего источника (после ET-009). - **PH-3 Smart Route** — растущая база публичных треков может в будущем улучшить smart-route. Не в scope.