public void TestCleanup() { var service = new AchievementAttemptService(_rockContext); service.DeleteRange(service.Queryable().Where(saa => _achievementIds.Contains(saa.Id) || saa.ForeignKey == KEY)); _rockContext.SaveChanges(); }
public void AccumulativeAchievementProcess() { var attemptsQuery = new AchievementAttemptService(_rockContext).Queryable() .AsNoTracking() .Where(saa => saa.AchievementTypeId == _achievementTypeId && _personAliasIds.Contains(saa.AchieverEntityId)) .OrderBy(saa => saa.AchievementAttemptStartDateTime); // There should be no attempts Assert.That.AreEqual(0, attemptsQuery.Count()); var achievementTypeCache = AchievementTypeCache.Get(_achievementTypeId); var interaction = new InteractionService(_rockContext).Queryable().FirstOrDefault(i => i.ForeignKey == KEY); var component = AchievementContainer.GetComponent(ComponentEntityTypeName); component.Process(_rockContext, achievementTypeCache, interaction); _rockContext.SaveChanges(); var attempts = attemptsQuery.ToList(); Assert.That.IsNotNull(attempts); Assert.That.AreEqual(1, attempts.Count); // The database stores progress with only 9 digits beyond the decimal var progress = decimal.Divide(COUNT, NUMBER_TO_ACHIEVE); var progressDifference = Math.Abs(progress - attempts[0].Progress); Assert.That.AreEqual(RockDateTime.Today, attempts[0].AchievementAttemptStartDateTime); Assert.That.AreEqual(RockDateTime.Today, attempts[0].AchievementAttemptEndDateTime); Assert.That.IsTrue(progressDifference < .000000001m); Assert.That.IsFalse(attempts[0].IsClosed); Assert.That.IsFalse(attempts[0].IsSuccessful); }
public void TestCleanup() { var service = new AchievementAttemptService(_rockContext); service.DeleteRange(service.Queryable().Where(saa => saa.AchievementTypeId == _achievementTypeId)); _rockContext.SaveChanges(); }
/// <summary> /// Gets the achievement attempts with person alias query. /// </summary> /// <param name="rockContext">The rock context.</param> /// <param name="attendanceRecordsPersonAliasIds">The attendance records person alias ids.</param> /// <param name="configuredAchievementTypeIds">The configured achievement type ids.</param> /// <returns></returns> private IQueryable <AchievementAttemptService.AchievementAttemptWithPersonAlias> GetAchievementAttemptsWithPersonAliasQuery(RockContext rockContext, int[] attendanceRecordsPersonAliasIds, List <int> configuredAchievementTypeIds) { var achievementAttemptService = new AchievementAttemptService(new RockContext()); IQueryable <AchievementAttemptService.AchievementAttemptWithPersonAlias> achievementAttemptsQuery = achievementAttemptService.GetAchievementAttemptWithAchieverPersonAliasQuery() .Where(a => configuredAchievementTypeIds.Contains(a.AchievementAttempt.AchievementTypeId) && attendanceRecordsPersonAliasIds.Contains(a.AchieverPersonAlias.Id)); return(achievementAttemptsQuery); }
/// <summary> /// Gets the attempt service. /// </summary> /// <returns></returns> private AchievementAttemptService GetAttemptService() { if (_attemptService == null) { var rockContext = GetRockContext(); _attemptService = new AchievementAttemptService(rockContext); } return(_attemptService); }
/// <summary> /// Gets the completed achievement attempt ids. /// </summary> /// <param name="rockContext">The rock context.</param> /// <param name="attendanceRecordsPersonAliasIds">The attendance records person alias ids.</param> /// <param name="configuredAchievementTypeIds">The configured achievement type ids.</param> /// <returns></returns> private AchievementAttemptService.AchievementAttemptWithPersonAlias[] GetSuccessfullyCompletedAchievementAttempts(RockContext rockContext, int[] attendanceRecordsPersonAliasIds, List <int> configuredAchievementTypeIds) { var achievementAttemptService = new AchievementAttemptService(new RockContext()); var completedAchievementAttempts = achievementAttemptService.GetAchievementAttemptWithAchieverPersonAliasQuery() .Where(x => attendanceRecordsPersonAliasIds.Contains(x.AchieverPersonAlias.Id)) .Where(a => configuredAchievementTypeIds.Contains(a.AchievementAttempt.AchievementTypeId)) .Where(a => a.AchievementAttempt.IsSuccessful || a.AchievementAttempt.IsClosed) .AsNoTracking() .ToArray(); return(completedAchievementAttempts); }
/// <summary> /// Gets the achievement attempt. /// </summary> /// <returns></returns> private AchievementAttempt GetAchievementAttempt() { if (_achievementAttempt != null) { return(_achievementAttempt); } var rockContext = new RockContext(); var service = new AchievementAttemptService(rockContext); _achievementAttempt = service.Get(AchievementAttemptGuid); return(_achievementAttempt); }
/// <summary> /// Gets the achiever attempt query. This is the query (not enumerated) that joins attempts of this achievement type with the /// achiever entities, as well as the name (<see cref="AchieverAttemptItem.AchieverName"/> that could represent the achiever /// in a grid or other such display. /// </summary> /// <param name="achievementTypeCache">The achievement type cache.</param> /// <param name="rockContext">The rock context.</param> /// <returns></returns> public override IQueryable <AchieverAttemptItem> GetAchieverAttemptQuery(AchievementTypeCache achievementTypeCache, RockContext rockContext) { var attemptService = new AchievementAttemptService(rockContext); var personAliasService = new PersonAliasService(rockContext); var attemptQuery = attemptService.Queryable().Where(aa => aa.AchievementTypeId == achievementTypeCache.Id); var personAliasQuery = personAliasService.Queryable(); return(attemptQuery.Join( personAliasQuery, aa => aa.AchieverEntityId, pa => pa.Id, (aa, pa) => new AchieverAttemptItem { AchievementAttempt = aa, Achiever = pa, AchieverName = pa.Person.NickName + " " + pa.Person.LastName })); }
/// <summary> /// Processes the specified achievement type cache for the source entity. /// </summary> /// <param name="rockContext">The rock context.</param> /// <param name="achievementTypeCache">The achievement type cache.</param> /// <param name="sourceEntity">The source entity.</param> /// <returns>The set of attempts that were created or updated</returns> public override HashSet <AchievementAttempt> Process(RockContext rockContext, AchievementTypeCache achievementTypeCache, IEntity sourceEntity) { var updatedAttempts = new HashSet <AchievementAttempt>(); // If we cannot link the transaction to a person, then there is nothing to do if (!(sourceEntity is FinancialTransaction financialTransaction)) { return(updatedAttempts); } // If the achievement type is not active (or null) then there is nothing to do if (achievementTypeCache?.IsActive != true) { return(updatedAttempts); } // If there are unmet prerequisites, then there is nothing to do var achievementTypeService = new AchievementTypeService(rockContext); var unmetPrerequisites = achievementTypeService.GetUnmetPrerequisites(achievementTypeCache.Id, financialTransaction.AuthorizedPersonAliasId.Value); if (unmetPrerequisites.Any()) { return(updatedAttempts); } // If the transaction is a refund, the person is empty, or less than zero amount, then there is nothing to do. if (null != financialTransaction.RefundDetails || !financialTransaction.AuthorizedPersonAliasId.HasValue || financialTransaction.AuthorizedPersonAliasId == 0 || financialTransaction.TotalAmount <= 0M) { return(updatedAttempts); } // Get all of the attempts for this interaction and achievement combo, ordered by start date DESC so that // the most recent attempts can be found with FirstOrDefault var achievementAttemptService = new AchievementAttemptService(rockContext); var attempts = achievementAttemptService.GetOrderedAchieverAttempts(achievementAttemptService.Queryable(), achievementTypeCache, financialTransaction.AuthorizedPersonAliasId.Value); var mostRecentSuccess = attempts.FirstOrDefault(saa => saa.AchievementAttemptEndDateTime.HasValue && saa.IsSuccessful); var overachievementPossible = achievementTypeCache.AllowOverAchievement; var successfulAttemptCount = attempts.Count(saa => saa.IsSuccessful); var maxSuccessesAllowed = achievementTypeCache.MaxAccomplishmentsAllowed ?? int.MaxValue; // If the most recent success is still open and overachievement is allowed, then update it if (overachievementPossible && mostRecentSuccess != null && !mostRecentSuccess.IsClosed) { UpdateOpenAttempt(mostRecentSuccess, achievementTypeCache, financialTransaction); updatedAttempts.Add(mostRecentSuccess); if (!mostRecentSuccess.IsClosed) { // New records can only be created once the open records are all closed return(updatedAttempts); } } // If the success count limit has been reached, then no more processing should be done if (successfulAttemptCount >= maxSuccessesAllowed) { return(updatedAttempts); } // Everything after the most recent success is on the table for deletion. Successes should not be // deleted. Everything after a success might be recalculated because of data changes. // Try to reuse these attempts if they match for continuity, but if the start date is changed, they // get deleted. var attemptsToDelete = attempts; if (mostRecentSuccess != null) { attemptsToDelete = attemptsToDelete .Where(saa => saa.AchievementAttemptStartDateTime > mostRecentSuccess.AchievementAttemptStartDateTime) .ToList(); } var newAttempts = CreateNewAttempts(achievementTypeCache, financialTransaction, mostRecentSuccess); if (newAttempts != null && newAttempts.Any()) { newAttempts = newAttempts.OrderBy(saa => saa.AchievementAttemptStartDateTime).ToList(); foreach (var newAttempt in newAttempts) { // Keep the old attempt if possible, otherwise add a new one var existingAttempt = attemptsToDelete.FirstOrDefault(saa => saa.AchievementAttemptStartDateTime == newAttempt.AchievementAttemptStartDateTime); if (existingAttempt != null) { attemptsToDelete.Remove(existingAttempt); CopyAttempt(newAttempt, existingAttempt); updatedAttempts.Add(existingAttempt); } else { newAttempt.AchieverEntityId = financialTransaction.AuthorizedPersonAliasId.Value; newAttempt.AchievementTypeId = achievementTypeCache.Id; achievementAttemptService.Add(newAttempt); updatedAttempts.Add(newAttempt); } // If this attempt was successful then make re-check the max success limit if (newAttempt.IsSuccessful) { successfulAttemptCount++; if (successfulAttemptCount >= maxSuccessesAllowed && !overachievementPossible) { break; } } } } if (attemptsToDelete.Any()) { updatedAttempts.RemoveAll(attemptsToDelete); achievementAttemptService.DeleteRange(attemptsToDelete); } return(updatedAttempts); }
/// <summary> /// Processes the specified achievement type cache for the source entity. /// </summary> /// <param name="rockContext">The rock context.</param> /// <param name="achievementTypeCache">The achievement type cache.</param> /// <param name="sourceEntity">The source entity.</param> /// <returns>The set of attempts that were created or updated</returns> public override HashSet <AchievementAttempt> Process(RockContext rockContext, AchievementTypeCache achievementTypeCache, IEntity sourceEntity) { var step = sourceEntity as Step; var updatedAttempts = new HashSet <AchievementAttempt>(); // If we cannot link the step to a person, then there is nothing to do if (step == null) { return(updatedAttempts); } // If the achievement type is not active (or null) then there is nothing to do if (achievementTypeCache?.IsActive != true) { return(updatedAttempts); } // If there are unmet prerequisites, then there is nothing to do var achievementTypeService = new AchievementTypeService(rockContext); var unmetPrerequisites = achievementTypeService.GetUnmetPrerequisites(achievementTypeCache.Id, step.PersonAliasId); if (unmetPrerequisites.Any()) { return(updatedAttempts); } // Get all of the attempts for this program and achievement combo, ordered by start date DESC so that // the most recent attempts can be found with FirstOrDefault var achievementAttemptService = new AchievementAttemptService(rockContext); var attempts = achievementAttemptService.Queryable() .Where(aa => aa.AchievementTypeId == achievementTypeCache.Id && aa.AchieverEntityId == step.PersonAliasId) .ToList() .OrderByDescending(aa => aa.AchievementAttemptStartDateTime) .ToList(); var mostRecentSuccess = attempts.FirstOrDefault(saa => saa.AchievementAttemptEndDateTime.HasValue && saa.IsSuccessful); // This component does not allow more than one success if (mostRecentSuccess != null) { return(updatedAttempts); } var currentAttempt = attempts.LastOrDefault(); if (currentAttempt == null) { currentAttempt = new AchievementAttempt { AchieverEntityId = step.PersonAliasId, AchievementTypeId = achievementTypeCache.Id }; achievementAttemptService.Add(currentAttempt); } var attributeMinDate = GetAttributeValue(achievementTypeCache, AttributeKey.StartDateTime).AsDateTime(); var attributeMaxDate = GetAttributeValue(achievementTypeCache, AttributeKey.EndDateTime).AsDateTime(); var completedStepTypeDates = GetCompletedStepTypeDates(achievementTypeCache, step.PersonAliasId, attributeMinDate, attributeMaxDate); var stepProgram = GetStepProgramCache(achievementTypeCache); var stepTypeCount = stepProgram.StepTypes.Count; var progress = CalculateProgress(completedStepTypeDates.Count, stepTypeCount); var isSuccessful = progress >= 1m; currentAttempt.AchievementAttemptStartDateTime = completedStepTypeDates.Any() ? completedStepTypeDates.First() : RockDateTime.Today; currentAttempt.AchievementAttemptEndDateTime = completedStepTypeDates.Any() ? completedStepTypeDates.Last() : RockDateTime.Today; currentAttempt.Progress = progress; currentAttempt.IsClosed = isSuccessful; currentAttempt.IsSuccessful = isSuccessful; return(updatedAttempts); }
/// <summary> /// Executes this instance. /// </summary> /// <exception cref="System.NotImplementedException"></exception> public void Execute() { var achievementTypeCache = AchievementTypeCache.Get(AchievementTypeId); if (achievementTypeCache == null || !achievementTypeCache.IsActive) { return; } if (IsNowStarting && achievementTypeCache.AchievementStartWorkflowTypeId.HasValue) { LaunchWorkflow(achievementTypeCache.AchievementStartWorkflowTypeId.Value); } if (IsNowEnding && !IsNowSuccessful && 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 achievementAttemptService = new AchievementAttemptService(rockContext); var achieverEntityId = achievementAttemptService.Queryable() .AsNoTracking() .Where(aa => aa.Guid == AchievementAttemptGuid) .Select(s => s.AchieverEntityId) .FirstOrDefault(); var personAliasEntityTypeId = EntityTypeCache.Get <PersonAlias>().Id; var personEntityTypeId = EntityTypeCache.Get <Person>().Id; int personAliasId = default; if (achievementTypeCache.AchieverEntityTypeId == personAliasEntityTypeId) { personAliasId = achieverEntityId; } else if (achievementTypeCache.AchieverEntityTypeId == personEntityTypeId) { var personAliasService = new PersonAliasService(rockContext); personAliasId = personAliasService.Queryable() .AsNoTracking() .Where(pa => pa.PersonId == achieverEntityId) .Select(pa => pa.Id) .FirstOrDefault(); } if (personAliasId != default) { AddStep(achievementTypeCache.AchievementStepTypeId.Value, achievementTypeCache.AchievementStepStatusId.Value, personAliasId); } } }
private void GivingToAccountAchievementMain( int attemptCount, int accumulateCount, int achievementId, List <int> allowedAccountIds) { var attemptsQuery = new AchievementAttemptService(_rockContext).Queryable() .AsNoTracking() .Where(saa => saa.AchievementTypeId == achievementId && saa.AchieverEntityId == _personAliasId) .OrderBy(saa => saa.AchievementAttemptStartDateTime); // There should be no attempts Assert.That.AreEqual(0, attemptsQuery.Count()); var achievementTypeCache = AchievementTypeCache.Get(achievementId); var component = AchievementContainer.GetComponent(ComponentEntityTypeName); var transactions = component.GetSourceEntitiesQuery(achievementTypeCache, _rockContext).Where(i => i.ForeignKey == KEY).ToList(); foreach (var sourceEntity in transactions) { var ft = ( FinancialTransaction )sourceEntity; // Check all of the account ids to make sure they are allowed // This is making sure the hierarchy logic is working as expected var transactionAccountIds = ft.TransactionDetails.Select(d => d.AccountId).Distinct(); foreach (var accountId in transactionAccountIds) { Assert.That.IsTrue(allowedAccountIds.Contains(accountId)); } // See Rock.Model.Engagement.AchievementType.AchievementTypeService // Process each streak in it's own data context to avoid the data context changes getting too big and slow using (var rockContext = new RockContext()) { component.Process(rockContext, achievementTypeCache, sourceEntity); rockContext.SaveChanges(); } } var attempts = attemptsQuery.ToList(); Assert.That.IsNotNull(attempts); Assert.That.AreEqual(attemptCount, attempts.Count); if (attemptCount > 0) { for (int i = 0; i < attempts.Count(); i++) { Assert.That.IsTrue(attempts[i].Progress >= 0m); if (attempts[i].Progress >= 1m) { Assert.That.IsTrue(attempts[i].IsClosed); Assert.That.IsTrue(attempts[i].IsSuccessful); } else { Assert.That.IsFalse(attempts[i].IsClosed); Assert.That.IsFalse(attempts[i].IsSuccessful); } } } }