예제 #1
0
        /// <summary>
        /// Gets the step program (model) that should be displayed in the block
        /// 1.) Use the block setting
        /// 2.) Use the page parameter
        /// 3.) Use the first active program
        /// </summary>
        /// <returns></returns>
        private StepProgram GetStepProgram()
        {
            if (_stepProgram == null)
            {
                var programGuid = GetAttributeValue(AttributeKey.StepProgram).AsGuidOrNull();
                var programId   = PageParameter(PageParameterKey.StepProgramId).AsIntegerOrNull();
                var rockContext = GetRockContext();
                var service     = new StepProgramService(rockContext);

                if (programGuid.HasValue)
                {
                    // 1.) Use the block setting
                    _stepProgram = service.Queryable()
                                   .AsNoTracking()
                                   .FirstOrDefault(sp => sp.Guid == programGuid.Value && sp.IsActive);
                }
                else if (programId.HasValue)
                {
                    // 2.) Use the page parameter
                    _stepProgram = service.Queryable()
                                   .AsNoTracking()
                                   .FirstOrDefault(sp => sp.Id == programId.Value && sp.IsActive);
                }
                else
                {
                    // 3.) Just use the first active program
                    _stepProgram = service.Queryable()
                                   .AsNoTracking()
                                   .FirstOrDefault(sp => sp.IsActive);
                }
            }

            return(_stepProgram);
        }
예제 #2
0
        /// <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(IStepProgramPicker 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 stepProgramService = new StepProgramService(new RockContext());
            var stepPrograms       = stepProgramService.Queryable().AsNoTracking()
                                     .Where(sp => sp.IsActive)
                                     .OrderBy(sp => sp.Order)
                                     .ThenBy(sp => sp.Name)
                                     .ToList();

            foreach (var stepProgram in stepPrograms)
            {
                var li = new ListItem(stepProgram.Name, stepProgram.Id.ToString());
                li.Selected = selectedItems.Contains(stepProgram.Id);
                picker.Items.Add(li);
            }
        }
예제 #3
0
        public void GetPersonCompletingProgramQuery_ReturnsCorrectData()
        {
            var startDate = new DateTime(2019, 1, 1);
            var endDate   = new DateTime(2019, 2, 4);

            var rockContext = new RockContext();
            var service     = new StepProgramService(rockContext);
            var stepProgram = service.Queryable().Where(sp => sp.ForeignKey == ForeignKey).First();
            var result      = service.GetPersonCompletingProgramQuery(stepProgram.Id).ToList();

            Assert.IsNotNull(result);
            Assert.AreEqual(2, result.Count);

            var personService = new PersonService(rockContext);
            var kathy         = personService.Get(KathyKolePersonGuidString.AsGuid());
            var jerry         = personService.Get(JerryJenkinsPersonGuidString.AsGuid());

            var kathyResult = result.FirstOrDefault(r => r.PersonId == kathy.Id);
            var jerryResult = result.FirstOrDefault(r => r.PersonId == jerry.Id);

            // Kathy completed once in 2019 and once in 2020 - we should only get the first
            Assert.IsNotNull(kathyResult);
            Assert.AreEqual(new DateTime(2019, 1, 1), kathyResult.StartedDateTime);
            Assert.AreEqual(new DateTime(2019, 4, 1), kathyResult.CompletedDateTime);

            // Jerry completed with two aliases. The first alias started in 2019. The second alias finished in 2020
            Assert.IsNotNull(jerryResult);
            Assert.AreEqual(new DateTime(2019, 1, 1), jerryResult.StartedDateTime);
            Assert.AreEqual(new DateTime(2020, 3, 1), jerryResult.CompletedDateTime);
        }
예제 #4
0
        /// <summary>
        /// Handles the GridReorder event of the gStepProgram control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="GridReorderEventArgs" /> instance containing the event data.</param>
        void gStepProgram_GridReorder(object sender, GridReorderEventArgs e)
        {
            var rockContext  = new RockContext();
            var service      = new StepProgramService(rockContext);
            var stepPrograms = service.Queryable().OrderBy(b => b.Order);

            service.Reorder(stepPrograms.ToList(), e.OldIndex, e.NewIndex);
            rockContext.SaveChanges();

            BindGrid();
        }
예제 #5
0
        /// <summary>
        /// Delete the test data
        /// </summary>
        private static void DeleteTestData()
        {
            using (var rockContext = new RockContext())
            {
                var stepService = new StepService(rockContext);
                var stepQuery   = stepService.Queryable().Where(s => s.ForeignKey == ForeignKey);
                stepService.DeleteRange(stepQuery);
                rockContext.SaveChanges();
            }

            using (var rockContext = new RockContext())
            {
                var stepProgramService = new StepProgramService(rockContext);
                var stepProgramQuery   = stepProgramService.Queryable().Where(sp => sp.ForeignKey == ForeignKey);
                stepProgramService.DeleteRange(stepProgramQuery);
                rockContext.SaveChanges();
            }

            using (var rockContext = new RockContext())
            {
                var personSearchKeyService = new PersonSearchKeyService(rockContext);
                var personSearchKeyQuery   = personSearchKeyService.Queryable()
                                             .Where(psk =>
                                                    psk.PersonAlias.Person.ForeignKey == ForeignKey ||
                                                    PersonGuids.Contains(psk.PersonAlias.Person.Guid));
                personSearchKeyService.DeleteRange(personSearchKeyQuery);
                rockContext.SaveChanges();
            }

            using (var rockContext = new RockContext())
            {
                var personAliasService = new PersonAliasService(rockContext);
                var personAliasQuery   = personAliasService.Queryable()
                                         .Where(pa =>
                                                pa.Person.ForeignKey == ForeignKey ||
                                                PersonGuids.Contains(pa.Person.Guid));
                personAliasService.DeleteRange(personAliasQuery);
                rockContext.SaveChanges();
            }

            using (var rockContext = new RockContext())
            {
                var personService = new PersonService(rockContext);
                var personQuery   = personService.Queryable()
                                    .Where(p =>
                                           p.ForeignKey == ForeignKey ||
                                           PersonGuids.Contains(p.Guid));
                personService.DeleteRange(personQuery);
                rockContext.SaveChanges();
            }
        }
예제 #6
0
        /// <summary>
        /// Get a list of triggers that may be fired by a state change in the entity being processed.
        /// </summary>
        /// <param name="dataContext"></param>
        /// <param name="entityGuid"></param>
        /// <returns></returns>
        protected override List <StepWorkflowTrigger> GetEntityChangeTriggers(RockContext dataContext, Guid entityGuid)
        {
            // Get the triggers associated with the Step Type to which this Step is related.
            var triggers = new List <StepWorkflowTrigger>();

            var stepTypeService = new StepTypeService(dataContext);

            var stepType = stepTypeService.Queryable()
                           .AsNoTracking()
                           .Include(x => x.StepWorkflowTriggers)
                           .FirstOrDefault(x => x.Id == this.StepTypeId);

            if (stepType == null)
            {
                ExceptionLogService.LogException($"StepChangeTransaction failed. Step Type does not exist [StepTypeId={ StepTypeId }].");
                return(null);
            }

            var stepTypeTriggers = stepType.StepWorkflowTriggers
                                   .Where(w => w.TriggerType != StepWorkflowTrigger.WorkflowTriggerCondition.Manual)
                                   .ToList();

            triggers.AddRange(stepTypeTriggers);

            var stepProgramId = stepType.StepProgramId;

            // Get the triggers associated with the Step Program to which this Step is related, but are not associated with a specific Step Type.
            var stepProgramService = new StepProgramService(dataContext);

            var stepProgram = stepProgramService.Queryable()
                              .AsNoTracking()
                              .Include(x => x.StepWorkflowTriggers)
                              .FirstOrDefault(x => x.Id == stepProgramId);

            if (stepProgram == null)
            {
                ExceptionLogService.LogException($"StepChangeTransaction failed. Step Program does not exist [StepProgramId={ stepProgramId }].");
                return(null);
            }

            var stepProgramTriggers = stepProgram.StepWorkflowTriggers
                                      .Where(w => w.StepTypeId == null &&
                                             w.TriggerType != StepWorkflowTrigger.WorkflowTriggerCondition.Manual)
                                      .ToList();

            triggers.AddRange(stepProgramTriggers);

            return(triggers);
        }
        /// <summary>
        /// Adds the step.
        /// </summary>
        /// <param name="stepTypeId">The step type identifier.</param>
        /// <param name="stepStatusId">The step status identifier.</param>
        /// <param name="personAliasId">The person alias identifier.</param>
        private void AddStep(int stepTypeId, int stepStatusId, int personAliasId)
        {
            var rockContext        = new RockContext();
            var stepService        = new StepService(rockContext);
            var stepProgramService = new StepProgramService(rockContext);

            // Get the step program with step types and statuses to better calculate the dates for the new step
            var stepProgram = stepProgramService.Queryable("StepTypes, StepStatuses").FirstOrDefault(sp =>
                                                                                                     sp.StepTypes.Any(st => st.Id == stepTypeId) &&
                                                                                                     sp.StepStatuses.Any(ss => ss.Id == stepStatusId));

            var stepType   = stepProgram?.StepTypes.FirstOrDefault(st => st.Id == stepTypeId);
            var stepStatus = stepProgram?.StepStatuses.FirstOrDefault(ss => ss.Id == stepStatusId);

            if (stepType == null)
            {
                ExceptionLogService.LogException($"Error adding step related to an achievement. The step type {stepTypeId} did not resolve.");
                return;
            }

            if (stepStatus == null)
            {
                ExceptionLogService.LogException($"Error adding step related to an achievement. The step status {stepStatusId} did not resolve.");
                return;
            }

            // Add the new step
            var step = new Step
            {
                StepTypeId        = stepTypeId,
                StepStatusId      = stepStatusId,
                CompletedDateTime = stepStatus.IsCompleteStatus ? EndDate : null,
                StartDateTime     = StartDate,
                EndDateTime       = stepType.HasEndDate ? EndDate : null,
                PersonAliasId     = personAliasId
            };

            // If the person cannot be added to the step type, then don't add anything since some step types only allow one step
            // or require pre-requisites
            if (stepService.CanAdd(step, out _))
            {
                stepService.Add(step);
            }

            rockContext.SaveChanges();
        }
예제 #8
0
        /// <summary>
        /// Returns a dictionary of the items available for selection.
        /// </summary>
        /// <returns></returns>
        protected override Dictionary <Guid, string> OnGetItemList()
        {
            var service = new StepProgramService(new RockContext());

            var items = service
                        .Queryable()
                        .AsNoTracking()
                        .OrderBy(o => o.Name)
                        .Select(o => new
            {
                o.Guid,
                o.Name,
            })
                        .ToDictionary(o => o.Guid, o => o.Name);

            return(items);
        }
        /// <summary>
        /// Delete the test data
        /// </summary>
        private static void DeleteTestData()
        {
            var rockContext = new RockContext();

            var stepProgramService = new StepProgramService(rockContext);
            var stepProgramQuery   = stepProgramService.Queryable().Where(sp => sp.ForeignKey == ForeignKey);

            stepProgramService.DeleteRange(stepProgramQuery);
            rockContext.SaveChanges();

            var dataViewFilterService = new DataViewFilterService(rockContext);
            var dvfQuery = dataViewFilterService.Queryable().Where(dvf => dvf.DataView.ForeignKey == ForeignKey || dvf.ForeignKey == ForeignKey);

            dataViewFilterService.DeleteRange(dvfQuery);
            rockContext.SaveChanges();

            var dataViewService = new DataViewService(rockContext);
            var dvQuery         = dataViewService.Queryable().Where(dv => dv.ForeignKey == ForeignKey);

            dataViewService.DeleteRange(dvQuery);
            rockContext.SaveChanges();

            var personSearchKeyService = new PersonSearchKeyService(rockContext);
            var personSearchKeyQuery   = personSearchKeyService.Queryable().Where(psk => psk.PersonAlias.Person.ForeignKey == ForeignKey);

            personSearchKeyService.DeleteRange(personSearchKeyQuery);

            var personAliasService = new PersonAliasService(rockContext);
            var personAliasQuery   = personAliasService.Queryable().Where(pa => pa.Person.ForeignKey == ForeignKey || pa.ForeignKey == ForeignKey);

            personAliasService.DeleteRange(personAliasQuery);
            rockContext.SaveChanges();

            var personService = new PersonService(rockContext);
            var personQuery   = personService.Queryable().Where(p => p.ForeignKey == ForeignKey);

            personService.DeleteRange(personQuery);
            rockContext.SaveChanges();
        }
        public void Execute_AddsNewStep()
        {
            var jobContext = new TestJobContext();

            jobContext.JobDetail.JobDataMap[StepsAutomation.AttributeKey.DuplicatePreventionDayRange] = 7.ToString();

            var job = new StepsAutomation();

            job.Execute(jobContext);

            var rockContext        = new RockContext();
            var stepProgramService = new StepProgramService(rockContext);
            var stepProgram        = stepProgramService.Queryable("StepTypes.Steps.StepStatus").FirstOrDefault(sp => sp.ForeignKey == ForeignKey);

            Assert.AreEqual(4, stepProgram.StepTypes.Count);

            foreach (var stepType in stepProgram.StepTypes)
            {
                if (stepType.AutoCompleteDataViewId.HasValue)
                {
                    // The three people in the dataview should have completed steps
                    Assert.AreEqual(3, stepType.Steps.Count);

                    foreach (var step in stepType.Steps)
                    {
                        Assert.IsTrue(step.IsComplete);
                        Assert.IsTrue(step.StepStatus.IsCompleteStatus);
                        Assert.IsNotNull(step.CompletedDateTime);
                    }
                }
                else
                {
                    // No steps should exist for a step type with no auto-complete dataview
                    Assert.AreEqual(0, stepType.Steps.Count);
                }
            }
        }
예제 #11
0
        /// <summary>
        /// Gets the models from the delimited values.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="stepProgram">The step program</param>
        /// <param name="stepType">The step type</param>
        private void GetModelsFromAttributeValue(string value, out StepProgram stepProgram, out StepType stepType)
        {
            stepProgram = null;
            stepType    = null;

            ParseDelimitedGuids(value, out var stepProgramGuid, out var stepTypeGuid);

            if (stepProgramGuid.HasValue || stepTypeGuid.HasValue)
            {
                var rockContext = new RockContext();

                if (stepProgramGuid.HasValue)
                {
                    var stepProgramService = new StepProgramService(rockContext);
                    stepProgram = stepProgramService.Queryable().AsNoTracking().FirstOrDefault(sp => sp.Guid == stepProgramGuid.Value);
                }

                if (stepTypeGuid.HasValue)
                {
                    var stepTypeService = new StepTypeService(rockContext);
                    stepType = stepTypeService.Queryable().AsNoTracking().FirstOrDefault(sp => sp.Guid == stepTypeGuid.Value);
                }
            }
        }
예제 #12
0
        /// <summary>
        /// Creates the child controls.
        /// </summary>
        /// <returns></returns>
        public override Control[] CreateChildControls(Type entityType, FilterField filterControl)
        {
            var dataContext = new RockContext();

            // Step Program selection
            var stepProgramSingleEntityPicker = new SingleEntityPicker <StepProgram>();

            stepProgramSingleEntityPicker.ID = filterControl.ID + "_StepProgramPicker";
            stepProgramSingleEntityPicker.AddCssClass("js-step-program-picker");
            stepProgramSingleEntityPicker.Label    = "Step Program";
            stepProgramSingleEntityPicker.Help     = "The Program in which the Step was undertaken.";
            stepProgramSingleEntityPicker.Required = true;

            stepProgramSingleEntityPicker.SelectedIndexChanged += StepProgramSingleEntityPicker_SelectedIndexChanged;
            stepProgramSingleEntityPicker.AutoPostBack          = true;

            var programService = new StepProgramService(dataContext);

            var availablePrograms = programService.Queryable()
                                    .Where(x => x.IsActive)
                                    .OrderBy(a => a.Order)
                                    .ThenBy(a => a.Name)
                                    .ToList();

            stepProgramSingleEntityPicker.InitializeListItems(availablePrograms, x => x.Name, allowEmptySelection: true);


            filterControl.Controls.Add(stepProgramSingleEntityPicker);

            // Step Type selection
            var cblStepType = new RockCheckBoxList();

            cblStepType.ID = filterControl.ID + "_cblStepType";
            cblStepType.AddCssClass("js-step-type");
            cblStepType.Label = "Steps";
            cblStepType.Help  = "If selected, specifies the required Steps that have been undertaken.";

            filterControl.Controls.Add(cblStepType);

            // Step Status selection
            var cblStepStatus = new RockCheckBoxList();

            cblStepStatus.ID = filterControl.ID + "_cblStepStatus";
            cblStepStatus.AddCssClass("js-step-status");
            cblStepStatus.Label = "Statuses";
            cblStepStatus.Help  = "If selected, specifies the required Statuses of the Steps.";

            filterControl.Controls.Add(cblStepStatus);

            // Date Started
            var drpStarted = new SlidingDateRangePicker();

            drpStarted.ID = filterControl.ID + "_drpDateStarted";
            drpStarted.AddCssClass("js-date-started");
            drpStarted.Label = "Date Started";
            drpStarted.Help  = "The date range within which the Step was started";

            filterControl.Controls.Add(drpStarted);

            // Date Completed
            var drpCompleted = new SlidingDateRangePicker();

            drpCompleted.ID = filterControl.ID + "_drpDateCompleted";
            drpCompleted.AddCssClass("js-date-completed");
            drpCompleted.Label = "Date Completed";
            drpCompleted.Help  = "The date range within which the Step was completed";

            filterControl.Controls.Add(drpCompleted);

            // Step Campus selection
            var cblStepCampus = new RockCheckBoxList();

            cblStepCampus.ID = filterControl.ID + "_cblStepCampus";
            cblStepCampus.AddCssClass("js-step-campus");
            cblStepCampus.Label = "Campuses";
            cblStepCampus.Help  = "Select the campuses that the steps were completed at. Not selecting a value will select all campuses.";

            filterControl.Controls.Add(cblStepCampus);

            // Populate lists
            PopulateStepProgramRelatedSelectionLists(filterControl);
            PopulateStepCampuses(cblStepCampus);

            return(new Control[] { stepProgramSingleEntityPicker, cblStepType, cblStepStatus, drpStarted, drpCompleted, cblStepCampus });
        }
예제 #13
0
        /// <summary>
        /// Initialize the essential context in which this block is operating.
        /// </summary>
        /// <returns>True, if the block context is valid.</returns>
        private bool InitializeBlockContext()
        {
            _program = null;

            // Try to load the Step Program from the cache.
            var programGuid = GetAttributeValue(AttributeKey.StepProgram).AsGuid();

            int    programId = 0;
            string sharedItemKey;

            // If a Step Program is specified in the block settings use it, otherwise use the PageParameters.
            if (programGuid != Guid.Empty)
            {
                sharedItemKey = string.Format("{0}:{1}", PageParameterKey.StepProgramId, programGuid);
            }
            else
            {
                programId = PageParameter(PageParameterKey.StepProgramId).AsInteger();

                sharedItemKey = string.Format("{0}:{1}", PageParameterKey.StepProgramId, programId);
            }

            if (!string.IsNullOrEmpty(sharedItemKey))
            {
                _program = RockPage.GetSharedItem(sharedItemKey) as StepProgram;
            }

            // Retrieve the program from the data store and cache for subsequent use.
            if (_program == null)
            {
                var dataContext = this.GetDataContext();

                var stepProgramService = new StepProgramService(dataContext);

                if (programGuid != Guid.Empty)
                {
                    _program = stepProgramService.Queryable().Where(g => g.Guid == programGuid).FirstOrDefault();
                }
                else if (programId != 0)
                {
                    _program = stepProgramService.Queryable().Where(g => g.Id == programId).FirstOrDefault();
                }

                if (_program != null)
                {
                    RockPage.SaveSharedItem(sharedItemKey, _program);
                }
            }

            // Verify the Step Program is valid.
            if (_program == null)
            {
                this.ShowNotification("There are no Step Types available in this context.", NotificationBoxType.Info, true);
                return(false);
            }

            // Check for View permissions.
            if (!_program.IsAuthorized(Authorization.VIEW, CurrentPerson))
            {
                this.ShowNotification("Sorry, you are not authorized to view this content.", NotificationBoxType.Danger, true);
                return(false);
            }

            return(true);
        }
예제 #14
0
        public void AddStepsRandomParticipationEntries()
        {
            // Get a complete set of active Step Types ordered by Program and structure order.
            var dataContext = new RockContext();

            var programService = new StepProgramService(dataContext);

            var programIdList = programService.Queryable().Where(x => x.StepTypes.Any()).OrderBy(x => x.Order).Select(x => x.Id).ToList();

            var statusService = new StepStatusService(dataContext);

            var statuses = statusService.Queryable().ToList();

            // Get a random selection of people that are not system users or specific users for which test data already exists.
            var personService = new PersonService(dataContext);

            int tedPersonAliasId = 0;

            var testPeopleIdList = new List <int> {
                tedPersonAliasId
            };

            var personQuery = personService.Queryable().Where(x => !x.IsSystem && x.LastName != "Anonymous" && !testPeopleIdList.Contains(x.Id)).Select(x => x.Id);

            var personAliasService = new PersonAliasService(dataContext);

            var personAliasIdList = personAliasService.Queryable().Where(x => personQuery.Contains(x.PersonId)).Select(x => x.Id).ToList();

            var personAliasIdQueue = new Queue <int>(personAliasIdList.GetRandomizedList(_MaxPersonCount));

            int      stepCounter   = 0;
            int      personAliasId = 0;
            int      stepProgramId = 0;
            DateTime startDateTime = RockDateTime.Now;
            DateTime newStepDateTime;
            int      campusId;
            int      maxStepTypeCount;
            int      stepsToAddCount;
            int      offsetDays;
            int      personCounter = 0;
            bool     isCompleted;

            // Loop through the set of people, adding at least 1 program and 1 step for each person.
            var rng = new Random();

            var typeService = new StepTypeService(dataContext);

            var stepTypesAll = typeService.Queryable().ToList();

            var campusList = CampusCache.All();

            StepService stepService = null;

            while (personAliasIdQueue.Any())
            {
                personAliasId = personAliasIdQueue.Dequeue();

                personCounter += 1;

                // Randomly select the Programs that this person will participate in.
                var addProgramCount = rng.Next(1, programIdList.Count + 1);

                var programsToAdd = new Queue <int>(programIdList.GetRandomizedList(addProgramCount));

                while (programsToAdd.Any())
                {
                    stepProgramId = programsToAdd.Dequeue();

                    newStepDateTime = startDateTime;

                    // Get a random campus at which the step was completed.
                    campusId = campusList.GetRandomElement().Id;

                    var stepStatuses = statusService.Queryable().Where(x => x.StepProgramId == stepProgramId).ToList();

                    maxStepTypeCount = stepTypesAll.Count(x => x.StepProgramId == stepProgramId);

                    // Randomly select a number of Steps that this person will achieve in the Program, in Step order.
                    // This creates a distribution weighted toward achievement of earlier Steps, which is the likely scenario for most Programs.
                    // Steps are added from last to first in reverse chronological order, with the last step being achieved in the current year.
                    stepsToAddCount = rng.Next(1, maxStepTypeCount);

                    Debug.Print($"Adding Steps: PersonAliasId: {personAliasId}, ProgramId={stepProgramId}, Steps={stepsToAddCount}");

                    var stepTypesToAdd = new Queue <StepType>(stepTypesAll.Take(stepsToAddCount).Reverse());

                    while (stepTypesToAdd.Any())
                    {
                        var stepTypeToAdd = stepTypesToAdd.Dequeue();

                        // If this is not the last step to be added for this person, make sure the status represents a completion.
                        if (stepTypesToAdd.Any())
                        {
                            isCompleted = true;
                        }
                        else
                        {
                            isCompleted = rng.Next(1, 100) <= _PercentChanceOfLastStepCompletion;
                        }

                        var eligibleStatuses = stepStatuses.Where(x => x.IsCompleteStatus == isCompleted).ToList();

                        // If there is no status that represents completion, allow any status.
                        if (eligibleStatuses.Count == 0)
                        {
                            eligibleStatuses = stepStatuses;
                        }

                        var newStatus = eligibleStatuses.GetRandomElement();

                        // Subtract a random number of days from the current step date to get a suitable date for the preceding step in the program.
                        offsetDays = rng.Next(1, _MaxDaysBetweenSteps);

                        newStepDateTime = newStepDateTime.AddDays(-1 * offsetDays);

                        var newStep = new Step();

                        newStep.StepTypeId = stepTypeToAdd.Id;

                        if (newStatus != null)
                        {
                            newStep.StepStatusId = newStatus.Id;
                        }

                        newStep.PersonAliasId = personAliasId;
                        newStep.CampusId      = campusId;
                        newStep.StartDateTime = newStepDateTime;

                        if (isCompleted)
                        {
                            newStep.CompletedDateTime = newStepDateTime;
                        }

                        newStep.ForeignKey = _SampleDataForeignKey;

                        if (stepService == null)
                        {
                            var stepDataContext = new RockContext();

                            stepService = new StepService(stepDataContext);
                        }

                        stepService.Add(newStep);

                        // Save a batch of records and recycle the context to speed up processing.
                        if (stepCounter % 100 == 0)
                        {
                            stepService.Context.SaveChanges();

                            stepService = null;
                        }

                        stepCounter++;
                    }
                }
            }

            Debug.Print($"--> Created {stepCounter} steps for {personCounter} people.");
        }
        public void Execute_RespectsAllowMultipleTrue()
        {
            // Create a complete step for a step type that does allow multiple for a person in the dataview
            var stepGuid = Guid.NewGuid();
            var stepCompletedDateTime = new DateTime(2000, 1, 1);
            int stepTypeId;

            using (var rockContext = new RockContext())
            {
                var stepProgramService = new StepProgramService(rockContext);
                var personAliasService = new PersonAliasService(rockContext);
                var stepService        = new StepService(rockContext);

                var stepProgram = stepProgramService.Queryable("StepTypes.Steps.StepStatus").FirstOrDefault(sp => sp.ForeignKey == ForeignKey);
                stepTypeId = stepProgram.StepTypes.FirstOrDefault(st => st.AutoCompleteDataViewId.HasValue && st.AllowMultiple).Id;

                stepService.Add(new Step
                {
                    ForeignKey        = ForeignKey,
                    StepTypeId        = stepTypeId,
                    StepStatus        = stepProgram.StepStatuses.FirstOrDefault(ss => ss.IsCompleteStatus),
                    CompletedDateTime = stepCompletedDateTime,
                    PersonAlias       = personAliasService.Queryable().FirstOrDefault(pa =>
                                                                                      pa.ForeignKey == ForeignKey &&
                                                                                      pa.Person.MiddleName == DataViewMiddleName),
                    Guid = stepGuid
                });

                rockContext.SaveChanges();
            }

            // Run the job
            var jobContext = new TestJobContext();

            jobContext.JobDetail.JobDataMap[StepsAutomation.AttributeKey.DuplicatePreventionDayRange] = 7.ToString();

            var job = new StepsAutomation();

            job.Execute(jobContext);

            // Refresh the data from the database
            using (var rockContext = new RockContext())
            {
                var stepProgramService = new StepProgramService(rockContext);
                var stepProgram        = stepProgramService.Queryable("StepTypes.Steps.StepStatus").FirstOrDefault(sp => sp.ForeignKey == ForeignKey);
                var foundOriginalStep  = false;

                Assert.AreEqual(4, stepProgram.StepTypes.Count);

                foreach (var stepType in stepProgram.StepTypes)
                {
                    if (stepType.Id == stepTypeId)
                    {
                        // This is the allow multiple type with an autocomplete dataview
                        // There should be the original step and now also 3 new ones.
                        Assert.AreEqual(4, stepType.Steps.Count);

                        foreach (var step in stepType.Steps)
                        {
                            // The original step should be found and the completed datetime should not have changed
                            if (step.Guid == stepGuid)
                            {
                                foundOriginalStep = true;
                                Assert.IsTrue(step.CompletedDateTime.HasValue);
                                Assert.AreEqual(stepCompletedDateTime, step.CompletedDateTime.Value);
                            }

                            Assert.IsTrue(step.IsComplete);
                            Assert.IsTrue(step.StepStatus.IsCompleteStatus);
                            Assert.IsNotNull(step.CompletedDateTime);
                        }
                    }
                    else if (stepType.AutoCompleteDataViewId.HasValue)
                    {
                        // There should be a completed step for each person in the dataview
                        Assert.AreEqual(3, stepType.Steps.Count);

                        foreach (var step in stepType.Steps)
                        {
                            Assert.IsTrue(step.IsComplete);
                            Assert.IsTrue(step.StepStatus.IsCompleteStatus);
                            Assert.IsNotNull(step.CompletedDateTime);
                        }
                    }
                    else
                    {
                        // No steps for types with no dataview
                        Assert.AreEqual(0, stepType.Steps.Count);
                    }
                }

                Assert.IsTrue(foundOriginalStep);
            }
        }
        public void Execute_CompletesExistingStep()
        {
            // Add an in-progress step that should be made complete by the job
            var stepGuid = Guid.NewGuid();

            using (var rockContext = new RockContext())
            {
                var stepProgramService = new StepProgramService(rockContext);
                var personAliasService = new PersonAliasService(rockContext);
                var stepService        = new StepService(rockContext);

                var stepProgram = stepProgramService.Queryable("StepTypes.Steps.StepStatus").FirstOrDefault(sp => sp.ForeignKey == ForeignKey);

                stepService.Add(new Step
                {
                    ForeignKey        = ForeignKey,
                    StepTypeId        = stepProgram.StepTypes.FirstOrDefault(st => st.AutoCompleteDataViewId.HasValue).Id,
                    StepStatus        = stepProgram.StepStatuses.FirstOrDefault(ss => !ss.IsCompleteStatus),
                    CompletedDateTime = null,
                    PersonAlias       = personAliasService.Queryable().FirstOrDefault(pa =>
                                                                                      pa.ForeignKey == ForeignKey &&
                                                                                      pa.Person.MiddleName == DataViewMiddleName),
                    Guid = stepGuid
                });

                rockContext.SaveChanges();
            }

            // Run the job
            var jobContext = new TestJobContext();

            jobContext.JobDetail.JobDataMap[StepsAutomation.AttributeKey.DuplicatePreventionDayRange] = 7.ToString();

            var job = new StepsAutomation();

            job.Execute(jobContext);

            // Refresh the data from the database
            using (var rockContext = new RockContext())
            {
                var stepProgramService = new StepProgramService(rockContext);
                var stepProgram        = stepProgramService.Queryable("StepTypes.Steps.StepStatus").FirstOrDefault(sp => sp.ForeignKey == ForeignKey);
                var foundOriginalStep  = false;

                Assert.AreEqual(4, stepProgram.StepTypes.Count);

                foreach (var stepType in stepProgram.StepTypes)
                {
                    if (stepType.AutoCompleteDataViewId.HasValue)
                    {
                        // The 3 people of the dataview should have a completed step
                        Assert.AreEqual(3, stepType.Steps.Count);

                        foreach (var step in stepType.Steps)
                        {
                            if (step.Guid == stepGuid)
                            {
                                // We need to ensure that the original step (was in-progress) still exists
                                foundOriginalStep = true;
                            }

                            Assert.IsTrue(step.IsComplete);
                            Assert.IsTrue(step.StepStatus.IsCompleteStatus);
                            Assert.IsNotNull(step.CompletedDateTime);
                        }
                    }
                    else
                    {
                        // No steps should exist for a type with no auto-complete dataview
                        Assert.AreEqual(0, stepType.Steps.Count);
                    }
                }

                Assert.IsTrue(foundOriginalStep);
            }
        }