VEU.WathBody/3-app/2-cli/Server.WatchBody.Import/WorkoutImporter.cs

83 lines
3.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Server.WatchBody.Core.Dal.Src.Domain;
using Server.WatchBody.Core.Dal.Src.Repository;
namespace Server.WatchBody.Import;
/// <summary>
/// Запись фактов в src.workout (spec 02 m06): дедуп по fit_file_id,
/// сопоставление телефонной тренировке ± 15 минут (Ф-8.4), иначе
/// строка «только .fit». 🛡 Телефонные поля не меняются; записи только
/// в facts_json/avg_hr/hr_recovery_s/fit_file_id.
/// </summary>
public class WorkoutImporter(
IWorkoutRepository workouts,
decimal restingHrSitting = 90)
{
public Guid Import(int athleteId, ActivityFacts facts)
{
var day = DateOnly.FromDateTime(facts.StartedAt);
var candidates = workouts.Select(new WorkoutFilter
{
AthleteId = athleteId,
DateFrom = day.AddDays(-1),
DateTo = day.AddDays(1),
}).ToList();
// 1. дедуп: тот же fit_file_id — пропуск (🛡3)
var sameFile = candidates.FirstOrDefault(w => w.FitFileId == facts.FitFileId);
if (sameFile != null) return sameFile.Id;
// 2. сопоставление: ближайшая без fit_file_id в окне ± 15 минут
var match = candidates
.Where(w => w.FitFileId == null)
.Select(w => (workout: w, gap: Math.Abs((w.StartedAt - facts.StartedAt).TotalMinutes)))
.Where(x => x.gap <= 15)
.OrderBy(x => x.gap)
.ThenBy(x => x.workout.Id)
.FirstOrDefault();
if (match.workout != null)
{
var existing = match.workout;
workouts.Update(athleteId, new Workout
{
Id = existing.Id,
AthleteId = athleteId,
ProgramVersionId = existing.ProgramVersionId,
StartedAt = existing.StartedAt,
EndedAt = existing.EndedAt,
Status = existing.Status,
SessionKind = existing.SessionKind,
AvgHr = facts.AvgHr ?? existing.AvgHr,
FactsJson = facts.FactsJson,
FitFileId = facts.FitFileId,
HrRecoveryS = facts.HrRecoveryS ?? existing.HrRecoveryS,
Sets = [],
Commands = [],
});
return existing.Id;
}
// 3. строка «только .fit» (uuid генерирует сервер, Р2-Б);
// workout_insert fit-полей не принимает — вставка + дозакрытие
// (как sync, errata m02)
var id = Guid.NewGuid();
workouts.Insert(athleteId, new Workout
{
Id = id, AthleteId = athleteId, StartedAt = facts.StartedAt,
Status = "finished", SessionKind = "normal", Sets = [], Commands = [],
});
workouts.Update(athleteId, new Workout
{
Id = id, AthleteId = athleteId, StartedAt = facts.StartedAt,
Status = "finished", SessionKind = "normal",
AvgHr = facts.AvgHr,
FactsJson = facts.FactsJson,
FitFileId = facts.FitFileId,
HrRecoveryS = facts.HrRecoveryS,
Sets = [], Commands = [],
});
return id;
}
}