/// <summary>
        /// Gets the attempt from the streak.
        /// </summary>
        /// <param name="computedStreak">The computed streak.</param>
        /// <param name="targetCount">The target count.</param>
        /// <param name="isClosed">if set to <c>true</c> [is closed].</param>
        /// <returns></returns>
        private static AchievementAttempt GetAttemptFromStreak(ComputedStreak computedStreak, int targetCount, bool isClosed)
        {
            var attempt = new AchievementAttempt();

            ApplyStreakToAttempt(computedStreak, attempt, targetCount, isClosed);
            return(attempt);
        }
        /// <summary>
        /// Gets the attempt from the streak
        /// </summary>
        /// <param name="computedStreak">The computed streak.</param>
        /// <param name="attempt">The attempt.</param>
        /// <param name="targetCount">The target count.</param>
        /// <param name="isClosed">if set to <c>true</c> [is closed].</param>
        private static void ApplyStreakToAttempt(ComputedStreak computedStreak, AchievementAttempt attempt, int targetCount, bool isClosed)
        {
            var progress = CalculateProgress(computedStreak.Count, targetCount);

            attempt.AchievementAttemptStartDateTime = computedStreak.StartDate;
            attempt.AchievementAttemptEndDateTime   = computedStreak.EndDate;
            attempt.Progress     = progress;
            attempt.IsClosed     = isClosed;
            attempt.IsSuccessful = progress >= 1m;
        }
Example #3
0
        /// <summary>
        /// Gets the attempt from the accumulation
        /// </summary>
        /// <param name="accumulation">The accumulation.</param>
        /// <param name="targetCount">The target count.</param>
        /// <param name="isClosed">if set to <c>true</c> [is closed].</param>
        /// <returns></returns>
        private static AchievementAttempt GetAttempt(ComputedStreak accumulation, int targetCount, bool isClosed)
        {
            var progress = CalculateProgress(accumulation.Count, targetCount);

            return(new AchievementAttempt
            {
                AchievementAttemptStartDateTime = accumulation.StartDate,
                AchievementAttemptEndDateTime = accumulation.EndDate,
                Progress = progress,
                IsClosed = isClosed,
                IsSuccessful = progress >= 1m
            });
        }
        /// <summary>
        /// Update the open attempt record if there are changes.
        /// </summary>
        /// <param name="openAttempt"></param>
        /// <param name="achievementTypeCache">The achievement type cache.</param>
        /// <param name="streak">The streak.</param>
        protected override void UpdateOpenAttempt(AchievementAttempt openAttempt, AchievementTypeCache achievementTypeCache, Streak streak)
        {
            var rockContext       = new RockContext();
            var streakTypeService = new StreakTypeService(rockContext);
            var streakTypeCache   = GetStreakTypeCache(achievementTypeCache);

            // Validate the attribute values
            var numberToAchieve = GetAttributeValue(achievementTypeCache, AttributeKey.NumberToAchieve).AsInteger();

            if (numberToAchieve <= 0)
            {
                ExceptionLogService.LogException($"StreakAchievement.UpdateOpenAttempt cannot process because the numberToAchieve attribute is less than 1");
                return;
            }

            var attributeTimespanDays = GetAttributeValue(achievementTypeCache, AttributeKey.TimespanInDays).AsIntegerOrNull();

            if (attributeTimespanDays.HasValue && attributeTimespanDays.Value <= 0)
            {
                ExceptionLogService.LogException($"StreakAchievement.UpdateOpenAttempt cannot process because the TimespanInDays attribute is less than 1");
                return;
            }

            // Calculate the date range where the open attempt can be validly fulfilled
            var attributeMaxDate = GetAttributeValue(achievementTypeCache, AttributeKey.EndDateTime).AsDateTime();
            var minDate          = openAttempt.AchievementAttemptStartDateTime;
            var maxDate          = CalculateMaxDateForAchievementAttempt(minDate, attributeMaxDate);

            // Get the max date that streaks can be broken. This is to avoid breaking streaks while people still have time to
            // engage in that day or week (because it is the current day or week)
            var maxDateForStreakBreaking = StreakTypeService.GetMaxDateForStreakBreaking(streakTypeCache);

            // Track the streak
            var computedStreak = new ComputedStreak(minDate)
            {
                EndDate = minDate
            };

            // Define what happens for each bit in the date range
            bool iterationAction(int currentUnit, DateTime currentDate, bool hasOccurrence, bool hasEngagement, bool hasExclusion)
            {
                var iterationCanStop = false;

                // If there is an engagement, then increment the streak
                if (hasOccurrence && hasEngagement)
                {
                    computedStreak.Count++;
                    computedStreak.EndDate = currentDate;

                    // Check for a fulfilled attempt
                    if (computedStreak.Count >= numberToAchieve)
                    {
                        ApplyStreakToAttempt(computedStreak, openAttempt, numberToAchieve, !achievementTypeCache.AllowOverAchievement);
                        iterationCanStop = !achievementTypeCache.AllowOverAchievement;
                    }
                }
                else if (hasOccurrence && !hasEngagement && !hasExclusion)
                {
                    // Break the streak and close the attempt if there is an unexcused absence
                    ApplyStreakToAttempt(computedStreak, openAttempt, numberToAchieve, currentDate <= maxDateForStreakBreaking);
                    iterationCanStop = true;
                }

                // If there is a timespan and this streak is too old, then the attempt is closed
                if (attributeTimespanDays.HasValue)
                {
                    var inclusiveAge = (currentDate - computedStreak.StartDate).Days + 1;

                    if (inclusiveAge >= attributeTimespanDays.Value)
                    {
                        ApplyStreakToAttempt(computedStreak, openAttempt, numberToAchieve, currentDate <= maxDateForStreakBreaking);
                        iterationCanStop = true;
                    }
                }

                return(iterationCanStop);
            }

            // Iterate through the streak date for the date range specified
            streakTypeService.IterateStreakMap(streakTypeCache, streak.PersonAliasId, minDate, maxDate, iterationAction, out var errorMessage);

            if (!errorMessage.IsNullOrWhiteSpace())
            {
                ExceptionLogService.LogException($"StreakAchievement.UpdateOpenAttempt got an error calling StreakTypeService.IterateStreakMap: {errorMessage}");
                return;
            }

            // If the attempt wasn't closed in the iteration, then it will remain open
            if (!openAttempt.IsClosed)
            {
                var progress = CalculateProgress(computedStreak.Count, numberToAchieve);

                openAttempt.Progress     = progress;
                openAttempt.IsSuccessful = progress >= 1m;
            }
        }
Example #5
0
        /// <summary>
        /// Create new attempt records and return them in a list. All new attempts should be after the most recent successful attempt.
        /// </summary>
        /// <param name="achievementTypeCache">The achievement type cache.</param>
        /// <param name="transaction">The financial transaction.</param>
        /// <param name="mostRecentSuccess">The most recent successful attempt.</param>
        /// <returns></returns>
        private List <AchievementAttempt> CreateNewAttempts(AchievementTypeCache achievementTypeCache, FinancialTransaction transaction, AchievementAttempt mostRecentSuccess)
        {
            // Validate the attribute values
            var numberToAccumulate = GetAttributeValue(achievementTypeCache, AttributeKey.NumberToAccumulate).AsInteger();

            if (numberToAccumulate <= 0)
            {
                ExceptionLogService.LogException($"{GetType().Name}. CreateNewAttempts cannot process because the NumberToAccumulate attribute is less than 1");
                return(null);
            }

            // Calculate the date range where new achievements can be validly found
            var attributeMinDate = GetAttributeValue(achievementTypeCache, AttributeKey.StartDateTime).AsDateTime();
            var attributeMaxDate = GetAttributeValue(achievementTypeCache, AttributeKey.EndDateTime).AsDateTime();
            var minDate          = CalculateMinDateForAchievementAttempt(DateTime.MinValue, mostRecentSuccess, attributeMinDate, numberToAccumulate);
            var maxDate          = CalculateMaxDateForAchievementAttempt(minDate, attributeMaxDate);

            // Track the attempts in a list that will be returned
            var            attempts     = new List <AchievementAttempt>();
            ComputedStreak accumulation = null;

            // Get the transaction dates and begin calculating attempts
            var transactionDates = GetOrderedFinancialTransactionDatesByPerson(achievementTypeCache, transaction.AuthorizedPersonAlias.PersonId, minDate, maxDate);

            foreach (var transactionDate in transactionDates)
            {
                if (!transactionDate.HasValue)
                {
                    // Nothing we can do without a date
                    continue;
                }

                if (accumulation == null)
                {
                    accumulation = new ComputedStreak(transactionDate.Value);
                }

                // Increment the accumulation
                accumulation.Count++;
                accumulation.EndDate = transactionDate;

                // Check for a fulfilled attempt
                if (accumulation.Count >= numberToAccumulate)
                {
                    attempts.Add(GetAttempt(accumulation, numberToAccumulate, true));

                    if (!achievementTypeCache.AllowOverAchievement)
                    {
                        accumulation = null;
                    }
                }
            }

            // The leftover accumulation is an open attempt
            if (accumulation != null)
            {
                var openAttempt = GetAttempt(accumulation, numberToAccumulate, false);
                var lastAttempt = attempts.LastOrDefault();

                if (null == lastAttempt ||
                    openAttempt.Progress != lastAttempt.Progress ||
                    openAttempt.AchievementAttemptStartDateTime != lastAttempt.AchievementAttemptStartDateTime ||
                    openAttempt.AchievementAttemptEndDateTime != lastAttempt.AchievementAttemptEndDateTime)
                {
                    attempts.Add(openAttempt);
                }
            }

            return(attempts);
        }
Example #6
0
        /// <summary>
        /// Update the open attempt record if there are changes.
        /// </summary>
        /// <param name="openAttempt"></param>
        /// <param name="streakTypeAchievementTypeCache">The streak type achievement type cache.</param>
        /// <param name="streak">The streak.</param>
        protected override void UpdateOpenAttempt(StreakAchievementAttempt openAttempt, StreakTypeAchievementTypeCache streakTypeAchievementTypeCache, Streak streak)
        {
            var rockContext       = new RockContext();
            var streakTypeService = new StreakTypeService(rockContext);
            var streakTypeCache   = streakTypeAchievementTypeCache.StreakTypeCache;

            // Validate the attribute values
            var numberToAccumulate = GetAttributeValue(streakTypeAchievementTypeCache, AttributeKey.NumberToAccumulate).AsInteger();

            if (numberToAccumulate <= 0)
            {
                ExceptionLogService.LogException($"AccumulativeAchievement.UpdateOpenAttempt cannot process because the NumberToAccumulate attribute is less than 1");
                return;
            }

            var attributeTimespanDays = GetAttributeValue(streakTypeAchievementTypeCache, AttributeKey.TimespanInDays).AsIntegerOrNull();

            if (attributeTimespanDays.HasValue && attributeTimespanDays.Value <= 0)
            {
                ExceptionLogService.LogException($"AccumulativeAchievement.UpdateOpenAttempt cannot process because the TimespanInDays attribute is less than 1");
                return;
            }

            // Calculate the date range where the open attempt can be validly fulfilled
            var attributeMaxDate = GetAttributeValue(streakTypeAchievementTypeCache, AttributeKey.EndDateTime).AsDateTime();
            var minDate          = openAttempt.AchievementAttemptStartDateTime;
            var maxDate          = CalculateMaxDateForAchievementAttempt(minDate, attributeMaxDate);

            // Track the accumulation
            var accumulation = new ComputedStreak(minDate)
            {
                EndDate = minDate
            };

            // Define what happens for each bit in the date range
            bool iterationAction(int currentUnit, DateTime currentDate, bool hasOccurrence, bool hasEngagement, bool hasExclusion)
            {
                // If there is an engagement, then increment the accumulation
                if (hasOccurrence && hasEngagement)
                {
                    accumulation.Count++;
                    accumulation.EndDate = currentDate;

                    // Check for a fulfilled attempt
                    if (accumulation.Count >= numberToAccumulate)
                    {
                        var progress = CalculateProgress(accumulation.Count, numberToAccumulate);

                        openAttempt.AchievementAttemptEndDateTime = accumulation.EndDate;
                        openAttempt.Progress     = progress;
                        openAttempt.IsClosed     = !streakTypeAchievementTypeCache.AllowOverAchievement;
                        openAttempt.IsSuccessful = progress >= 1m;
                    }
                }

                // If there is a timespan and this accumulation is too old, then the attempt is closed
                if (attributeTimespanDays.HasValue)
                {
                    var inclusiveAge = (currentDate - accumulation.StartDate).Days + 1;

                    if (inclusiveAge >= attributeTimespanDays.Value)
                    {
                        var progress = CalculateProgress(accumulation.Count, numberToAccumulate);

                        openAttempt.AchievementAttemptEndDateTime = accumulation.EndDate;
                        openAttempt.Progress     = progress;
                        openAttempt.IsClosed     = true;
                        openAttempt.IsSuccessful = progress >= 1m;
                    }
                }

                return(openAttempt.IsClosed);
            }

            // Iterate through the streak date for the date range specified
            streakTypeService.IterateStreakMap(streakTypeCache, streak.PersonAliasId, minDate, maxDate, iterationAction, out var errorMessage);

            if (!errorMessage.IsNullOrWhiteSpace())
            {
                ExceptionLogService.LogException($"AccumulativeAchievement.UpdateOpenAttempt got an error calling StreakTypeService.IterateStreakMap: {errorMessage}");
                return;
            }

            // If the attempt wasn't closed in the iteration, then it will remain open
            if (!openAttempt.IsClosed)
            {
                var progress = CalculateProgress(accumulation.Count, numberToAccumulate);

                openAttempt.Progress     = progress;
                openAttempt.IsSuccessful = progress >= 1m;
            }
        }