240 lines
24 KiB
Markdown
240 lines
24 KiB
Markdown
---
|
||
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.
|