VEU.WathBody/1-docs/4-modules/m06-fit-import/api_specs/02-workout-import.md

2.8 KiB
Raw Blame History

Спецификация 02: WorkoutImporter — сопоставление и запись фактов

Источники: Ф-8.4, tables.md (src.workout: UNIQUE(athlete_id, fit_file_id), окно ± 15 минут), 🛡2 module_reqs.

1. Сигнатура

public Guid Import(int athleteId, FitFacts facts); // → workout id
// + перегрузка для упрощённого факта TCX/GPX (см. §4)

2. Алгоритм

  1. Дедуп: workouts.Select(athleteId, DateFrom/DateTo = день факта) содержит строку с тем же fit_file_id → возврат её id, записей нет (🛡3; повторный импорт — пропуск).
  2. Сопоставление (окно ± 15 минут от facts.StartedAt, Ф-8.4):
    • кандидат — тренировка телефона: program_version_id/session_kind любые, но без fit_file_id (уже связанная не перецепляется); ближайшая по |started_at − факт|;
    • несколько кандидатов в окне → ближайший; равенство (невозможно практически) — по возрастанию id (детерминированность 🛡).
  3. Сопоставилась → workouts.Update: дописываются только facts_json, avg_hr, hr_recovery_s, fit_file_id; started_at/status/session_kind/подходы телефона не меняются (🛡2). Существующий facts_json перезаписывается (файл один — у тренировки не может быть двух .fit).
  4. Не сопоставилась → новая строка «только .fit»: Id = new Guid() (сервер, Р2-Б), StartedAt = facts.StartedAt, Status = "finished", SessionKind = "normal" + поля фактов. Подходы (workout_set) не создаются (модуль не знает соответствие упражнений — v1).

3. Возврат

workout id связанной/созданной строки (для лога).

4. Упрощённый факт TCX/GPX

TcxGpxParser возвращает тот же FitFacts, где FactsJson — схема workout-facts/1 с "schema": "workout-facts/1" и заполненными session/records (пульсы), laps пустой, sets пустой, fit_file_id = имя файла, device = "". Запись — тот же алгоритм §2.

5. Инварианты 🛡

  • все выборки по athleteId (🛡1);
  • сопоставление не удаляет и не переписывает телефонные данные;
  • идемпотентность по fit_file_id.