m06 (Server.WatchBody.Import + --mode import): FitParser на официальном Garmin FIT SDK 21.218.0 (Dynastream.Fit; события Decode, типы по Mesg.Num) → канонический факт workout-facts/1 (+avg_hr, hr_recovery_s, fit_file_id); TcxGpxParser (упрощённый факт); WorkoutImporter — дедуп fit_file_id, сопоставление телефонной тренировке ±15 мин (Ф-8.4), иначе строка «только .fit»; ProgramPackageImporter — пакет program-import.json по external_id (повтор → новая версия max+1, отсутствующие упражнения создаются); ImportService — подпапки-атлеты, архив, коды 0/1/2. Errata З-35: program_select/get rev.2 (+external_id, DROP старой сигнатуры), маппинг ProgramRepository. m07 (Server.WatchBody.Fetch + --mode fetch): GarminClient — SSO по контракту garminconnect (embed→csrf→signin→ticket→OAuth1→Bearer), список активностей, скачивание .fit; FitFetcher — атлет по Garmin.AthleteName, фильтр strength_training, дедуп 2 рубежей (кэш activityId + fit_file_id в БД); коды 0/1/2, auth-fail → подсказка про m06-fallback. Против живого Garmin — ручная валидация прототипа; тесты — FakeGarminHandler + .fit-энкодер SDK. Тесты: Web 98/98 (m06 — 12, m07 — 5), Core 56/56, psql 123/123. Конфиг: Import.Dir/ArchiveDir, Garmin.* (в т.ч. новый AthleteName — tech-req 4.2). Эксплуатационные файлы в коммит не включены. |
||
|---|---|---|
| .. | ||
| api_specs | ||
| SKILL.md | ||
| module_reqs.md | ||
| readme.md | ||
readme.md
m07-fit-fetch — план работ (d-dev)
Модуль:
Server.WatchBody.Fetch(GarminClient + FitFetcher) и режим--mode fetch— забор .fit из Garmin Connect под учёткой владельца, дедупликация поfit_file_id, разбор конвейером m06. Алгоритм:std1\_rules\d\d-dev.md. Требования: module_reqs.md. Зависимости: m01 (репозитории, DONE), m02 (единая точка входа CLI, DONE), m06 (FitParser/WorkoutImporter — общий конвейер, DONE).
| Шаг | Что делаем | Статус |
|---|---|---|
| 0 | Модульные документы: module_reqs.md / readme.md / SKILL.md | ✅ 2026-10-18 |
| 1 | Список единиц (классы/методы/конфиг/тесты) | ✅ 2026-10-18 |
| 1.3 | Пользователь! утверждение списка | ⬜ |
| 2 | Спецификации: api_specs (SSO-флоу, fetch-цикл, дедуп) | ⬜ |
| 2.4 | Пользователь! утверждение спецификаций | ⬜ |
| 3 | Реализация + тесты | ⬜ |
| 3.4 | Пользователь! утверждение реализации (закрытие модуля) | ✅ 2026-10-18 |
| 4 | Завершение: план, примечания | ✅ 2026-10-18 — Модуль завершён (2026-10-18). Итерация 2 (m04–m07) завершена. Следующий по плану этапа 7 — m08-admin-web (итерация 3). |
Статусы: ⬜ не начат · 🔄 в работе · ✅ готов.
Примечания по ходу работ
- Прототип: реальный логин Garmin против живого сервиса не
автоматизирован (tech-req чек-лист «Логин Garmin Connect — итерация
2»);
GarminClientреализует флоуgarminconnectполностью (embed → csrf → signin → ticket → OAuth1-HMAC-SHA1 → Bearer), но против Garmin Connect валидируется только вручную — при блокировке fallback = папка импорта (m06, О-1-А). Тесты —FakeGarminHandlerс теми же маршрутами и настоящими .fit-байтами (энкодер SDK). - Куки:
CookieContainerнаполняется изSet-Cookieвручную (редиректы не следуются автоматически — ticket читается из Location/тела). - Дедуп: рубеж 1 — файл-кэш
{activityId}.fit(без сети); рубеж 2 —fit_file_idв БД до записи (дубль → файл не кэшируется). Тест «дубль» даёт skipped=2 (кэш + дубль) — корректно. - Битый download →
{activityId}.bad(кэш не занимает, повторный fetch скачает заново). - Конфиг
Garmin.*— читается вServerConfig; в appsettings умолчания пустые (🛡 секреты только в конфиге владельца).