public async Task<IEnumerable<Achievement>> Execute(Workout workout)
        {
            var offset = (int) (await _database.Single<int>(
                "select count(*) " +
                "from [Workout] " +
                "where [UserId] = @UserId", new {workout.UserId})*Percentile);
            if (offset > 0)
            {
                var threshold = await _database.Single<int>(
                    "select [Points] " +
                    "from [Workout] " +
                    "where [UserId] = @UserId " +
                    "order by [Points] desc " +
                    "offset @offset rows " +
                    "fetch next 1 rows only", new {workout.UserId, offset});
                if (workout.Points > threshold)
                {
                    return Enumerable.Repeat(
                        new Achievement
                            {
                                Type = "TopPercentilePoints",
                                IsPropped = true
                            }, 1);
                }
            }

            return Enumerable.Empty<Achievement>();
        }
        public void NoPrevious_Test()
        {
            var database = new SQLiteDatabaseService();

            var activityGrouping = new Mock<IActivityGroupingService>();
            activityGrouping.Setup(x => x.GetGroupCategory("Cycling")).Returns(ActivityCategory.Cardio);

            var workout = new Workout {Date = new DateTime(2015, 1, 1), Activities = new[] {new Activity {Group = "Cycling", Sets = new[] {new Set {Distance = 100000}}}}};

            var achievements = new LifetimeMilestoneProvider(database, activityGrouping.Object).Execute(workout).Result.ToList();

            Assert.That(achievements, Is.Empty);
        }
        public void Key_Metric_Not_Specified_Test()
        {
            var database = new SQLiteDatabaseService();

            var activityGrouping = new Mock<IActivityGroupingService>();
            activityGrouping.Setup(x => x.GetGroupCategory("Cycling")).Returns(ActivityCategory.Cardio);

            var workout = new Workout {Date = new DateTime(2015, 1, 1), Activities = new[] {new Activity {Group = "Cycling", Sets = new[] {new Set(), new Set()}}}};

            var achievements = new DailyRecordProvider(database, activityGrouping.Object).Execute(workout).Result;

            Assert.That(achievements, Is.Empty);
        }
        public void Normal_Test()
        {
            var database = new SQLiteDatabaseService();
            database.Insert(new Workout {Id = 0, Date = new DateTime(2014, 1, 1), Activities = new[] {new Activity {Name = "Cycling", Group = "Cycling", Sets = new[] {new Set {Distance = 900000}}}}});

            var activityGrouping = new Mock<IActivityGroupingService>();
            activityGrouping.Setup(x => x.GetGroupCategory("Cycling")).Returns(ActivityCategory.Cardio);

            var workout = new Workout {Date = new DateTime(2015, 1, 1), Activities = new[] {new Activity {Group = "Cycling", Sets = new[] {new Set {Distance = 100000}}}}};

            var achievements = new LifetimeMilestoneProvider(database, activityGrouping.Object).Execute(workout).Result.ToList();

            Assert.That(achievements.Count, Is.EqualTo(1));
            var achievement = achievements[0];
            Assert.That(achievement.Type, Is.EqualTo("LifetimeMilestone"));
            Assert.That(achievement.Group, Is.EqualTo("Cycling"));
            Assert.That(achievement.Distance, Is.EqualTo(1000000M));
            Assert.That(achievement.CommentText, Is.EqualTo("Lifetime Cycling milestone: 1,000 km"));
        }
        public void Imperial_Distance_Test()
        {
            var database = new SQLiteDatabaseService();
            database.Insert(new Workout {Id = 0, Date = new DateTime(2014, 1, 1), Activities = new[] {new Activity {Name = "Cycling", Group = "Cycling", Sets = new[] {new Set {Distance = 20000, Duration = 2000}}}}});

            var activityGrouping = new Mock<IActivityGroupingService>();
            activityGrouping.Setup(x => x.GetGroupCategory("Cycling")).Returns(ActivityCategory.Cardio);

            var workout = new Workout {Date = new DateTime(2015, 1, 1), Activities = new[] {new Activity {Group = "Cycling", Sets = new[] {new Set {Distance = 16100, Duration = 1000, IsImperial = true}}}}};

            var achievements = new QualifiedRecordProvider(database, activityGrouping.Object).Execute(workout).Result.ToList();

            Assert.That(achievements.Count, Is.EqualTo(1));
            var achievement = achievements[0];
            Assert.That(achievement.Type, Is.EqualTo("QualifiedRecord"));
            Assert.That(achievement.Group, Is.EqualTo("Cycling"));
            Assert.That(achievement.Speed, Is.EqualTo(16.1M));
            Assert.That(achievement.Distance, Is.EqualTo(16093.44M));
            Assert.That(achievement.CommentText, Is.EqualTo("Qualified Cycling record: 36 mph for 10 mi or more"));
        }
        public void Duplicate_Record_In_Single_Workout_Test()
        {
            var database = new SQLiteDatabaseService();
            database.Insert(new Workout {Id = 0, Date = new DateTime(2014, 1, 1), Activities = new[] {new Activity {Name = "Squats", Group = "Squats", Sets = new[] {new Set {Weight = 100, Repetitions = 1}}}}});

            var activityGrouping = new Mock<IActivityGroupingService>();
            activityGrouping.Setup(x => x.GetGroupCategory("Squats")).Returns(ActivityCategory.Weights);

            var workout = new Workout {Date = new DateTime(2015, 1, 1), Activities = new[] {new Activity {Group = "Squats", Sets = new[] {new Set {Weight = 50, Repetitions = 2}}}, new Activity {Sequence = 1, Group = "Squats", Sets = new[] {new Set {Weight = 50, Repetitions = 2}}}}};

            var achievements = new QualifiedRecordProvider(database, activityGrouping.Object).Execute(workout).Result.ToList();

            Assert.That(achievements.Count, Is.EqualTo(1));
            var achievement = achievements[0];
            Assert.That(achievement.Type, Is.EqualTo("QualifiedRecord"));
            Assert.That(achievement.Group, Is.EqualTo("Squats"));
            Assert.That(achievement.Weight, Is.EqualTo(50M));
            Assert.That(achievement.Repetitions, Is.EqualTo(2M));
            Assert.That(achievement.CommentText, Is.EqualTo("Qualified Squats record: 2 reps at 50 kg or more"));
        }
        public void Equal_To_Old_Weights_Record_Test()
        {
            var database = new SQLiteDatabaseService();
            database.Insert(new Workout {Id = 0, Date = new DateTime(2013, 1, 1), Activities = new[] {new Activity {Name = "Squats", Sets = new[] {new Set {Weight = 2}}}}});
            database.Insert(new Workout {Id = 1, Date = new DateTime(2014, 1, 1), Activities = new[] {new Activity {Name = "Squats", Sets = new[] {new Set {Weight = 1}}}}});

            var activityGrouping = new Mock<IActivityGroupingService>();
            activityGrouping.Setup(x => x.GetGroupCategory("Squats")).Returns(ActivityCategory.Weights);

            var workout = new Workout {Date = new DateTime(2015, 1, 1), Activities = new[] {new Activity {Name = "Squats", Group = "Squats", Sets = new[] {new Set {Weight = 2}}}}};

            var achievements = new ComebackRecordProvider(database, activityGrouping.Object).Execute(workout).Result.ToList();

            Assert.That(achievements.Count, Is.EqualTo(1));
            var achievement = achievements[0];
            Assert.That(achievement.Type, Is.EqualTo("ComebackRecord"));
            Assert.That(achievement.Activity, Is.EqualTo("Squats"));
            Assert.That(achievement.Weight, Is.EqualTo(2M));
            Assert.That(achievement.CommentText, Is.EqualTo("1 year Squats comeback record: 2 kg"));
        }
        public void Duplicate_Across_Pages_Test()
        {
            var database = new Mock<IDatabaseService>();
            database.Setup(x => x.GetWorkouts(0, new DateTime(2014, 1, 1), DateTime.MaxValue)).Returns(Task.FromResult<IEnumerable<Workout>>(new Workout[0]));

            var fitocracy = new Mock<IFitocracyService>();
            var workout2 = new Workout {Id = 2, Date = new DateTime(2014, 1, 2), Activities = new Activity[0]};
            var workout1 = new Workout {Id = 1, Date = new DateTime(2014, 1, 1), Activities = new Activity[0]};
            fitocracy.Setup(x => x.GetWorkouts(0, 0)).Returns(Task.FromResult<IList<Workout>>(new[] {workout2}));
            fitocracy.Setup(x => x.GetWorkouts(0, 1)).Returns(Task.FromResult<IList<Workout>>(new[] {workout2, workout1}));
            fitocracy.Setup(x => x.GetWorkouts(0, 3)).Returns(Task.FromResult<IList<Workout>>(new Workout[0]));

            var activityGrouping = new Mock<IActivityGroupingService>();

            var workouts = new WorkoutPullService(database.Object, fitocracy.Object, activityGrouping.Object).Pull(new User()).Result;

            Assert.That(workouts, Is.EqualTo(new[] {workout1, workout2}));
            database.Verify(x => x.Insert(workout1));
            database.Verify(x => x.Insert(workout2));
            database.Verify(x => x.Update(It.IsAny<Workout>(), It.IsAny<bool>()), Times.Never);
            database.Verify(x => x.Delete(It.IsAny<Workout>()), Times.Never);
        }
        public void Very_Small_Weight_Test()
        {
            var database = new SQLiteDatabaseService();
            database.Insert(new Workout {Id = 0, Date = new DateTime(2014, 1, 1), Activities = new[] {new Activity {Name = "Squats", Group = "Squats", Sets = new[] {new Set {Weight = 2, Repetitions = 1}}}}});

            var activityGrouping = new Mock<IActivityGroupingService>();
            activityGrouping.Setup(x => x.GetGroupCategory("Squats")).Returns(ActivityCategory.Weights);

            var workout = new Workout {Date = new DateTime(2015, 1, 1), Activities = new[] {new Activity {Group = "Squats", Sets = new[] {new Set {Weight = 0.9M, Repetitions = 2}}}}};

            var achievements = new QualifiedRecordProvider(database, activityGrouping.Object).Execute(workout).Result;

            Assert.That(achievements, Is.Empty);
        }
        public void Very_Small_Distance_Test()
        {
            var database = new SQLiteDatabaseService();
            database.Insert(new Workout {Id = 0, Date = new DateTime(2014, 1, 1), Activities = new[] {new Activity {Name = "Cycling", Group = "Cycling", Sets = new[] {new Set {Distance = 2000, Speed = 1}}}}});

            var activityGrouping = new Mock<IActivityGroupingService>();
            activityGrouping.Setup(x => x.GetGroupCategory("Cycling")).Returns(ActivityCategory.Cardio);

            var workout = new Workout {Date = new DateTime(2015, 1, 1), Activities = new[] {new Activity {Group = "Cycling", Sets = new[] {new Set {Distance = 900, Speed = 2}}}}};

            var achievements = new QualifiedRecordProvider(database, activityGrouping.Object).Execute(workout).Result;

            Assert.That(achievements, Is.Empty);
        }
        public async Task<IEnumerable<Achievement>> Execute(Workout workout)
        {
            var achievements = new List<Achievement>();

            foreach (var group in workout.Activities.GroupBy(activity => activity.Group).Where(group => group.Key != null))
            {
                var category = _grouping.GetGroupCategory(group.Key);
                if (category == null)
                {
                    continue;
                }

                int threshold;
                if (!Thresholds.TryGetValue(group.Key, out threshold))
                {
                    switch (category)
                    {
                        case ActivityCategory.Cardio:
                            threshold = 500;
                            break;
                        case ActivityCategory.Sports:
                            threshold = 100;
                            break;
                        default:
                            threshold = 2000;
                            break;
                    }
                }

                string column;
                switch (category)
                {
                    case ActivityCategory.Cardio:
                        column = "Distance";
                        threshold *= 1000;
                        break;
                    case ActivityCategory.Sports:
                        column = "Duration";
                        threshold *= 3600;
                        break;
                    default:
                        column = "Repetitions";
                        break;
                }

                var previousSum = await _database.Single<decimal?>(
                    "select sum([" + column + "]) " +
                    "from [Workout] w, [Activity] a, [Set] s " +
                    "where w.[Id] = a.[WorkoutId] " +
                    "and a.[Id] = s.[ActivityId] " +
                    "and w.[UserId] = @UserId " +
                    "and w.[Date] < @Date " +
                    "and a.[Group] = @Key", new {workout.UserId, workout.Date, group.Key});
                if (previousSum == null)
                {
                    continue;
                }

                var sum = group.SelectMany(activity => activity.Sets)
                               .Sum(set =>
                                   {
                                       switch (category)
                                       {
                                           case ActivityCategory.Cardio:
                                               return set.Distance;
                                           case ActivityCategory.Sports:
                                               return set.Duration;
                                           default:
                                               return set.Repetitions;
                                       }
                                   }) + previousSum;
                if (sum < threshold)
                {
                    continue;
                }

                previousSum = Math.Floor(previousSum.Value/threshold)*threshold;
                sum = Math.Floor(sum.Value/threshold)*threshold;
                if (sum == previousSum)
                {
                    continue;
                }

                var achievement = new Achievement
                    {
                        Type = "LifetimeMilestone",
                        Group = group.Key
                    };
                string formattedValue;
                switch (category)
                {
                    case ActivityCategory.Cardio:
                        achievement.Distance = sum;
                        formattedValue = sum.FormatDistance();
                        break;
                    case ActivityCategory.Sports:
                        achievement.Duration = sum;
                        formattedValue = sum.FormatDuration();
                        break;
                    default:
                        achievement.Repetitions = sum;
                        formattedValue = sum.FormatRepetitions();
                        break;
                }
                achievement.CommentText = $"Lifetime {group.Key} milestone: {formattedValue}";
                achievements.Add(achievement);
            }

            return achievements;
        }
        public void Single_Update_Shallow_Test()
        {
            var database = new Mock<IDatabaseService>();
            var workout1 = new Workout {Id = 1, Date = new DateTime(2014, 1, 1), Points = 1, Activities = new Activity[0]};
            database.Setup(x => x.GetWorkouts(0, new DateTime(2014, 1, 1), DateTime.MaxValue)).Returns(Task.FromResult<IEnumerable<Workout>>(new[] {workout1}));

            var fitocracy = new Mock<IFitocracyService>();
            var workout1A = new Workout {Id = 1, Date = new DateTime(2014, 1, 1), Points = 2, Activities = new Activity[0]};
            fitocracy.Setup(x => x.GetWorkouts(0, 0)).Returns(Task.FromResult<IList<Workout>>(new[] {workout1A}));
            fitocracy.Setup(x => x.GetWorkouts(0, 1)).Returns(Task.FromResult<IList<Workout>>(new Workout[0]));

            var activityGrouping = new Mock<IActivityGroupingService>();

            var workouts = new WorkoutPullService(database.Object, fitocracy.Object, activityGrouping.Object).Pull(new User()).Result;

            Assert.That(workouts, Is.EqualTo(new[] {workout1A}));
            database.Verify(x => x.Update(workout1A, false));
            database.Verify(x => x.Insert(It.IsAny<Workout>()), Times.Never);
            database.Verify(x => x.Delete(It.IsAny<Workout>()), Times.Never);
        }
        public async Task<IEnumerable<Achievement>> Execute(Workout workout)
        {
            var achievements = new List<Achievement>();

            foreach (var group in workout.Activities.GroupBy(activity => activity.Group).Where(group => group.Key != null))
            {
                switch (_grouping.GetGroupCategory(group.Key))
                {
                    case ActivityCategory.Cardio:
                        {
                            var sets = group.SelectMany(activity => activity.Sets
                                                                            .Select(set => new
                                                                                {
                                                                                    ActivitySeq = activity.Sequence,
                                                                                    SetSeq = set.Sequence,
                                                                                    set.IsImperial,
                                                                                    Speed = set.Speed ?? Round(set.Distance/set.Duration),
                                                                                    Distance = Truncate(set.Distance ?? set.Speed*set.Duration, set.IsImperial ? KilometersPerMile : 0)
                                                                                }))
                                            .Where(set => (set.Speed ?? 0) > 0 && (set.Distance ?? 0) >= 1000)
                                            .ToList();
                            foreach (var set in sets)
                            {
                                if (sets.Any(item => item != set &&
                                                     ((item.Distance >= set.Distance && item.Speed > set.Speed) ||
                                                      (item.Distance > set.Distance && item.Speed >= set.Speed) ||
                                                      (item.Distance == set.Distance && item.Speed == set.Speed &&
                                                       (item.ActivitySeq > set.ActivitySeq ||
                                                        item.ActivitySeq == set.ActivitySeq && item.SetSeq > set.SetSeq)))))
                                {
                                    continue;
                                }

                                var previousMax = await _database.Single<decimal?>(
                                    "select max(coalesce(s.[Speed], s.[Distance]/s.[Duration])) " +
                                    "from [Workout] w, [Activity] a, [Set] s " +
                                    "where w.[Id] = a.[WorkoutId] " +
                                    "and a.[Id] = s.[ActivityId] " +
                                    "and w.[UserId] = @UserId " +
                                    "and w.[Date] < @Date " +
                                    "and a.[Group] = @Key " +
                                    "and coalesce(s.[Distance], s.[Speed]*s.[Duration]) >= @Distance", new {workout.UserId, workout.Date, group.Key, set.Distance});
                                if (set.Speed > Round(previousMax))
                                {
                                    achievements.Add(
                                        new Achievement
                                            {
                                                Type = "QualifiedRecord",
                                                Group = group.Key,
                                                Speed = set.Speed,
                                                Distance = set.Distance,
                                                CommentText = $"Qualified {group.Key} record: {set.Speed.FormatSpeed(set.IsImperial)} for {set.Distance.FormatDistance(set.IsImperial)} or more"
                                            });
                                }
                            }
                        }
                        break;
                    case ActivityCategory.Weights:
                        {
                            var sets = group.SelectMany(activity => activity.Sets
                                                                            .Select(set => new
                                                                                {
                                                                                    ActivitySeq = activity.Sequence,
                                                                                    SetSeq = set.Sequence,
                                                                                    set.IsImperial,
                                                                                    set.Repetitions,
                                                                                    Weight = Truncate(set.Weight, set.IsImperial ? KilogramsPerPound : 0)
                                                                                }))
                                            .Where(set => (set.Repetitions ?? 0) > 0 && (set.Weight ?? 0) >= 1)
                                            .ToList();
                            foreach (var set in sets)
                            {
                                if (sets.Any(item => item != set &&
                                                     ((item.Weight >= set.Weight && item.Repetitions > set.Repetitions) ||
                                                      (item.Weight > set.Weight && item.Repetitions >= set.Repetitions) ||
                                                      (item.Weight == set.Weight && item.Repetitions == set.Repetitions &&
                                                       (item.ActivitySeq > set.ActivitySeq ||
                                                        item.ActivitySeq == set.ActivitySeq && item.SetSeq > set.SetSeq)))))
                                {
                                    continue;
                                }

                                var previousMax = await _database.Single<decimal?>(
                                    "select max(s.[Repetitions]) " +
                                    "from [Workout] w, [Activity] a, [Set] s " +
                                    "where w.[Id] = a.[WorkoutId] " +
                                    "and a.[Id] = s.[ActivityId] " +
                                    "and w.[UserId] = @UserId " +
                                    "and w.[Date] < @Date " +
                                    "and a.[Group] = @Key " +
                                    "and s.[Weight] >= @Weight", new {workout.UserId, workout.Date, group.Key, set.Weight});
                                if (set.Repetitions > previousMax)
                                {
                                    achievements.Add(
                                        new Achievement
                                            {
                                                Type = "QualifiedRecord",
                                                Group = group.Key,
                                                Repetitions = set.Repetitions,
                                                Weight = set.Weight,
                                                CommentText = $"Qualified {group.Key} record: {set.Repetitions.FormatRepetitions()} at {set.Weight.FormatWeight(set.IsImperial)} or more"
                                            });
                                }
                            }
                        }
                        break;
                }
            }

            return achievements;
        }
        public async Task<IEnumerable<Achievement>> Execute(Workout workout)
        {
            var achievements = new List<Achievement>();

            foreach (var activity in workout.Activities
                                            .Where(activity => activity.Group != null)
                                            .GroupBy(activity => new {activity.Group, activity.Name})
                                            .Select(group => new {group.Key.Group, group.Key.Name, Sets = group.SelectMany(activity => activity.Sets).ToList()})
                                            .Where(activity => !activity.Sets.Any(set => set.IsPr)))
            {
                var category = _grouping.GetGroupCategory(activity.Group);
                if (category == null)
                {
                    continue;
                }

                if (category == ActivityCategory.Weights)
                {
                    var max = activity.Sets
                                      .OrderByDescending(set => set.Weight)
                                      .ThenByDescending(set => set.Repetitions)
                                      .FirstOrDefault();
                    if (max == null || (max.Weight == null && max.Repetitions == null))
                    {
                        continue;
                    }

                    var fromDate = workout.Date.AddYears(-1);
                    var lastYearMax = await _database.Single<dynamic>(
                        "select top 1 s.[Weight], s.[Repetitions] " +
                        "from [Workout] w, [Activity] a, [Set] s " +
                        "where w.[Id] = a.[WorkoutId] " +
                        "and a.[Id] = s.[ActivityId] " +
                        "and w.[UserId] = @UserId " +
                        "and w.[Date] < @fromDate " +
                        "and a.[Name] = @Name " +
                        "order by s.[Weight] desc, s.[Repetitions] desc", new {workout.UserId, fromDate, activity.Name});
                    if (lastYearMax == null ||
                        max.Weight > lastYearMax.Weight ||
                        (max.Weight == lastYearMax.Weight && max.Repetitions > lastYearMax.Repetitions) ||
                        max.Weight*2 <= lastYearMax.Weight)
                    {
                        continue;
                    }

                    var thisYearMax = await _database.Single<dynamic>(
                        "select top 1 s.[Weight], s.[Repetitions] " +
                        "from [Workout] w, [Activity] a, [Set] s " +
                        "where w.[Id] = a.[WorkoutId] " +
                        "and a.[Id] = s.[ActivityId] " +
                        "and w.[UserId] = @UserId " +
                        "and w.[Date] >= @fromDate " +
                        "and w.[Date] < @Date " +
                        "and a.[Name] = @Name " +
                        "and s.[Weight] " + (max.Weight != null ? ">= @Weight" : "is null") + " " +
                        "order by s.[Weight] desc, s.[Repetitions] desc", new {workout.UserId, fromDate, workout.Date, activity.Name, max.Weight});
                    if (thisYearMax != null && (max.Weight < thisYearMax.Weight || max.Repetitions <= thisYearMax.Repetitions))
                    {
                        continue;
                    }

                    var achievement = new Achievement
                        {
                            Type = "ComebackRecord",
                            Activity = activity.Name,
                            CommentText = $"1 year {activity.Name} comeback record: "
                        };
                    if (max.Weight == null)
                    {
                        achievement.Repetitions = max.Repetitions;
                        achievement.CommentText += max.Repetitions.FormatRepetitions();
                    }
                    else
                    {
                        achievement.Weight = max.Weight;
                        achievement.CommentText += max.Weight.FormatWeight(max.IsImperial);
                        if (thisYearMax != null && max.Weight == thisYearMax.Weight)
                        {
                            achievement.Repetitions = max.Repetitions;
                            achievement.CommentText += " for " + max.Repetitions.FormatRepetitions();
                        }
                    }
                    achievements.Add(achievement);
                }
                else
                {
                    string column;
                    switch (category)
                    {
                        case ActivityCategory.Cardio:
                            column = "Distance";
                            break;
                        case ActivityCategory.Bodyweight:
                            column = "Repetitions";
                            break;
                        case ActivityCategory.Sports:
                            column = "Duration";
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }

                    var max = activity.Sets
                                      .Select(set =>
                                          {
                                              switch (category)
                                              {
                                                  case ActivityCategory.Cardio:
                                                      return new {Value = set.Distance, set.IsImperial};
                                                  case ActivityCategory.Bodyweight:
                                                      return new {Value = set.Repetitions, set.IsImperial};
                                                  case ActivityCategory.Sports:
                                                      return new {Value = set.Duration, set.IsImperial};
                                                  default:
                                                      throw new ArgumentOutOfRangeException();
                                              }
                                          })
                                      .OrderByDescending(set => set.Value)
                                      .FirstOrDefault();
                    if (max?.Value == null)
                    {
                        continue;
                    }

                    var fromDate = workout.Date.AddYears(-1);
                    var lastYearMax = await _database.Single<decimal?>(
                        "select max(s.[" + column + "]) " +
                        "from [Workout] w, [Activity] a, [Set] s " +
                        "where w.[Id] = a.[WorkoutId] " +
                        "and a.[Id] = s.[ActivityId] " +
                        "and w.[UserId] = @UserId " +
                        "and w.[Date] < @fromDate " +
                        "and a.[Name] = @Name", new {workout.UserId, fromDate, activity.Name});
                    if (lastYearMax == null || max.Value > lastYearMax || max.Value*2 <= lastYearMax)
                    {
                        continue;
                    }

                    var thisYearCount = await _database.Single<int>(
                        "select count(*) " +
                        "from [Workout] w, [Activity] a, [Set] s " +
                        "where w.[Id] = a.[WorkoutId] " +
                        "and a.[Id] = s.[ActivityId] " +
                        "and w.[UserId] = @UserId " +
                        "and w.[Date] >= @fromDate " +
                        "and w.[Date] < @Date " +
                        "and a.[Name] = @Name " +
                        "and s.[" + column + "] >= @Value", new {workout.UserId, fromDate, workout.Date, activity.Name, max.Value});
                    if (thisYearCount > 0)
                    {
                        continue;
                    }

                    var achievement = new Achievement
                        {
                            Type = "ComebackRecord",
                            Activity = activity.Name
                        };
                    string formattedValue;
                    switch (category)
                    {
                        case ActivityCategory.Cardio:
                            achievement.Distance = max.Value;
                            formattedValue = max.Value.FormatDistance(max.IsImperial);
                            break;
                        case ActivityCategory.Bodyweight:
                            achievement.Repetitions = max.Value;
                            formattedValue = max.Value.FormatRepetitions();
                            break;
                        case ActivityCategory.Sports:
                            achievement.Duration = max.Value;
                            formattedValue = max.Value.FormatDuration();
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }
                    achievement.CommentText = $"1 year {activity.Name} comeback record: {formattedValue}";
                    achievements.Add(achievement);
                }
            }

            return achievements;
        }
        public async Task<IEnumerable<Achievement>> Execute(Workout workout)
        {
            var achievements = new List<Achievement>();

            foreach (var group in workout.Activities.GroupBy(activity => activity.Group).Where(group => group.Key != null))
            {
                var category = _grouping.GetGroupCategory(group.Key);
                if (category == null)
                {
                    continue;
                }

                var sets = group.SelectMany(activity => activity.Sets).ToList();
                if (sets.Count <= 1)
                {
                    continue;
                }

                var sum = sets.Sum(set =>
                    {
                        switch (category)
                        {
                            case ActivityCategory.Cardio:
                                return set.Distance;
                            case ActivityCategory.Sports:
                                return set.Duration;
                            default:
                                return set.Repetitions;
                        }
                    });
                if (sum == 0 || (category == ActivityCategory.Cardio && sum < 1000))
                {
                    continue;
                }

                string column;
                switch (category)
                {
                    case ActivityCategory.Cardio:
                        column = "Distance";
                        break;
                    case ActivityCategory.Sports:
                        column = "Duration";
                        break;
                    default:
                        column = "Repetitions";
                        break;
                }

                var previousMax = await _database.Single<decimal?>(
                    "select max([Value]) " +
                    "from ( " +
                    "  select sum(s.[" + column + "]) [Value] " +
                    "  from [Workout] w, [Activity] a, [Set] s " +
                    "  where w.[Id] = a.[WorkoutId] " +
                    "  and a.[Id] = s.[ActivityId] " +
                    "  and w.[UserId] = @UserId " +
                    "  and w.[Date] < @Date " +
                    "  and a.[Group] = @Key " +
                    "  group by w.[Id] " +
                    ") [x]", new {workout.UserId, workout.Date, group.Key});
                if ((previousMax ?? 0) == 0 || sum <= previousMax)
                {
                    continue;
                }

                var achievement = new Achievement
                    {
                        Type = "DailyRecord",
                        Group = group.Key
                    };
                string formattedValue;
                switch (category)
                {
                    case ActivityCategory.Cardio:
                        achievement.Distance = sum;
                        var lookup = sets.ToLookup(set => set.IsImperial);
                        formattedValue = sum.FormatDistance(lookup[true].Count() > lookup[false].Count());
                        break;
                    case ActivityCategory.Sports:
                        achievement.Duration = sum;
                        formattedValue = sum.FormatDuration();
                        break;
                    default:
                        achievement.Repetitions = sum;
                        formattedValue = sum.FormatRepetitions();
                        break;
                }
                achievement.CommentText = $"Daily {group.Key} record: {formattedValue}";
                achievements.Add(achievement);
            }

            return achievements;
        }
Example #16
0
 public bool HasChanges(Workout workout)
 {
     return Date != workout.Date ||
            Points != workout.Points ||
            ActivitiesHash != workout.ActivitiesHash;
 }
        public void Normal_Test()
        {
            var database = new SQLiteDatabaseService();
            database.Insert(new Workout {Id = 0, Date = new DateTime(2013, 1, 1), Activities = new[] {new Activity {Name = "Cycling", Sets = new[] {new Set {Distance = 3000}}}}});
            database.Insert(new Workout {Id = 1, Date = new DateTime(2014, 1, 1), Activities = new[] {new Activity {Name = "Cycling", Sets = new[] {new Set {Distance = 1000}}}}});

            var activityGrouping = new Mock<IActivityGroupingService>();
            activityGrouping.Setup(x => x.GetGroupCategory("Cycling")).Returns(ActivityCategory.Cardio);

            var workout = new Workout {Date = new DateTime(2015, 1, 1), Activities = new[] {new Activity {Name = "Cycling", Group = "Cycling", Sets = new[] {new Set {Distance = 2000}}}}};

            var achievements = new ComebackRecordProvider(database, activityGrouping.Object).Execute(workout).Result.ToList();

            Assert.That(achievements.Count, Is.EqualTo(1));
            var achievement = achievements[0];
            Assert.That(achievement.Type, Is.EqualTo("ComebackRecord"));
            Assert.That(achievement.Activity, Is.EqualTo("Cycling"));
            Assert.That(achievement.Distance, Is.EqualTo(2000M));
            Assert.That(achievement.CommentText, Is.EqualTo("1 year Cycling comeback record: 2 km"));
        }