Example #1
0
        /// <summary>
        /// Get a query for people that have met prerequisites
        /// </summary>
        /// <param name="rockContext"></param>
        /// <param name="stepTypeView"></param>
        /// <returns></returns>
        private IQueryable <int> GetPersonIdsThatHaveMetPrerequisitesQuery(RockContext rockContext, StepTypeView stepTypeView)
        {
            var stepService = new StepService(rockContext);

            // We are querying for people that have met all the prerequisites for this step type
            // This method should not be called for stepTypes that do not have prerequisites
            // because that would be a query for everyone in the database
            var firstStepTypeId   = stepTypeView.PrerequisiteStepTypeIds.First();
            var prerequisiteCount = stepTypeView.PrerequisiteStepTypeIds.Count();

            // Aliases that have completed the first prerequisite
            var query = stepService.Queryable().AsNoTracking()
                        .Where(s =>
                               s.StepStatus.IsCompleteStatus &&
                               s.StepTypeId == firstStepTypeId)
                        .Select(s => s.PersonAlias.PersonId);

            for (var i = 1; i < prerequisiteCount; i++)
            {
                var stepTypeId = stepTypeView.PrerequisiteStepTypeIds.ElementAt(i);

                // Aliases that have completed this subsequent prerequisite
                var subquery = stepService.Queryable().AsNoTracking()
                               .Where(s =>
                                      s.StepStatus.IsCompleteStatus &&
                                      s.StepTypeId == stepTypeId)
                               .Select(s => s.PersonAlias.PersonId);

                // Find the intersection (people in the main query who have also met this prerequisite)
                query = query.Intersect(subquery);
            }

            return(query);
        }
Example #2
0
        /// <summary>
        /// These are people that cannot have new step because they already
        /// have one and are within the minimum date range.
        /// </summary>
        /// <param name="stepTypeView">The step type view.</param>
        /// <param name="rockContext"></param>
        /// <param name="minDaysBetweenSteps"></param>
        /// <returns></returns>
        private IQueryable <int> GetPersonIdsThatCannotGetStepQuery(RockContext rockContext, StepTypeView stepTypeView, int minDaysBetweenSteps)
        {
            // We are querying for people that will ultimately be excluded from getting a new
            // step created from this job.
            var stepService = new StepService(rockContext);
            var query       = stepService.Queryable().AsNoTracking().Where(s => s.StepTypeId == stepTypeView.StepTypeId);

            if (stepTypeView.AllowMultiple)
            {
                // If allow multiple and completed date is within the minDaysBetweenSteps timeframe
                var minStepDate = minDaysBetweenSteps >= 1 ?
                                  RockDateTime.Now.AddDays(0 - minDaysBetweenSteps) :
                                  DateTime.MinValue;

                query = query.Where(s => s.CompletedDateTime.HasValue && s.CompletedDateTime >= minStepDate);
            }
            else
            {
                // If not allow multiple and has a completed date at all
                query = query.Where(s => s.CompletedDateTime.HasValue);
            }

            return(query.Select(s => s.PersonAlias.PersonId));
        }
Example #3
0
        /// <summary>
        /// These are people that cannot have new step because they already
        /// have one and are within the minimum date range.
        /// </summary>
        /// <param name="stepTypeView">The step type view.</param>
        /// <param name="rockContext"></param>
        /// <param name="minDaysBetweenSteps"></param>
        /// <returns></returns>
        private IQueryable <int> GetPeopleThatCannotGetStepQuery(RockContext rockContext, StepTypeView stepTypeView, int minDaysBetweenSteps)
        {
            var stepService = new StepService(rockContext);
            var minStepDate = DateTime.MinValue;

            // We are querying for people that will ultimately be excluded from getting a new
            // step created from this job.
            // If duplicates are not allowed, then we want to find anyone with a step ever
            // If duplicates are allowed and a day range is set, then it is within that timeframe.
            if (stepTypeView.AllowMultiple && minDaysBetweenSteps >= 1)
            {
                minStepDate = RockDateTime.Now.AddDays(0 - minDaysBetweenSteps);
            }

            var query = stepService.Queryable().AsNoTracking()
                        .Where(s =>
                               s.StepTypeId == stepTypeView.StepTypeId &&
                               (!s.CompletedDateTime.HasValue || s.CompletedDateTime.Value >= minStepDate))
                        .Select(s => s.PersonAlias.PersonId);

            return(query);
        }
Example #4
0
        /// <summary>
        /// Processes the step type. Add steps for everyone in the dataview
        /// </summary>
        /// <param name="jobContext">The job context.</param>
        /// <param name="stepTypeView">The step type view.</param>
        /// <param name="minDaysBetweenSteps">The minimum days between steps.</param>
        /// <param name="addedResults">The added results.</param>
        /// <param name="updatedResults">The updated results.</param>
        /// <param name="errorMessages">The error message.</param>
        private void ProcessStepType(
            IJobExecutionContext jobContext,
            StepTypeView stepTypeView,
            int minDaysBetweenSteps,
            ConcurrentBag <int> addedResults,
            ConcurrentBag <int> updatedResults,
            out List <string> errorMessages)
        {
            errorMessages = new List <string>();
            var rockContextGetList = new RockContext();

            rockContextGetList.Database.CommandTimeout = SqlCommandTimeoutSeconds;

            // Steps are created with a status of "complete", so if we need to know the status id
            var stepStatusId = stepTypeView.CompletedStepStatusIds.FirstOrDefault();

            if (stepStatusId == default)
            {
                errorMessages.Add($"The Step Type with id {stepTypeView.StepTypeId} does not have a valid Complete Status to use");
                return;
            }

            // Get the dataview configured for the step type
            var dataViewService = new DataViewService(rockContextGetList);
            var dataview        = dataViewService.Get(stepTypeView.AutoCompleteDataViewId);

            if (dataview == null)
            {
                errorMessages.Add($"The dataview {stepTypeView.AutoCompleteDataViewId} for step type {stepTypeView.StepTypeId} did not resolve");
                return;
            }

            // We can use the dataview to get the person alias id query
            var dataViewGetQueryArgs = new DataViewGetQueryArgs
            {
                DbContext = rockContextGetList,
                DatabaseTimeoutSeconds = SqlCommandTimeoutSeconds
            };

            IQueryable <IEntity> dataviewQuery;

            try
            {
                dataviewQuery = dataview.GetQuery(dataViewGetQueryArgs);
            }
            catch (Exception ex)
            {
                errorMessages.Add(ex.Message);
                ExceptionLogService.LogException(ex);
                return;
            }

            if (dataviewQuery == null)
            {
                errorMessages.Add($"Generating a query for dataview {stepTypeView.AutoCompleteDataViewId} for step type {stepTypeView.StepTypeId} was not successful");
                return;
            }

            // This query contains person ids in the dataview
            var personIdQuery = dataviewQuery.AsNoTracking().Select(e => e.Id);

            // Get the query for people that cannot get a new step
            var personIdsThatCannotGetStepQuery = GetPersonIdsThatCannotGetStepQuery(rockContextGetList, stepTypeView, minDaysBetweenSteps);

            // Subtract the people that cannot get a new step
            personIdQuery = personIdQuery.Except(personIdsThatCannotGetStepQuery);

            // If there are prerequisites, then subtract the people that cannot get the step because of unmet prerequisites
            if (stepTypeView.PrerequisiteStepTypeIds.Any())
            {
                var personIdsThatHaveMetPrerequisitesQuery = GetPersonIdsThatHaveMetPrerequisitesQuery(rockContextGetList, stepTypeView);
                personIdQuery = personIdQuery.Intersect(personIdsThatHaveMetPrerequisitesQuery);
            }

            // Convert to person aliases ids
            var personAliasService = new PersonAliasService(rockContextGetList);
            var personInfoList     = personAliasService.GetPrimaryAliasQuery()
                                     .Where(a => personIdQuery.Contains(a.PersonId))
                                     .Select(a => new
            {
                PersonId       = a.PersonId,
                PrimaryAliasId = a.Id
            })
                                     .ToList();

            // Add or update steps for each of the remaining aliases that have met all the conditions
            var stepServiceGetList = new StepService(rockContextGetList);
            var now          = RockDateTime.Now;
            var addedCount   = 0;
            var updatedCount = 0;

            // Query for existing incomplete steps for the people
            var existingIncompleteStepIdsByPersonId = stepServiceGetList.Queryable()
                                                      .Where(s =>
                                                             s.StepTypeId == stepTypeView.StepTypeId &&
                                                             personIdQuery.Contains(s.PersonAlias.PersonId) &&
                                                             !s.CompletedDateTime.HasValue)
                                                      .Select(a => new
            {
                a.PersonAlias.PersonId,
                StepId = a.Id
            })
                                                      .ToList()
                                                      .GroupBy(a => a.PersonId)
                                                      .ToDictionary(
                k => k.Key,
                // just in case the Person is has more than one incomplete step for this step type, just use the latest one
                // it should clean it self up on subsequent runs since the other steps for this person wouldn't have been marked complete yet
                v => v.Max(s => s.StepId)
                );


            long totalCount    = personInfoList.Count;
            long progressCount = 0;

            foreach (var personIdInfo in personInfoList)
            {
                var personId             = personIdInfo.PersonId;
                var personPrimaryAliasId = personIdInfo.PrimaryAliasId;

                var existingStepId = existingIncompleteStepIdsByPersonId.GetValueOrNull(personId);

                using (var rockContextLoop = new RockContext())
                {
                    var  stepServiceLoop = new StepService(rockContextLoop);
                    Step step;
                    if (existingStepId.HasValue)
                    {
                        step = stepServiceLoop.Get(existingStepId.Value);
                    }
                    else
                    {
                        step = new Step
                        {
                            StepTypeId    = stepTypeView.StepTypeId,
                            Caption       = stepTypeView.Name,
                            StartDateTime = now,
                            PersonAliasId = personPrimaryAliasId
                        };
                    }

                    step.CompletedDateTime = now;
                    step.StepStatusId      = stepStatusId;

                    if (!existingStepId.HasValue)
                    {
                        stepServiceLoop.AddWithoutValidation(step);
                        addedCount++;
                    }
                    else
                    {
                        updatedCount++;
                    }

                    rockContextLoop.SaveChanges();
                }

                progressCount++;

                // Update the progress every 5 seconds
                if ((RockDateTime.Now - _lastProgressUpdate).TotalSeconds >= 5)
                {
                    try
                    {
                        jobContext.UpdateLastStatusMessage($"Processing {stepTypeView.Name } steps : {progressCount}/{totalCount}");
                    }
                    catch (Exception ex)
                    {
                        // ignore, but write to debug output
                        System.Diagnostics.Debug.WriteLine($"Error updating LastStatusMessage for ProcessStepType loop: {ex}");
                    }
                    finally
                    {
                        _lastProgressUpdate = RockDateTime.Now;
                    }
                }
            }

            addedResults.Add(addedCount);
            updatedResults.Add(updatedCount);
        }
Example #5
0
        /// <summary>
        /// Processes the step type. Add steps for everyone in the dataview
        /// </summary>
        /// <param name="stepTypeView">The step type view.</param>
        /// <param name="errorMessages">The error message.</param>
        /// <param name="minDaysBetweenSteps"></param>
        /// <returns></returns>
        private int ProcessStepType(StepTypeView stepTypeView, int minDaysBetweenSteps, out List <string> errorMessages)
        {
            errorMessages = new List <string>();
            var rockContext = new RockContext();

            // Steps are created with a status of "complete", so if we need to know the status id
            var stepStatusId = stepTypeView.CompletedStepStatusIds.FirstOrDefault();

            if (stepStatusId == default)
            {
                errorMessages.Add($"The Step Type with id {stepTypeView.StepTypeId} does not have a valid Complete Status to use");
                return(0);
            }

            // Get the dataview configured for the step type
            var dataViewService = new DataViewService(rockContext);
            var dataview        = dataViewService.Get(stepTypeView.AutoCompleteDataViewId);

            if (dataview == null)
            {
                errorMessages.Add($"The dataview {stepTypeView.AutoCompleteDataViewId} for step type {stepTypeView.StepTypeId} did not resolve");
                return(0);
            }

            // We can use the dataview to get the person alias id query
            var dataviewQuery = dataview.GetQuery(null, rockContext, null, out var dataviewQueryErrors);

            if (dataviewQueryErrors != null && dataviewQueryErrors.Any())
            {
                errorMessages.AddRange(dataviewQueryErrors);
                return(0);
            }

            if (dataviewQuery == null)
            {
                errorMessages.Add($"Generating a query for dataview {stepTypeView.AutoCompleteDataViewId} for step type {stepTypeView.StepTypeId} was not successful");
                return(0);
            }

            // This query contains person ids in the dataview
            var personIdQuery = dataviewQuery.AsNoTracking().Select(e => e.Id);

            // Get the query for person aliases that cannot get a new step
            var peopleThatCannotGetStepQuery = GetPeopleThatCannotGetStepQuery(rockContext, stepTypeView, minDaysBetweenSteps);

            // Subtract the people that cannot get a new step
            personIdQuery = personIdQuery.Except(peopleThatCannotGetStepQuery);

            // If there are prerequisites, then subtract the people that cannot be the step because of unmet prerequisites
            if (stepTypeView.PrerequisiteStepTypeIds.Any())
            {
                var peopleThatHaveMetPrerequisitesQuery = GetPeopleThatHaveMetPrerequisitesQuery(rockContext, stepTypeView);
                personIdQuery = personIdQuery.Intersect(peopleThatHaveMetPrerequisitesQuery);
            }

            // Convert to person aliases ids
            var personAliasService = new PersonAliasService(rockContext);
            var personAliasIds     = personAliasService.Queryable().AsNoTracking()
                                     .Where(pa => personIdQuery.Contains(pa.PersonId))
                                     .Select(pa => pa.Id)
                                     .Distinct()
                                     .ToList();

            // Add steps for each of the remaining aliases that have met all the conditions
            var stepService = new StepService(rockContext);
            var now         = RockDateTime.Now;
            var count       = 0;

            foreach (var personAliasId in personAliasIds)
            {
                var step = new Step
                {
                    StepTypeId        = stepTypeView.StepTypeId,
                    CompletedDateTime = now,
                    StartDateTime     = now,
                    StepStatusId      = stepStatusId,
                    PersonAliasId     = personAliasId
                };

                stepService.AddWithoutValidation(step);
                count++;

                if (count % 100 == 0)
                {
                    rockContext.SaveChanges();
                }
            }

            rockContext.SaveChanges();
            return(count);
        }