/// <summary> /// Processes attempts for the specified streak type achievement type identifier. This adds new attempts and updates existing attempts. /// </summary> /// <param name="streakTypeAchievementTypeId">The streak type achievement type identifier.</param> public static void Process(int streakTypeAchievementTypeId) { var achievementTypeCache = StreakTypeAchievementTypeCache.Get(streakTypeAchievementTypeId); if (achievementTypeCache == null) { throw new ArgumentException($"The StreakTypeAchievementTypeCache did not resolve for record id {streakTypeAchievementTypeId}"); } var achievementComponent = achievementTypeCache.AchievementComponent; if (achievementComponent == null) { throw new ArgumentException($"The AchievementComponent did not resolve for record id {streakTypeAchievementTypeId}"); } var streakTypeId = achievementTypeCache.StreakTypeId; var streakService = new StreakService(new RockContext()); var streaks = streakService.Queryable().AsNoTracking() .Where(s => s.StreakTypeId == streakTypeId); foreach (var streak in streaks) { // Process each streak in it's own data context to avoid the data context changes getting too big and slow var rockContext = new RockContext(); achievementComponent.Process(rockContext, achievementTypeCache, streak); rockContext.SaveChanges(); } }
/// <summary> /// Gets the progress statement for the person for this achievement. Flat means that the unmet prerequisites are not computed. /// </summary> /// <param name="streakTypeAchievementTypeCache">The streak type achievement type cache.</param> /// <param name="personId">The person identifier.</param> /// <returns></returns> private ProgressStatement GetFlatProgressStatement(StreakTypeAchievementTypeCache streakTypeAchievementTypeCache, int personId) { var rockContext = Context as RockContext; var attemptService = new StreakAchievementAttemptService(rockContext); var attempts = attemptService.Queryable() .AsNoTracking() .Where(saa => saa.StreakTypeAchievementTypeId == streakTypeAchievementTypeCache.Id && saa.Streak.PersonAlias.PersonId == personId) .OrderByDescending(saa => saa.AchievementAttemptStartDateTime) .ToList(); var progressStatement = new ProgressStatement(streakTypeAchievementTypeCache); // If there are no attempts, no other information can be derived if (!attempts.Any()) { return(progressStatement); } var mostRecentAttempt = attempts.First(); var bestAttempt = attempts.OrderByDescending(saa => saa.Progress).First(); progressStatement.SuccessCount = attempts.Count(saa => saa.IsSuccessful); progressStatement.AttemptCount = attempts.Count(); progressStatement.BestAttempt = bestAttempt; progressStatement.MostRecentAttempt = mostRecentAttempt; return(progressStatement); }
/// <summary> /// Gets the progress statements for the person for all active achievements. /// </summary> /// <param name="personId">The person identifier.</param> /// <returns></returns> public List <ProgressStatement> GetProgressStatements(int personId) { var achievementTypes = StreakTypeAchievementTypeCache.All().Where(stat => stat.IsActive).ToList(); var orderedAchievementTypes = SortAccordingToPrerequisites(achievementTypes); var progressStatementsDictionary = new Dictionary <int, ProgressStatement>(); var progressStatements = new List <ProgressStatement>(); foreach (var achievementType in orderedAchievementTypes) { var progressStatement = GetFlatProgressStatement(achievementType, personId); progressStatementsDictionary[achievementType.Id] = progressStatement; progressStatements.Add(progressStatement); foreach (var prerequisite in achievementType.PrerequisiteAchievementTypes) { var prerequisiteProgressStatement = progressStatementsDictionary[prerequisite.Id]; if (prerequisiteProgressStatement.SuccessCount == 0) { progressStatement.UnmetPrerequisites.Add(prerequisiteProgressStatement); } } } return(progressStatements); }
/// <summary> /// Loads the drop down items. /// </summary> /// <param name="picker">The picker.</param> /// <param name="includeEmptyOption">if set to <c>true</c> [include empty option].</param> public static void LoadDropDownItems(IStreakTypeAchievementTypePicker picker, bool includeEmptyOption) { var selectedItems = picker.Items.Cast <ListItem>() .Where(i => i.Selected) .Select(i => i.Value).AsIntegerList(); picker.Items.Clear(); if (includeEmptyOption) { // add Empty option first picker.Items.Add(new ListItem()); } var achievementTypes = StreakTypeAchievementTypeCache.All() .Where(stat => stat.IsActive) .OrderBy(stat => stat.Name) .ToList(); foreach (var achievementType in achievementTypes) { var li = new ListItem(achievementType.Name, achievementType.Id.ToString()); li.Selected = selectedItems.Contains(achievementType.Id); picker.Items.Add(li); } }
/// <summary> /// Returns a collection of Achievement Types that can be selected as prerequisites for a new Achievement Type. /// </summary> /// <param name="streakTypeCache">The streak type cache.</param> /// <returns></returns> public static List <StreakTypeAchievementTypeCache> GetEligiblePrerequisiteAchievementTypeCaches(StreakTypeCache streakTypeCache) { return(StreakTypeAchievementTypeCache.All() .Where(stat => stat.IsActive && stat.StreakTypeId == streakTypeCache.Id) .ToList()); }
/// <summary> /// Initializes a new instance of the <see cref="ProgressStatement" /> class. /// </summary> /// <param name="streakTypeAchievementTypeCache">The streak type achievement type cache.</param> public ProgressStatement(StreakTypeAchievementTypeCache streakTypeAchievementTypeCache) { StreakTypeAchievementTypeId = streakTypeAchievementTypeCache.Id; StreakTypeAchievementTypeName = streakTypeAchievementTypeCache.Name; StreakTypeAchievementTypeDescription = streakTypeAchievementTypeCache.Description; Attributes = streakTypeAchievementTypeCache.AttributeValues? .Where(kvp => kvp.Key != "Active" && kvp.Key != "Order") .ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Value); }
/// <summary> /// Returns a collection of Achievement Types that can be selected as prerequisites of the specified Achievement Type. /// An Achievement Type cannot be a prerequisite of itself, or of any Achievement Type that has it as a prerequisite. /// </summary> /// <param name="streakTypeAchievementTypeCache">The Achievement Type for which prerequisites are required.</param> /// <returns></returns> public static List <StreakTypeAchievementTypeCache> GetEligiblePrerequisiteAchievementTypeCaches(StreakTypeAchievementTypeCache streakTypeAchievementTypeCache) { // Get achievement types of which the specified achievement type is not already a prerequisite. return(StreakTypeAchievementTypeCache.All() .Where(stat => stat.IsActive && stat.Id != streakTypeAchievementTypeCache.Id && stat.StreakTypeId == streakTypeAchievementTypeCache.StreakTypeId && !stat.Prerequisites.Any(p => p.PrerequisiteStreakTypeAchievementTypeId == streakTypeAchievementTypeCache.Id)) .ToList()); }
/// <summary> /// Gets the progress statement for the person for this achievement. /// </summary> /// <param name="streakTypeAchievementTypeCache">The streak type achievement type cache.</param> /// <param name="personId">The person identifier.</param> /// <returns></returns> public ProgressStatement GetProgressStatement(StreakTypeAchievementTypeCache streakTypeAchievementTypeCache, int personId) { var progressStatement = GetFlatProgressStatement(streakTypeAchievementTypeCache, personId); foreach (var prerequisite in streakTypeAchievementTypeCache.PrerequisiteAchievementTypes) { var prerequisiteProgressStatement = GetFlatProgressStatement(prerequisite, personId); progressStatement.UnmetPrerequisites.Add(prerequisiteProgressStatement); } return(progressStatement); }
/// <summary> /// Gets the unmet prerequisites. /// </summary> /// <param name="achievementTypeId">The achievement type identifier.</param> /// <param name="personId">The person identifier.</param> /// <returns></returns> public List <ProgressStatement> GetUnmetPrerequisites(int achievementTypeId, int personId) { var achievementType = StreakTypeAchievementTypeCache.Get(achievementTypeId); if (achievementType == null || !achievementType.Prerequisites.Any()) { return(new List <ProgressStatement>()); } return(achievementType.Prerequisites .Select(stat => GetFlatProgressStatement(stat.PrerequisiteStreakTypeAchievementType, personId)) .Where(ps => ps.SuccessCount == 0) .ToList()); }
/// <summary> /// Visit for sort. Part of the <see cref="SortAccordingToPrerequisites(List{StreakTypeAchievementTypeCache})" /> algorithm /// </summary> /// <param name="currentAchievementType">The current achievement type being visited.</param> /// <param name="visitedIds">The IDs of achievement types that have been visited already.</param> /// <param name="sorted">The sorted achievement types thus far.</param> /// <exception cref="System.Exception">Attempting to sort achievement types according to prerequisites and encountered a cyclic dependency on id: {item.Id}</exception> private static void VisitForSort(StreakTypeAchievementTypeCache currentAchievementType, HashSet <int> visitedIds, List <StreakTypeAchievementTypeCache> sorted) { if (!visitedIds.Contains(currentAchievementType.Id)) { visitedIds.Add(currentAchievementType.Id); foreach (var prerequisite in currentAchievementType.Prerequisites) { VisitForSort(prerequisite.PrerequisiteStreakTypeAchievementType, visitedIds, sorted); } sorted.Add(currentAchievementType); } else if (!sorted.Contains(currentAchievementType)) { throw new Exception($"Attempting to sort achievement types according to prerequisites and encountered a cyclic dependency on id: {currentAchievementType.Id}"); } }
/// <summary> /// Executes this instance. /// </summary> /// <exception cref="System.NotImplementedException"></exception> public void Execute() { var achievementTypeCache = StreakTypeAchievementTypeCache.Get(StreakTypeAchievementTypeId); if (achievementTypeCache == null || !achievementTypeCache.IsActive) { return; } if (IsNowStarting && achievementTypeCache.AchievementStartWorkflowTypeId.HasValue) { LaunchWorkflow(achievementTypeCache.AchievementStartWorkflowTypeId.Value); } if (IsNowEnding && achievementTypeCache.AchievementFailureWorkflowTypeId.HasValue) { LaunchWorkflow(achievementTypeCache.AchievementFailureWorkflowTypeId.Value); } if (IsNowSuccessful && achievementTypeCache.AchievementSuccessWorkflowTypeId.HasValue) { LaunchWorkflow(achievementTypeCache.AchievementSuccessWorkflowTypeId.Value); } if (IsNowSuccessful && achievementTypeCache.AchievementStepStatusId.HasValue && achievementTypeCache.AchievementStepTypeId.HasValue) { var rockContext = new RockContext(); var streakService = new StreakService(rockContext); var personAliasId = streakService.Queryable().AsNoTracking() .Where(s => s.Id == StreakId) .Select(s => s.PersonAliasId) .FirstOrDefault(); if (personAliasId != default) { AddStep(achievementTypeCache.AchievementStepTypeId.Value, achievementTypeCache.AchievementStepStatusId.Value, personAliasId); } } }
/// <summary> /// Gets the type of the achievement. /// </summary> /// <returns></returns> private StreakTypeAchievementTypeCache GetAchievementTypeCache() { if (_streakTypeAchievementTypeCache != null) { return(_streakTypeAchievementTypeCache); } var attempt = GetAttempt(); var achievementTypeId = PageParameter(PageParameterKey.StreakTypeAchievementTypeId).AsIntegerOrNull(); if (attempt != null) { achievementTypeId = attempt.StreakTypeAchievementTypeId; } if (achievementTypeId.HasValue && achievementTypeId.Value > 0) { _streakTypeAchievementTypeCache = StreakTypeAchievementTypeCache.Get(achievementTypeId.Value); } return(_streakTypeAchievementTypeCache); }
/// <summary> /// Rs the filter_ display filter value. /// </summary> /// <param name="sender">The sender.</param> /// <param name="e">The e.</param> protected void rFilter_DisplayFilterValue(object sender, GridFilter.DisplayFilterValueArgs e) { switch (e.Key) { case FilterKey.AchievementType: var achievementTypeCache = StreakTypeAchievementTypeCache.Get(e.Value.AsInteger()); e.Value = achievementTypeCache != null ? achievementTypeCache.Name : string.Empty; break; case FilterKey.Status: case FilterKey.FirstName: case FilterKey.LastName: break; case FilterKey.AttemptStartDateRange: e.Value = DateRangePicker.FormatDelimitedValues(e.Value); break; default: e.Value = string.Empty; break; } }
public virtual List <ProgressStatement> GetProgressForPerson([FromUri] int personId = default) { var rockContext = Service.Context as RockContext; // If not specified, use the current person id if (personId == default) { personId = GetPerson(rockContext)?.Id ?? default; if (personId == default) { var errorResponse = ControllerContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, "The personId for the current user did not resolve"); throw new HttpResponseException(errorResponse); } } var achievementTypeService = Service as StreakTypeAchievementTypeService; var progressStatements = StreakTypeAchievementTypeCache.All() .Where(stat => stat.IsActive) .Select(stat => achievementTypeService.GetProgressStatement(stat, personId)) .ToList(); return(progressStatements); }
/// <summary> /// Updates any Cache Objects that are associated with this entity /// </summary> /// <param name="entityState">State of the entity.</param> /// <param name="dbContext">The database context.</param> public void UpdateCache(EntityState entityState, Rock.Data.DbContext dbContext) { StreakTypeAchievementTypeCache.UpdateCachedEntity(Id, entityState); }
/// <summary> /// Gets the cache object associated with this Entity /// </summary> /// <returns></returns> public IEntityCache GetCacheObject() { return(StreakTypeAchievementTypeCache.Get(this.Id)); }
/// <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 ); // 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 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 ) { var iterationCanStop = false; // 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; iterationCanStop = !streakTypeAchievementTypeCache.AllowOverAchievement; } } // 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 = currentDate <= maxDateForStreakBreaking; openAttempt.IsSuccessful = progress >= 1m; 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( $"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; } }
/// <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 numberToAchieve = GetAttributeValue(streakTypeAchievementTypeCache, AttributeKey.NumberToAchieve).AsInteger(); if (numberToAchieve <= 0) { ExceptionLogService.LogException($"StreakAchievement.UpdateOpenAttempt cannot process because the numberToAchieve attribute is less than 1"); return; } var attributeTimespanDays = GetAttributeValue(streakTypeAchievementTypeCache, 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(streakTypeAchievementTypeCache, AttributeKey.EndDateTime).AsDateTime(); var minDate = openAttempt.AchievementAttemptStartDateTime; var maxDate = CalculateMaxDateForAchievementAttempt(minDate, attributeMaxDate); // 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) { // 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, !streakTypeAchievementTypeCache.AllowOverAchievement); } } else if (hasOccurrence && !hasEngagement && !hasExclusion) { // Break the streak and close the attempt if there is an unexcused absence ApplyStreakToAttempt(computedStreak, openAttempt, numberToAchieve, 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, true); } } 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($"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; } }
/// <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="streakTypeAchievementTypeCache">The streak type achievement type cache.</param> /// <param name="streak">The streak.</param> /// <param name="mostRecentClosedAttempt">The most recent closed attempt.</param> /// <returns></returns> protected override List <StreakAchievementAttempt> CreateNewAttempts(StreakTypeAchievementTypeCache streakTypeAchievementTypeCache, Streak streak, StreakAchievementAttempt mostRecentClosedAttempt) { var rockContext = new RockContext(); var streakTypeService = new StreakTypeService(rockContext); var streakTypeCache = streakTypeAchievementTypeCache.StreakTypeCache; // Validate the attribute values var numberToAchieve = GetAttributeValue(streakTypeAchievementTypeCache, AttributeKey.NumberToAchieve).AsInteger(); if (numberToAchieve <= 0) { ExceptionLogService.LogException($"StreakAchievement.CreateNewAttempts cannot process because the NumberToAchieve attribute is less than 1"); return(null); } var attributeTimespanDays = GetAttributeValue(streakTypeAchievementTypeCache, AttributeKey.TimespanInDays).AsIntegerOrNull(); if (attributeTimespanDays.HasValue && attributeTimespanDays.Value <= 0) { ExceptionLogService.LogException($"StreakAchievement.CreateNewAttempts cannot process because the TimespanInDays attribute is less than 1"); return(null); } // Calculate the date range where new achievements can be validly found var attributeMinDate = GetAttributeValue(streakTypeAchievementTypeCache, AttributeKey.StartDateTime).AsDateTime(); var attributeMaxDate = GetAttributeValue(streakTypeAchievementTypeCache, AttributeKey.EndDateTime).AsDateTime(); var minDate = CalculateMinDateForAchievementAttempt(streak.EnrollmentDate, mostRecentClosedAttempt, attributeMinDate, numberToAchieve); var maxDate = CalculateMaxDateForAchievementAttempt(minDate, attributeMaxDate); // Track the attempts in a list that will be returned. The int is the streak count for that attempt var attempts = new List <StreakAchievementAttempt>(); var streaks = new List <ComputedStreak>(); // 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 and a timespan, then this is a possible attempt. If there is no timespan then only one // attempt needs to be tracked at a time if (hasOccurrence && hasEngagement && (attributeTimespanDays.HasValue || !streaks.Any())) { streaks.Add(new ComputedStreak(currentDate)); } else if (hasOccurrence && !hasEngagement && !hasExclusion && streaks.Any()) { // Break the streaks and close an attempt if there is an unexcused absence var longestStreak = streaks.First(); attempts.Add(GetAttemptFromStreak(longestStreak, numberToAchieve, true)); streaks.Clear(); return(false); } for (var i = streaks.Count - 1; i >= 0; i--) { var computedStreak = streaks[i]; if (hasOccurrence && hasEngagement) { // Increment the streak computedStreak.Count++; computedStreak.EndDate = currentDate; // Check for a fulfilled attempt if (computedStreak.Count >= numberToAchieve) { streaks.Clear(); if (streakTypeAchievementTypeCache.AllowOverAchievement) { streaks.Add(computedStreak); i = 0; } else { attempts.Add(GetAttemptFromStreak(computedStreak, numberToAchieve, !streakTypeAchievementTypeCache.AllowOverAchievement)); break; } } } // 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) { var timedOutAttempt = GetAttemptFromStreak(computedStreak, numberToAchieve, true); attempts.Add(timedOutAttempt); streaks.RemoveAt(i); // Remove more recently started streaks that started before the next valid start date (based // on the deficiency of this timed out attempt) var nextValidStartDate = CalculateMinDateForAchievementAttempt(streak.EnrollmentDate, timedOutAttempt, attributeMinDate, numberToAchieve); for (var j = streaks.Count - 1; j >= i; j--) { var moreRecentStreak = streaks[j]; if (moreRecentStreak.StartDate < nextValidStartDate) { streaks.RemoveAt(j); } } } } } return(false); } // 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.CreateNewAttempts got an error calling StreakTypeService.IterateStreakMap: {errorMessage}"); return(null); } // The longest leftover streak is an open attempt if (streaks.Any()) { var longestStreak = streaks.First(); attempts.Add(GetAttemptFromStreak(longestStreak, numberToAchieve, false)); } return(attempts); }
/// <summary> /// Gets the achievement type cache. /// </summary> /// <returns></returns> private StreakTypeAchievementTypeCache GetAchievementTypeCache() { return(StreakTypeAchievementTypeCache.Get(PageParameter(PageParameterKey.StreakTypeAchievementTypeId).AsInteger())); }