bbed0e1082
feat(gps-tracks): lower public-tracks minzoom to z5 (ET-012)
...
CI / lint (push) Successful in 5s
CI / test (push) Successful in 9s
CI / build (push) Successful in 2s
CI / lint (pull_request) Successful in 4s
CI / test (pull_request) Successful in 10s
CI / build (pull_request) Successful in 3s
Калибровка существующих tier-таблиц `build_gps_mvt` /
`_simplify_coords` (ADR-016), чтобы при первом открытии карты
пользователь видел общее покрытие сети треков, а не пустую подложку.
Backend (src/api/gps_tracks/mvt.py):
- build_gps_mvt: добавлены тиры z<=5 (min_length=10 км, limit=1500)
и z=6 (5 км / 2000); z=7+ — без изменений (регрессия).
- _simplify_coords: tolerance для z=6 = 0.018° (~2 км),
для z<=5 = 0.04° (~4 км); z=7+ не меняется.
Frontend:
- GPS_TRACKS_MIN_ZOOM понижен с 8 до 5; vector-source.minzoom
подхватывает константу автоматически.
- line-width / halo получили stop на z=5 (0.8 / 1.8 CSS-px),
чтобы линия была читаема на любом DPR.
- Hint #public-tracks-zoom-hint: «Зум 8+» → «Зум 5+».
Тесты:
- 8 unit zoom-tier (UT-Z5/6/7/8/12) — REQ-F-09.
- 10 unit simplify (UT-SIMP-*) — REQ-F-10.
- 9 integration endpoint z5-z7 (IT-Z5/6/7, CACHE, REGRESS) — REQ-F-11/12.
- 2 perf (PERF-Z5-01/02; avg ~64 ms, p95 ~89 ms при 500 треках —
ниже бюджета 200/500 ms по M-6) — REQ-F-13.
Маркер @pytest.mark.perf, не в основном CI-gate.
Контракт API /api/gps-tracks* не меняется (REQ-F-15);
localStorage-ключи и конфиги тоже (REQ-F-16, F-18).
Refs: ET-012
ADR: docs/work-items/ET-012/06-adr/ADR-016-z5-tiling-policy.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-06-04 06:29:41 +00:00
721b33a2f6
fix(gps-tracks): address ET-011 review — JS UI tests + flat 403 contract
...
CI / lint (push) Successful in 4s
CI / test (push) Successful in 6s
CI / lint (pull_request) Successful in 4s
CI / build (push) Successful in 2s
CI / test (pull_request) Successful in 6s
CI / build (pull_request) Successful in 4s
Закрывает findings из docs/work-items/ET-011/12-review.md (REQUEST_CHANGES,
попытка 3/3):
P1-01 — добавлены поведенческие JS unit-тесты UI download-flow
- tests/web/track_download.test.js — 28 кейсов (node --test):
• _parseFilenameFromCD — RFC 5987 приоритет, plain fallback,
битый percent-encoding, null/empty (REQ-F-05.2, AC-2 UI)
• _handleDownloadError — 400/403/404/413/5xx тосты, defensive
при отсутствии showToast, поддержка flat (ADR-015 §G) и legacy
wrapped 403-форм (REQ-F-05.4, AC-7 UI)
• _renderTrackPopupHtml — наличие кнопки, aria-label «Скачать GPX»,
data-track-id, отсутствие при невалидном id, регрессия прочих
полей (REQ-F-01, AC-1)
- tests/web/test_track_download.py — pytest-обёртка (статические
проверки + запуск Node-раннера), исполняется в обычном pytest tests/
- 04b-ui-test-cases.md: AC-13 (mobile-bbox) явно маркирован как
MANUAL release-smoke (Playwright-раннер в проекте не настроен;
альтернатива согласована reviewer'ом в P1-01).
P2-01 — устранено расхождение «doc vs runtime» по контракту 403
- endpoint.py: HTTPException(detail={...}) → JSONResponse(content={...}),
чтобы FastAPI не оборачивал dict во второй слой «detail». Контракт
теперь совпадает с ADR-015 §G и ADR-014 §6:
{"detail":"source_forbidden","external_urls":[...]}
- test_gps_tracks_download.py IT-05: упрощено — body уже плоский,
без двухуровневого `body.get("detail", body)` workaround.
- gps_tracks.js::_handleDownloadError: flat-форма стала приоритетной,
wrapped-форма оставлена как defensive fallback (с комментарием).
Регрессия: 89/89 API-тестов + 24/24 предыдущих JS-тестов + 28 новых
JS-тестов download-flow проходят. ruff check — clean.
Refs: ET-011
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-06-03 23:01:19 +00:00
eea6c846c2
feat(gps-tracks): GPX download from public track popup
...
CI / lint (push) Failing after 5s
CI / test (push) Failing after 6s
CI / build (push) Has been skipped
CI / lint (pull_request) Failing after 4s
CI / test (pull_request) Failing after 5s
CI / build (pull_request) Has been skipped
Реализация ET-011: кнопка «Скачать GPX» в popup публичного GPS-трека и
новый эндпоинт GET /api/gps-tracks/{track_id}/download (GPX 1.1 +
Content-Disposition с UTF-8 именем по RFC 5987). Реэкспорт защищён
per-source флагом `download_allowed` в `config/gps_sources.yaml`
(default-deny, MVP whitelist = `osm`).
Backend:
- `src/api/gps_tracks/export.py` — чистый stdlib-builder GPX 1.1
(`build_gpx`) + санитизация имени файла (`safe_filename`, RFC 5987).
- `src/api/gps_tracks/endpoint.py` — новый route с проверками
400 / 403 / 404 / 413; cap 200 000 точек (REQ-NF-02).
- `src/api/gps_tracks/config.py` — `load_download_allowed_sources()`
читает YAML, default-deny при отсутствии поля; fallback на `{"osm"}`
при отсутствии конфига.
- `src/api/main.py` — пробрасывает `GPS_SOURCES_CONFIG_PATH` в router.
Frontend:
- `src/web/gps_tracks.js` — кнопка в `_renderTrackPopupHtml`,
обработчик `_downloadPublicTrack` (fetch + Blob + a.download — тот же
паттерн, что в `app.js::downloadGPX`, R-1 митигирован), парсер
`_parseFilenameFromCD` для RFC 5987, маппинг ошибок
`_handleDownloadError` (403/404/413/5xx → showToast).
- `src/web/app.css` — стиль кнопки, 32×32 CSS px (REQ-NF-04).
Тесты:
- 13 unit для GPX-builder (UT-01/02/03/05; XSD-валидация против
`tests/fixtures/gpx-1.1/gpx.xsd`).
- 10 unit для `safe_filename` (UT-04).
- 11 integration для download-эндпоинта (IT-01..08 +
ANY-rule license check + default-deny без конфига).
ADR-014 (gpx-download-endpoint), ADR-015 (source-redistribution-policy).
Refs: ET-011
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-06-03 20:59:53 +00:00
1ffa178b38
fix(gps-tracks): aggregate last_pipeline_run in health endpoint (REQ-F-12)
...
CI / lint (push) Failing after 4s
CI / test (push) Failing after 5s
CI / build (push) Has been skipped
CI / lint (pull_request) Failing after 3s
CI / test (pull_request) Failing after 5s
CI / build (pull_request) Has been skipped
Replace raw single-row fetch with aggregation over all pipeline_runs
rows sharing the latest started_at. Returns structured object with
regions[], sources_ok[], sources_error[], tracks_added instead of
a raw DB row with region_id/source_id strings.
Returns null when no runs exist (empty DB).
Update test_i40_health_endpoint: add db_with_pipeline_runs fixture
(two rows, same started_at, two regions) and assert the full
aggregated shape including concrete values.
Refs: ET-008
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-01 14:27:52 +00:00
ba356ae317
fix(gps-tracks): rename health fields and fix layer insert priority (F-04, F-05)
...
CI / lint (push) Failing after 4s
CI / test (push) Failing after 5s
CI / build (push) Has been skipped
CI / lint (pull_request) Failing after 4s
CI / test (pull_request) Failing after 5s
CI / build (pull_request) Has been skipped
F-04: rename gps_health() response fields per tester feedback:
- total_tracks → tracks_total
- by_activity → tracks_by_activity
- recent_pipeline_runs (list) → last_pipeline_run (object | null)
Change LIMIT from 10 to 1; fetch single row instead of a list.
F-05: rewrite _findGpsInsertPosition with explicit priority order:
1. gpx-layer-* (ET-006 GPX file layers) — highest priority
2. route-* (ET-002 routing layers)
Remove old combined find() that lacked clear priority semantics.
Add tests/web/gps_tracks.test.js (22 JS unit tests via node:test):
- _findGpsInsertPosition priority logic (9 cases)
- Filter state management — default state assertions (5 cases)
- Color palette mapping and _buildColorExpression (8 cases)
Add tests/web/test_gps_tracks.py — Python pytest runner (8 static
checks + node --test invocation).
Refs: ET-008
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-01 14:18:06 +00:00
edbe9a3044
fix(gps-tracks): normalise GeoJSON props, add health fields, OSM meta fetch, z-order fix
...
CI / lint (push) Failing after 4s
CI / test (push) Failing after 4s
CI / build (push) Has been skipped
CI / lint (pull_request) Failing after 3s
CI / test (pull_request) Failing after 4s
CI / build (pull_request) Has been skipped
Refs: ET-008
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-01 12:42:36 +00:00
0060003f28
feat(gps-tracks): ET-008 публичные GPS-треки с публичных платформ
...
CI / lint (push) Failing after 4s
CI / test (push) Failing after 4s
CI / build (push) Has been skipped
CI / lint (pull_request) Failing after 4s
CI / test (pull_request) Failing after 4s
CI / build (pull_request) Has been skipped
Backend:
- Миграция gps_tracks_001_init.sql: таблицы tracks + pipeline_runs
- Пакет src/api/gps_tracks/: models, db (WAL+upsert с dedup), dedup
(bbox+length+date bucket-hash), mvt (LRU-кэш 1024 тайла), endpoint
(GET /api/gps-tracks, GET /api/gps-tracks/tiles/{z}/{x}/{y}.mvt,
GET /api/gps-tracks/health, POST /api/gps-tracks/cache/clear), config
- Парсеры: osm (split_bbox, haversine, defusedxml XXE-защита),
enduro_russia + ttrails — заглушки (ADR-010/011 proposed, блокированы)
- Licensing guard: pipeline проверяет status ADR-файла до запуска источника
- scripts/gps_collect.py: CLI с --region/--source/--dry-run/--gc
Frontend:
- src/web/gps_tracks.js: двухрежимный слой (MVT z≤11, GeoJSON z≥12),
debounced fetch + AbortController, фильтры активности/источника,
цветовая палитра by-source/by-activity, halo на спутнике, popup трека,
restorePublicTracksState(), localStorage persistence
- index.html: чекбокс «Публичные треки» в terrain-popup, #sheet-gps-filters
- app.css: .terrain-link-btn, .gps-filter-grid, .track-popup
- app.js: вызов restorePublicTracksState() в rebuildMapOverlays(),
applyGpsHaloVisibility() в applyBaseLayer()
Конфиги:
- config/gps_sources.yaml: osm (enabled), enduro_russia/ttrails (disabled)
- config/gps_regions.yaml: ЦФО+Чувашия (enabled), Кавказ (disabled)
Docker:
- gps-collector service с profiles: [batch]
Тесты: 48 новых тестов (unit + integration), 125/125 pass
Refs: ET-008
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-01 12:28:54 +00:00