feat(ET-009): activate EnduroRussia + Wikiloc GPS sources
Конфиг-only активация двух новых источников GPS-треков поверх pipeline ET-008. Не вводит новых компонентов, БД-таблиц, endpoint'ов. Config: - config/gps_sources.yaml: enduro_russia enabled=true, base_url исправлен на endurorussia.ru (без дефиса); добавлена запись wikiloc с max_tracks_per_run=50, activity_filter=[motorcycle, enduro]. - config/gps_regions.yaml: wikiloc добавлен в tsfo_plus_chuvashia.sources. Parser: - wikiloc.py: добавлен soft-cap max_tracks_per_run в collect(), извлечение created_at из GPX metadata/первого trkpt — для корректной межисточниковой дедупликации с EnduroRussia. UI (src/web/gps_tracks.js): - GPS_SOURCE_COLORS: добавлен цвет wikiloc (#4363d8). - Дефолтный фильтр sources включает wikiloc. - GPS_SOURCE_ATTRIBUTIONS: маппинг source_id → строка атрибуции; _updateGpsAttribution() подтягивает /api/gps-tracks/health и выставляет attribution с теми источниками, у которых tracks > 0. - _buildGpsFiltersUI: чекбокс «Wikiloc» в #gps-source-grid. Tests: - Fixtures: 7 файлов в tests/fixtures/gps-tracks/. - Unit: 10 UT-ER + 10 UT-WL — парсеры, MAPPING, bbox-фильтр, pagination, 429/403 graceful-stop, rate-limit, max_tracks_per_run. - Integration: IT-ER-01, IT-WL-01, IT-WL-02, IT-DEDUP-01, IT-LIC-01 через scripts.gps_collect.main + httpx.MockTransport. - Contract: 2 CT-ER с маркером @pytest.mark.network (nightly only). - JS: 2 новых теста на наличие wikiloc в SOURCE_COLORS и в фильтрах. Linters/Tests: ruff clean (новые файлы), 166 pytest passed, 24 JS-tests passed. Refs: ET-009 Acceptance: AC-01..AC-08, AC-14..AC-17 (для AC-09..AC-13 — продакшн-прогон) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -62,6 +62,8 @@ class WikilocParser(SourceParser):
|
||||
source_id = self.config.get("id", "wikiloc")
|
||||
source_priority = self.config.get("source_priority", 70)
|
||||
activity_filter = self.config.get("activity_filter", ["motorcycle", "enduro"])
|
||||
max_tracks = self.config.get("max_tracks_per_run")
|
||||
yielded = 0
|
||||
|
||||
headers = {
|
||||
"User-Agent": user_agent,
|
||||
@@ -188,7 +190,15 @@ class WikilocParser(SourceParser):
|
||||
):
|
||||
continue
|
||||
|
||||
if max_tracks is not None and yielded >= max_tracks:
|
||||
logger.info(
|
||||
"Wikiloc: reached max_tracks_per_run=%d, stopping",
|
||||
max_tracks,
|
||||
)
|
||||
return
|
||||
|
||||
yield track
|
||||
yielded += 1
|
||||
|
||||
page += 1
|
||||
|
||||
@@ -260,16 +270,40 @@ def _parse_gpx(
|
||||
if tag.startswith("{"):
|
||||
ns = tag.split("}")[0] + "}"
|
||||
|
||||
# Извлекаем название из GPX metadata если нет из HTML
|
||||
if not name:
|
||||
for child in root:
|
||||
local = child.tag.replace(ns, "") if ns else child.tag
|
||||
if local == "metadata":
|
||||
for meta_child in child:
|
||||
local2 = meta_child.tag.replace(ns, "") if ns else meta_child.tag
|
||||
if local2 == "name":
|
||||
name = meta_child.text
|
||||
# Извлекаем название и время из GPX metadata
|
||||
created_at = None
|
||||
for child in root:
|
||||
local = child.tag.replace(ns, "") if ns else child.tag
|
||||
if local == "metadata":
|
||||
for meta_child in child:
|
||||
local2 = meta_child.tag.replace(ns, "") if ns else meta_child.tag
|
||||
if local2 == "name" and not name:
|
||||
name = meta_child.text
|
||||
elif local2 == "time" and meta_child.text:
|
||||
created_at = meta_child.text.strip()
|
||||
break
|
||||
|
||||
# Fallback: первая <trkpt><time> из первого trkseg
|
||||
if not created_at:
|
||||
for trk in root:
|
||||
local = trk.tag.replace(ns, "") if ns else trk.tag
|
||||
if local != "trk":
|
||||
continue
|
||||
for trkseg in trk:
|
||||
local2 = trkseg.tag.replace(ns, "") if ns else trkseg.tag
|
||||
if local2 != "trkseg":
|
||||
continue
|
||||
for trkpt in trkseg:
|
||||
for sub in trkpt:
|
||||
sub_local = sub.tag.replace(ns, "") if ns else sub.tag
|
||||
if sub_local == "time" and sub.text:
|
||||
created_at = sub.text.strip()
|
||||
break
|
||||
if created_at:
|
||||
break
|
||||
if created_at:
|
||||
break
|
||||
if created_at:
|
||||
break
|
||||
|
||||
coords = []
|
||||
@@ -324,7 +358,7 @@ def _parse_gpx(
|
||||
description=None,
|
||||
activity_type=activity_type,
|
||||
user=None,
|
||||
created_at=None,
|
||||
created_at=created_at,
|
||||
length_m=length_m,
|
||||
points_count=len(coords),
|
||||
geom_wkb=geom_wkb,
|
||||
|
||||
Reference in New Issue
Block a user