private P2GWorkout BuildP2GWorkout(string workoutId, JObject workout, JObject workoutSamples) { using var tracing = Tracing.Trace($"{nameof(PelotonService)}.{nameof(BuildP2GWorkout)}") .WithWorkoutId(workoutId); dynamic data = new JObject(); data.Workout = workout; data.WorkoutSamples = workoutSamples; P2GWorkout deSerializedData = null; try { deSerializedData = JsonSerializer.Deserialize <P2GWorkout>(data.ToString(), new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }); deSerializedData.Raw = data; } catch (Exception e) { _failedCount++; var title = "workout_failed_to_deserialize_" + workoutId; _logger.Error("Failed to deserialize workout from Peloton. You can find the raw data from the workout here: @FileName", title, e); tracing.AddTag("exception.message", e.Message); SaveRawData(data, title); } return(deSerializedData); }
public override ConvertStatus Convert(P2GWorkout workout) { if (!_config.Format.Fit) { return new ConvertStatus() { Success = true, ErrorMessage = "Fit format disabled in config." } } ; return(base.Convert(FileFormat.Fit, workout)); }
public void GetWorkoutType_Should_Map_Correctly([Values] FitnessDiscipline fitnessDiscipline, [Values] bool hasGPS) { var workout = new P2GWorkout() { Workout = new Workout() { Fitness_Discipline = fitnessDiscipline }, WorkoutSamples = new WorkoutSamples() { Location_Data = hasGPS ? new List <LocationData>() { new LocationData() } : null } }; var workoutType = workout.WorkoutType; switch (fitnessDiscipline) { case FitnessDiscipline.None: workoutType.Should().Be(WorkoutType.None); break; case FitnessDiscipline.Bike_Bootcamp: workoutType.Should().Be(WorkoutType.BikeBootcamp); break; case FitnessDiscipline.Cardio: workoutType.Should().Be(WorkoutType.Cardio); break; case FitnessDiscipline.Circuit: workoutType.Should().Be(WorkoutType.Circuit); break; case FitnessDiscipline.Cycling: workoutType.Should().Be(WorkoutType.Cycling); break; case FitnessDiscipline.Meditation: workoutType.Should().Be(WorkoutType.Meditation); break; case FitnessDiscipline.Strength: workoutType.Should().Be(WorkoutType.Strength); break; case FitnessDiscipline.Stretching: workoutType.Should().Be(WorkoutType.Stretching); break; case FitnessDiscipline.Yoga: workoutType.Should().Be(WorkoutType.Yoga); break; case FitnessDiscipline.Running when hasGPS: workoutType.Should().Be(WorkoutType.OutdoorRunning); break; case FitnessDiscipline.Running: workoutType.Should().Be(WorkoutType.TreadmillRunning); break; case FitnessDiscipline.Walking when hasGPS: workoutType.Should().Be(WorkoutType.OutdoorRunning); break; case FitnessDiscipline.Walking: workoutType.Should().Be(WorkoutType.TreadmillWalking); break; } }
public async Task DownloadLatestWorkoutDataAsync() { if (_config.Peloton.NumWorkoutsToDownload <= 0) { return; } using var tracing = Tracing.Trace(nameof(DownloadLatestWorkoutDataAsync)); await _pelotonApi.InitAuthAsync(); var recentWorkouts = await _pelotonApi.GetWorkoutsAsync(_config.Peloton.NumWorkoutsToDownload); var completedWorkouts = recentWorkouts.data.Where(w => { if (w.Status == "COMPLETE") { return(true); } Log.Debug("Skipping in progress workout. {@WorkoutId} {@WorkoutStatus} {@WorkoutType}", w.Id, w.Status, w.Fitness_Discipline); return(false); }); var filteredWorkouts = completedWorkouts.Where(w => { if (!_config.Peloton.ExcludeWorkoutTypes?.Contains(w.Fitness_Discipline) ?? true) { return(true); } Log.Debug("Skipping excluded workout type. {@WorkoutId} {@WorkoutStatus} {@WorkoutType}", w.Id, w.Status, w.Fitness_Discipline); return(false); }); var workingDir = _config.App.DownloadDirectory; _fileHandler.MkDirIfNotExists(workingDir); foreach (var recentWorkout in filteredWorkouts) { var workoutId = recentWorkout.Id; SyncHistoryItem syncRecord = _dbClient.Get(recentWorkout.Id); if ((syncRecord?.DownloadDate is object)) { Log.Debug("Workout {@WorkoutId} already downloaded, skipping.", recentWorkout.Id); continue; } using var workoutTimer = WorkoutDownloadDuration.NewTimer(); var workoutTask = _pelotonApi.GetWorkoutByIdAsync(recentWorkout.Id); var workoutSamplesTask = _pelotonApi.GetWorkoutSamplesByIdAsync(recentWorkout.Id); await Task.WhenAll(workoutTask, workoutSamplesTask); var workout = workoutTask.GetAwaiter().GetResult(); var workoutSamples = workoutSamplesTask.GetAwaiter().GetResult(); dynamic data = new JObject(); data.Workout = workout; data.WorkoutSamples = workoutSamples; var workoutTitle = string.Empty; P2GWorkout deSerializedData = null; try { deSerializedData = JsonSerializer.Deserialize <P2GWorkout>(data.ToString(), new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }); workoutTitle = WorkoutHelper.GetUniqueTitle(deSerializedData.Workout); if (_config.Format.Json && _config.Format.SaveLocalCopy) { SaveRawData(data, workoutTitle); } } catch (Exception e) { _failedCount++; var title = "workout_failed_to_deserialize_" + _failedCount; Log.Error("Failed to deserialize workout from Peloton. You can find the raw data from the workout here: @FileName", title, e); SaveRawData(data, title); return; } Log.Debug("Write peloton workout details to file for {@WorkoutId}.", workoutId); File.WriteAllText(Path.Join(workingDir, $"{workoutTitle}.json"), data.ToString()); var syncHistoryItem = new SyncHistoryItem(deSerializedData.Workout) { DownloadDate = DateTime.Now, }; _dbClient.Upsert(syncHistoryItem); } }
protected void Convert(string format) { if (!_fileHandler.DirExists(_config.App.DownloadDirectory)) { Log.Information("No download directory found. Nothing to do. {@File}", _config.App.DownloadDirectory); return; } var files = _fileHandler.GetFiles(_config.App.DownloadDirectory); if (files.Length == 0) { Log.Information("No files to convert in download directory. Nothing to do."); return; } if (_config.Garmin.Upload) { _fileHandler.MkDirIfNotExists(_config.App.UploadDirectory); } // Foreach file in directory foreach (var file in files) { using var workoutTimer = WorkoutsConverted.WithLabels(format).NewTimer(); // load file and deserialize P2GWorkout workoutData = null; try { workoutData = _fileHandler.DeserializeJson <P2GWorkout>(file); } catch (Exception e) { Log.Error(e, "Failed to load and parse workout data {@File}", file); _fileHandler.MoveFailedFile(file, _config.App.FailedDirectory); continue; } using var tracing = Tracing.Trace("Convert") .WithWorkoutId(workoutData.Workout.Id) .WithTag(TagKey.Format, format); // call internal convert method T converted = default; var workoutTitle = WorkoutHelper.GetUniqueTitle(workoutData.Workout); try { converted = Convert(workoutData.Workout, workoutData.WorkoutSamples); } catch (Exception e) { Log.Error(e, "Failed to convert workout data {@Workout} {@File}", workoutTitle, file); } if (converted is null) { _fileHandler.MoveFailedFile(file, _config.App.FailedDirectory); continue; } // write to output dir var path = Path.Join(_config.App.WorkingDirectory, $"{workoutTitle}.{format}"); try { Save(converted, path); } catch (Exception e) { Log.Error(e, "Failed to write {@Format} file for {@Workout}", format, workoutTitle); continue; } // copy to local save if (_config.Format.SaveLocalCopy) { try { _fileHandler.MkDirIfNotExists(_config.App.TcxDirectory); _fileHandler.MkDirIfNotExists(_config.App.FitDirectory); var dir = format == "fit" ? _config.App.FitDirectory : _config.App.TcxDirectory; var backupDest = Path.Join(dir, $"{workoutTitle}.{format}"); _fileHandler.Copy(path, backupDest, overwrite: true); Log.Information("Backed up file {@File}", backupDest); } catch (Exception e) { Log.Error(e, "Failed to backup {@Format} file for {@Workout}", format, workoutTitle); continue; } } // copy to upload dir if (_config.Garmin.Upload && _config.Garmin.FormatToUpload == format) { try { var uploadDest = Path.Join(_config.App.UploadDirectory, $"{workoutTitle}.{format}"); _fileHandler.Copy(path, uploadDest, overwrite: true); Log.Debug("Prepped {@Format} file {@Path} for upload.", format, uploadDest); } catch (Exception e) { Log.Error(e, "Failed to copy {@Format} file for {@Workout}", format, workoutTitle); continue; } } // update db item with conversion date SyncHistoryItem syncRecord = _dbClient.Get(workoutData.Workout.Id); if (syncRecord is null) { syncRecord = new SyncHistoryItem(workoutData.Workout) { DownloadDate = System.DateTime.Now, }; } syncRecord.ConvertedToFit = syncRecord.ConvertedToFit || format == "fit"; syncRecord.ConvertedToTcx = syncRecord.ConvertedToTcx || format == "tcx"; _dbClient.Upsert(syncRecord); } }
protected ConvertStatus Convert(FileFormat format, P2GWorkout workoutData) { using var tracing = Tracing.Trace($"{nameof(IConverter)}.{nameof(Convert)}.Workout")? .WithWorkoutId(workoutData.Workout.Id) .WithTag(TagKey.Format, format.ToString()); var status = new ConvertStatus(); // call internal convert method T converted = default; var workoutTitle = WorkoutHelper.GetUniqueTitle(workoutData.Workout); try { converted = Convert(workoutData.Workout, workoutData.WorkoutSamples); } catch (Exception e) { _logger.Error(e, "Failed to convert workout data to format {@Format} {@Workout}", format, workoutTitle); status.Success = false; status.ErrorMessage = "Failed to convert workout data."; tracing?.AddTag("excetpion.message", e.Message); tracing?.AddTag("exception.stacktrace", e.StackTrace); tracing?.AddTag("convert.success", false); tracing?.AddTag("convert.errormessage", status.ErrorMessage); return(status); } // write to output dir var path = Path.Join(_config.App.WorkingDirectory, $"{workoutTitle}.{format}"); try { _fileHandler.MkDirIfNotExists(_config.App.WorkingDirectory); Save(converted, path); status.Success = true; } catch (Exception e) { status.Success = false; status.ErrorMessage = "Failed to save converted workout for upload."; _logger.Error(e, "Failed to write {@Format} file for {@Workout}", format, workoutTitle); tracing?.AddTag("excetpion.message", e.Message); tracing?.AddTag("exception.stacktrace", e.StackTrace); tracing?.AddTag("convert.success", false); tracing?.AddTag("convert.errormessage", status.ErrorMessage); return(status); } // copy to local save try { SaveLocalCopy(path, workoutTitle); } catch (Exception e) { _logger.Error(e, "Failed to backup {@Format} file for {@Workout}", format, workoutTitle); } // copy to upload dir if (_config.Garmin.Upload && _config.Garmin.FormatToUpload == format) { try { var uploadDest = Path.Join(_config.App.UploadDirectory, $"{workoutTitle}.{format}"); _fileHandler.MkDirIfNotExists(_config.App.UploadDirectory); _fileHandler.Copy(path, uploadDest, overwrite: true); _logger.Debug("Prepped {@Format} for upload: {@Path}", format, uploadDest); } catch (Exception e) { _logger.Error(e, "Failed to copy {@Format} file for {@Workout}", format, workoutTitle); status.Success = false; status.ErrorMessage = $"Failed to save file for {@format} and workout {workoutTitle} to Upload directory"; tracing?.AddTag("excetpion.message", e.Message); tracing?.AddTag("exception.stacktrace", e.StackTrace); tracing?.AddTag("convert.success", false); tracing?.AddTag("convert.errormessage", status.ErrorMessage); return(status); } } return(status); }
public abstract ConvertStatus Convert(P2GWorkout workoutData);
public override ConvertStatus Convert(P2GWorkout workoutData) { return(base.Convert(FileFormat.Fit, workoutData)); }