---
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=<N>&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=<code>&sw=<lat,lon>&ne=<lat,lon>&page=<N>` → HTML с `<a href="/trails/…/<id>">`            |
| Endpoint трека             | `GET /trails/<slug>/<id>` → HTML c ссылкой на GPX                                                                  |
| Endpoint GPX               | `GET /wikiloc/downloadTrail.do?id=<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.
