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();
        }
Beispiel #4
0
        /// <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);
        }
Beispiel #6
0
        /// <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);
        }
Beispiel #8
0
        /// <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
            }));
        }
Beispiel #9
0
        /// <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);
        }
Beispiel #10
0
        /// <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);
                    }
                }
            }
        }