2.8 KiB
2.8 KiB
Спецификация 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. Алгоритм
- Дедуп:
workouts.Select(athleteId, DateFrom/DateTo = день факта)содержит строку с тем жеfit_file_id→ возврат её id, записей нет (🛡3; повторный импорт — пропуск). - Сопоставление (окно ± 15 минут от
facts.StartedAt, Ф-8.4):- кандидат — тренировка телефона:
program_version_id/session_kindлюбые, но безfit_file_id(уже связанная не перецепляется); ближайшая по|started_at − факт|; - несколько кандидатов в окне → ближайший; равенство (невозможно практически) — по возрастанию id (детерминированность 🛡).
- кандидат — тренировка телефона:
- Сопоставилась →
workouts.Update: дописываются толькоfacts_json,avg_hr,hr_recovery_s,fit_file_id;started_at/status/session_kind/подходы телефона не меняются (🛡2). Существующийfacts_jsonперезаписывается (файл один — у тренировки не может быть двух .fit). - Не сопоставилась → новая строка «только .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.