Files
enduro-trails/docs/work-items/ET-009/08-data-requirements.md
claude-bot 4be7fbf3de
Some checks failed
CI / lint (push) Failing after 4s
CI / test (push) Failing after 6s
CI / build (push) Has been skipped
feat(ET-009): architect deliverables — ADR, infra requirements, data requirements, tech risks, wikiloc parser stub
2026-06-01 19:20:15 +00:00

19 KiB
Raw Permalink Blame History

type, work_item_id, title, version, status, created_at, authors
type work_item_id title version status created_at authors
data-requirements ET-009 Требования к данным — ET-009: Активация EnduroRussia + Wikiloc 1 approved 2026-06-01
agent:architect

Требования к данным — ET-009

1. Резюме

ET-009 — активация двух уже разработанных source-парсеров. Никаких изменений в схеме БД, контрактах API, формате localStorage или dedup-алгоритме.

Меняются:

  • Содержимое существующей таблицы tracks (новые записи с source_id ∈ {enduro_russia, wikiloc});
  • Содержимое существующей таблицы pipeline_runs (новые записи с source_id ∈ {enduro_russia, wikiloc});
  • Содержимое config/gps_sources.yaml, config/gps_regions.yaml;
  • Содержимое src/web/style.json, style-dark.json (match-expressions по source).

Не меняются:

  • Schema tracks, pipeline_runs;
  • API контракты /api/gps-tracks*;
  • localStorage ключи и значения;
  • Dedup-алгоритм (compute_dedup_key);
  • ACTIVITY_TYPES enum.

2. Архитектурные границы данных

Слой данных Тип Расположение Изменения в ET-009
OSM-vector (trails, centralfederal.sqlite) существующий /app/data/centralfederal.sqlite нет
Личные GPX треки (ET-006) существующий браузер (memory) нет
Публичные GPS треки (ET-008) существующий /app/data/gps_tracks.sqlite +новые записи из новых источников
OSRM-граф существующий /app/data/enduro.osrm.* нет
User UI state существующий localStorage нет новых ключей

3. Серверные данные — gps_tracks.sqlite

3.1 Schema

Без изменений vs ET-008. См. docs/work-items/ET-008/08-data-requirements.md §3.1, §3.5. Никаких ALTER TABLE / DROP COLUMN / INDEX CREATE не делается.

3.2 Новые записи в tracks

Поле Значение для source_id='enduro_russia' Значение для source_id='wikiloc'
dedup_key вычислено compute_dedup_key вычислено compute_dedup_key
name из JSON meta.name из HTML <h1> или GPX metadata/name
description nullable (ADR-010: сохраняем) null (ADR-012: save_description: false)
activity_type из MAPPING (difficulty → enduro/moto) из MAPPING (motorcycle → moto, enduro → enduro)
user null (ADR-010: save_user_field: false) null (ADR-012)
created_at из JSON meta.created_at (если есть) nullable
length_m, points_count вычислено из GPX вычислено из GPX
min_lon..max_lat вычислено вычислено
geom WKB LineString WKB LineString
sources_json ["enduro_russia"] или ["enduro_russia", ...] после merge ["wikiloc"] или [..., "wikiloc"]
external_urls_json ["https://endurorussia.ru/tracks/<id>"] ["https://www.wikiloc.com/trails/<slug>/<id>"]
tags_json [] (источник не отдаёт tags) []
inserted_at, updated_at NOW() NOW()

3.3 Dedup-key — без изменений

Алгоритм compute_dedup_key (ADR-006) не меняется. Применяется к трекам из всех источников.

Ожидаемое поведение для пары (osm-трек, enduro_russia-трек, wikiloc-трек) из одной поездки:

  • Одинаковые (bbox_quantized, length_bucket, date) → одинаковый dedup_key;
  • Upsert ON CONFLICT → sources_json объединяется ["osm", "enduro_russia", "wikiloc"] (порядок по source_priority descending);
  • external_urls_json синхронно объединяется.

См. ET-008 ADR-006 для деталей.

3.4 ACTIVITY_TYPES — без изменений

Enum остаётся прежним. MAPPING каждого source-парсера независимо переводит свои категории в этот enum.

Source-категория → ACTIVITY_TYPES
EnduroRussia: enduro, hard, soft enduro
EnduroRussia: мото, тур moto
EnduroRussia: motorcycle moto
EnduroRussia: offroad offroad
EnduroRussia: остальное enduro (fallback в коде)
Wikiloc (act=19): motorcycle, enduro moto (default из MAPPING['motorcycle'])
Wikiloc (act=3): mtb, mountain biking bicycle
Wikiloc: hiking, running, trail running hike
Wikiloc: offroad offroad
Wikiloc: неизвестное moto (parser fallback)

3.5 Новые записи в pipeline_runs

После первого прогона:

SELECT id, source_id, status, tracks_new, finished_at - started_at
FROM pipeline_runs
ORDER BY id DESC LIMIT 5;

Ожидаемо ≥ 2 новые строки:

  • source_id='enduro_russia', status='ok' (или partial), tracks_new ≥ 200;
  • source_id='wikiloc', status ∈ {ok, partial, rate_limited}, tracks_new ≥ 1.

errors_json — null или JSON-object {HTTPError429: N, ...} если были transient errors.

3.6 Размер БД — оценка после ET-009

Источник Треков Средний размер записи Итого
OSM (уже в БД) ≤ 5000 ≈ 21 КБ ≤ 105 МБ
EnduroRussia (новое) ≈ 200305 ≈ 50 КБ (треки длиннее) ≈ 1015 МБ
Wikiloc (новое) ≈ 150 ≈ 50 КБ ≈ 0.52.5 МБ
Итого после ET-009 ≤ 5400 ≤ 130 МБ

Запас до операционного лимита (2 ГБ) — больше 15×.

3.7 GC и retention

Без изменений vs ET-008. Месячный GC через --gc (запускается отдельным cron'ом после двух успешных ручных прогонов).

3.8 Backup

Без изменений (см. 07-infra-requirements.md §4.2).

4. Клиентское хранилище

4.1 Существующие ключи (ET-008) — без изменений

Ключ Значение Замечания для ET-009
gps-tracks-enabled "true" | "false" без изменений
gps-tracks-activities JSON-array без изменений
gps-tracks-sources JSON-array source IDs может содержать новые ID после первого прогона; клиент сам подхватит. Defaults обновляются автоматически: при первом открытии после ET-009 — все 3 enabled источника попадают в default-набор
gps-tracks-color-mode "source" | "activity" без изменений

4.2 Миграция defaults

При первом открытии страницы после ET-009 клиент видит, что gps-tracks-sources (если есть в localStorage со старым значением ["osm"]) не содержит enduro_russia и wikiloc. Поведение ET-008:

  • Существующее значение localStorage сохраняется (пользователь сознательно мог выключить источники);
  • Новые источники появляются в UI-фильтре с галкой unchecked;
  • Пользователь может включить их вручную.

Это компромисс UX: автоматическое включение новых источников без согласия пользователя — нарушение принципа «без сюрпризов»; оставляем явный opt-in.

При желании оператора (нет в scope ET-009) — добавить one-shot migration в client-side JS: «если gps-tracks-sources существует и не содержит enduro_russia или wikiloc — добавить и пересохранить». Не делаем в ET-009.

4.3 Не-персистентное состояние

window.gpsTracksLayer (ET-008) — без изменений.

Маппинг SOURCE_ATTRIBUTIONS в gps_tracks.js расширяется:

const SOURCE_ATTRIBUTIONS = {
  osm: "© OpenStreetMap contributors (ODbL)",
  enduro_russia: "EnduroRussia.ru",
  wikiloc: "© Wikiloc contributors",
  ttrails: "ttrails.ru",   // для будущей активации
};

И маппинг SOURCE_LABELS для UI-чекбоксов:

const SOURCE_LABELS = {
  osm: "OSM",
  enduro_russia: "EnduroRussia",
  wikiloc: "Wikiloc",
  ttrails: "ttrails.ru",
};

5. Внешние входные данные

5.1 OSM Public GPS Traces (ADR-009) — без изменений

См. docs/work-items/ET-008/08-data-requirements.md §5.1.

5.2 EnduroRussia.ru (ADR-010 accepted)

Параметр Значение
Endpoint list GET https://endurorussia.ru/api/tracks?page=N&limit=50
Endpoint GPX GET https://endurorussia.ru/api/tracks/{id}/gpx
Формат list JSON {items: [{id, name, difficulty, created_at}, ...], total}
Формат GPX XML (GPX 1.1) — <trk><trkseg><trkpt>
Лицензия Public; ADR-010 §3 — обезличенно (без user)
Атрибуция EnduroRussia.ru
Rate-limit 5 sec / req
Объём для ЦФО+Чувашии (оценка) ≥ 200 треков
User-Agent enduro-trails/1.0 (+https://openclaw.mva154.duckdns.org/enduro/)
Authentication Нет

5.3 Wikiloc (ADR-012 accepted)

Параметр Значение
Endpoint поиска GET https://www.wikiloc.com/wikiloc/find.do?act=<code>&sw=<lat,lon>&ne=<lat,lon>&page=<N>
Endpoint трека GET https://www.wikiloc.com/trails/<slug>/<id>
Endpoint GPX GET https://www.wikiloc.com/wikiloc/downloadTrail.do?id=<id>
Формат поиска HTML (regex-extract <a href="/trails/…/<id>">)
Формат трека HTML (regex-extract <h1> для имени + ссылка на GPX)
Формат GPX XML (GPX 1.1)
Лицензия Proprietary (ADR-012 §3 — обезличенно, без description)
Атрибуция © Wikiloc contributors
Rate-limit 10 sec / req (жёстко)
Graceful-stop На 403/429 — return без raise
max_tracks_per_run 50 (soft-cap первого прогона)
User-Agent enduro-trails/1.0 (+https://openclaw.mva154.duckdns.org/enduro/)
Authentication Нет

5.4 ttrails.ru (ADR-011 proposed)

Не используется в ET-009. enabled: false в gps_sources.yaml, pipeline-guard пропускает.

6. Контракт публичного API

6.1 GET /api/gps-tracks — без изменений

Endpoint остаётся как в ET-008. Новые ID источников (enduro_russia, wikiloc) появляются в значениях:

  • properties.sources — массив ["enduro_russia"] / ["wikiloc"] / ["osm", "enduro_russia"] (после dedup-merge);
  • properties.external_urls["https://endurorussia.ru/tracks/<id>"] / ["https://www.wikiloc.com/trails/<slug>/<id>"].

Никаких новых query-параметров, response-полей или error-кодов.

Query-параметр source=... (фильтр по source ID) уже существует; теперь принимает новые значения enduro_russia, wikiloc.

6.2 GET /api/gps-tracks/tiles/{z}/{x}/{y}.mvt — без изменений

properties.source в MVT-feature может теперь принимать значения enduro_russia / wikiloc (первый source в sources_json). Клиент-стиль (match-expression line-color) переключается на соответствующий цвет.

6.3 GET /api/gps-tracks/health — без изменений в схеме

Response shape без изменений. Содержимое:

  • tracks_by_source теперь содержит ключи enduro_russia и wikiloc с числовыми значениями;
  • last_pipeline_run.sources_ok / sources_error / sources_skipped_license могут содержать новые source IDs.

Клиент-side SOURCE_ATTRIBUTIONS маппинг превращает ключи tracks_by_source в строки атрибуции для MapLibre Attribution control (REQ-F-14).

6.4 POST /api/gps-tracks/cache/clear — без изменений

7. Персональные данные (PII)

Без изменений vs ET-008 §7, с расширением табличного сводного:

Канал PII Условия в ET-009
tracks.user для enduro_russia нетsave_user_field: false (ADR-010) сохраняется null
tracks.user для wikiloc нетsave_user_field: false (ADR-012) сохраняется null
tracks.geom, tracks.created_at, tracks.length_m низкий риск, публично выложено автором сохраняется как в ET-008
tracks.description для enduro_russia возможны следы PII в свободном тексте сохраняется в default (ADR-010 §3); может быть пере-включено save_description: false
tracks.description для wikiloc возможны следы PII nullsave_description: false (ADR-012)
tracks.name для enduro_russia / wikiloc название может содержать псевдонимы сохраняется (видно в popup)
IP mva154 становится известен endurorussia.ru, wikiloc.com да стандартное поведение скрейпера; User-Agent с контактом

7.1 Право на удаление

Без изменений. external_urls_json хранит ссылку; точечное удаление по запросу автора возможно (ET-008 §7.1).

7.2 GDPR / РФ ФЗ-152

Без изменений. Обрабатываются только публично выложенные данные.

8. Атрибуция

Расширение vs ET-008:

Источник Атрибуция-строка
osm © OpenStreetMap contributors (ODbL)
enduro_russia EnduroRussia.ru
wikiloc © Wikiloc contributors
ttrails (будущее) ttrails.ru

Клиент формирует список из tracks_by_source (где count > 0) через SOURCE_ATTRIBUTIONS маппинг и подмешивает в MapLibre Attribution control при включённом слое «Публичные треки».

В popup трека (gps_tracks.js) — ссылки external_urls (как в ET-008 REQ-F-18); никаких дополнительных правок.

9. Backup и retention

Без изменений vs ET-008 §9. Ежедневный snapshot + 14 дней retention для data/gps_tracks.sqlite. После ET-009 backup-размер вырастет с ~5 МБ до ~50 МБ — пренебрежимое влияние на disk budget.

10. Тестовые данные (фикстуры)

ET-009 вводит новые фикстуры в tests/fixtures/gps-tracks/:

Файл Содержимое Использование
enduro-russia-api-tracks-page1.json реальный snapshot GET /api/tracks?page=0&limit=50; ≥ 5 items с полями id/name/difficulty/created_at UT-ER-01..08, IT-ER-01
enduro-russia-track-1.gpx реальный GPX, ≥ 10 trkpt, в bbox tsfo_plus_chuvashia UT-ER-01, IT-ER-01
enduro-russia-track-2.gpx пустой GPX (<trkseg></trkseg>) UT-ER-02 (skip-логика)
enduro-russia-track-3.gpx GPX с одной точкой за пределами bbox UT-ER-03 (bbox-фильтрация)
wikiloc-search-page1.html HTML страницы поиска; ≥ 5 ссылок /trails/…/<id> UT-WL-01, IT-WL-01
wikiloc-trail-page.html HTML страницы одного трека UT-WL-02..04, IT-WL-01
wikiloc-track.gpx реальный GPX, координаты совпадают с одним из EnduroRussia-треков UT-WL-05, IT-DEDUP-01
wikiloc-rate-limited.html пустой/тестовый HTML UT-WL-07/08 (для mock 403/429)

Снимки делаются разово, вручную оператором / разработчиком через curl или браузер-инспектор; сохраняются в git и не зависят от состояния сайта.

10.1 Юридический статус фикстур

Фикстуры в tests/fixtures/gps-tracks/ — публичные snapshot'ы открытых страниц/API, размещённые исключительно для верификации парсеров (некоммерческое тестовое использование). Не включаются в production-БД, не отдаются через API. Внутри фикстур не сохраняются authentication-cookies, авторские контактные данные или иные PII.

При запросе администратора платформы — фикстура подменяется на синтетический минимальный пример с той же структурой.

11. Контракты, которые нельзя ломать

Без изменений vs ET-008 §10:

  1. dedup_key формула — не меняется в ET-009.
  2. ACTIVITY_TYPES enum — не меняется в ET-009.
  3. GeoJSON response shape — не меняется.
  4. MVT layer name gps_tracks и properties — не меняется.
  5. localStorage keys — не меняется.

Новое: маппинги SOURCE_ATTRIBUTIONS / SOURCE_LABELS в клиенте являются «soft contract»: добавление ключей — safe; удаление — сломает атрибуцию для соответствующих треков.

12. Вывод

ET-009 — append-only data event:

  • Заполняет существующую схему БД новыми записями;
  • Использует существующие API-контракты без изменений;
  • Расширяет существующие client-side маппинги (атрибуция, цвета);
  • Никаких миграций, никаких ALTER, никаких новых ключей localStorage.

Юридически защищён через ADR-010 (accepted) и ADR-012 (accepted). Pipeline-guard прозрачен — proposed ADR блокирует source автоматически.