Files
enduro-trails/docs/work-items/ET-009/01-brd.md
claude-bot eaa6b4cd27
Some checks failed
CI / lint (push) Failing after 4s
CI / test (push) Failing after 5s
CI / build (push) Has been skipped
feat(ET-009): analyst artifacts — BRD, TRZ, AC, test plan
2026-06-01 17:51:47 +00:00

240 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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` строки 3773): при `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.