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));
        }
Пример #3
0
        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;
            }
        }
Пример #4
0
        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);
            }
        }
Пример #5
0
        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);
            }
        }
Пример #6
0
        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);
        }
Пример #7
0
 public abstract ConvertStatus Convert(P2GWorkout workoutData);
 public override ConvertStatus Convert(P2GWorkout workoutData)
 {
     return(base.Convert(FileFormat.Fit, workoutData));
 }