/// <summary> /// Gets the step type. /// </summary> /// <param name="rockContext"></param> /// <param name="action">The action.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> private StepType GetStepType(RockContext rockContext, WorkflowAction action, out string errorMessage) { errorMessage = string.Empty; var stepTypeIncludes = "StepProgram.StepStatuses"; var stepTypeService = new StepTypeService(rockContext); StepType stepType = null; var stepTypeValue = GetLavaAttributeValue(action, AttributeKey.StepProgramStepType); // Check if the value is a guid. This method works for stepProgram|stepType or simply just step type guids StepProgramStepTypeFieldType.ParseDelimitedGuids(stepTypeValue, out var unused1, out var stepTypeGuid); if (stepTypeGuid.HasValue) { stepType = stepTypeService.Queryable(stepTypeIncludes).AsNoTracking().FirstOrDefault(st => st.Guid == stepTypeGuid.Value); if (stepType == null) { errorMessage = $"The step type with the guid '{stepTypeGuid.Value}' was not found"; return(null); } } // Try to get a step type with a step type id if (stepType == null) { var stepTypeId = stepTypeValue.AsIntegerOrNull(); if (!stepTypeId.HasValue) { errorMessage = "The step type identifier is required but was missing"; return(null); } stepType = stepTypeService.Queryable(stepTypeIncludes).AsNoTracking().FirstOrDefault(st => st.Id == stepTypeId.Value); if (stepType == null) { errorMessage = $"The step type with the id '{stepTypeId.Value}' was not found"; return(null); } } if (stepType.StepProgram == null) { errorMessage = $"The step type '{stepType.Name}' program is missing"; return(null); } if (stepType.StepProgram.StepStatuses == null || !stepType.StepProgram.StepStatuses.Any()) { errorMessage = $"The step program '{stepType.StepProgram.Name}' does not have any statuses"; return(null); } return(stepType); }
/// <summary> /// Get the step type model /// </summary> /// <returns></returns> private StepType GetStepType() { if (_stepType == null) { var step = GetStep(); if (step != null) { _stepType = step.StepType; } else { var stepTypeId = GetAttributeValue(AttributeKey.StepType).AsIntegerOrNull() ?? PageParameter(ParameterKey.StepTypeId).AsIntegerOrNull(); if (stepTypeId.HasValue) { var rockContext = GetRockContext(); var service = new StepTypeService(rockContext); _stepType = service.Queryable() .AsNoTracking() .FirstOrDefault(st => st.Id == stepTypeId.Value && st.IsActive); } } } return(_stepType); }
/// <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> internal static void LoadDropDownItems(IStepTypePicker picker, bool includeEmptyOption) { var selectedItems = picker.Items.Cast <ListItem>() .Where(i => i.Selected) .Select(i => i.Value).AsIntegerList(); picker.Items.Clear(); if (!picker.StepProgramId.HasValue) { return; } if (includeEmptyOption) { // add Empty option first picker.Items.Add(new ListItem()); } var stepTypeService = new StepTypeService(new RockContext()); var stepTypes = stepTypeService.Queryable().AsNoTracking() .Where(st => st.StepProgramId == picker.StepProgramId.Value && st.IsActive) .OrderBy(st => st.Order) .ThenBy(st => st.Name) .ToList(); foreach (var stepType in stepTypes) { var li = new ListItem(stepType.Name, stepType.Id.ToString()); li.Selected = selectedItems.Contains(stepType.Id); picker.Items.Add(li); } }
/// <summary> /// Gets the step type view query. All active step types that have a person based /// dataview configured /// </summary> /// <returns></returns> private List <StepTypeView> GetStepTypeViews() { var personEntityTypeId = EntityTypeCache.Get <Person>().Id; var rockContext = new RockContext(); var stepTypeService = new StepTypeService(rockContext); var views = stepTypeService.Queryable().AsNoTracking() .Where(st => st.StepProgram.IsActive && st.IsActive && st.AutoCompleteDataViewId.HasValue && st.AutoCompleteDataView.EntityTypeId == personEntityTypeId) .Select(st => new StepTypeView { StepTypeId = st.Id, StepProgramId = st.StepProgramId, AllowMultiple = st.AllowMultiple, AutoCompleteDataViewId = st.AutoCompleteDataViewId.Value, PrerequisiteStepTypeIds = st.StepTypePrerequisites.Select(stp => stp.PrerequisiteStepTypeId), // Get a list of the step statuses, but only take 1 so that it doesn't cause a second database call CompletedStepStatusIds = st.StepProgram.StepStatuses .OrderBy(ss => ss.Order) .Where(ss => ss.IsCompleteStatus && ss.IsActive) .Select(ss => ss.Id) .Take(1) }) .ToList(); return(views); }
/// <summary> /// Populates the selection lists for Step Type and Step Status. /// </summary> /// <param name="filterField">The filter field.</param> private void PopulateStepProgramRelatedSelectionLists(FilterField filterField) { var dataContext = new RockContext(); var programService = new StepProgramService(dataContext); SingleEntityPicker <StepProgram> stepProgramSingleEntityPicker = filterField.ControlsOfTypeRecursive <SingleEntityPicker <StepProgram> >().FirstOrDefault(c => c.HasCssClass("js-step-program-picker")); RockCheckBoxList cblStepType = filterField.ControlsOfTypeRecursive <RockCheckBoxList>().FirstOrDefault(c => c.HasCssClass("js-step-type")); RockCheckBoxList _cblStepStatus = filterField.ControlsOfTypeRecursive <RockCheckBoxList>().FirstOrDefault(c => c.HasCssClass("js-step-status")); int?stepProgramId = stepProgramSingleEntityPicker.SelectedId; StepProgram stepProgram = null; if (stepProgramId != null) { stepProgram = programService.Get(stepProgramId.Value); } if (stepProgram != null) { // Step Type list cblStepType.Items.Clear(); var stepTypeService = new StepTypeService(dataContext); var stepTypes = stepTypeService.Queryable().Where(x => x.StepProgramId == stepProgramId); foreach (var item in stepTypes) { cblStepType.Items.Add(new ListItem(item.Name, item.Guid.ToString())); } cblStepType.Visible = cblStepType.Items.Count > 0; // Step Status list _cblStepStatus.Items.Clear(); var stepStatusService = new StepStatusService(dataContext); var stepStatuses = stepStatusService.Queryable().Where(x => x.StepProgramId == stepProgramId); foreach (var item in stepStatuses) { _cblStepStatus.Items.Add(new ListItem(item.Name, item.Guid.ToString())); } _cblStepStatus.Visible = _cblStepStatus.Items.Count > 0; } else { cblStepType.Visible = false; _cblStepStatus.Visible = false; } }
/// <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> /// Populates the selection lists for Step Type and Step Status. /// </summary> /// <param name="stepProgramId">The Step Program identifier.</param> private void PopulateStepProgramRelatedSelectionLists(int?stepProgramId) { var dataContext = new RockContext(); var programService = new StepProgramService(dataContext); StepProgram stepProgram = null; if (stepProgramId != null) { stepProgram = programService.Get(stepProgramId.Value); } if (stepProgram != null) { // Step Type list _cblStepType.Items.Clear(); var stepTypeService = new StepTypeService(dataContext); var stepTypes = stepTypeService.Queryable().Where(x => x.StepProgramId == stepProgramId); foreach (var item in stepTypes) { _cblStepType.Items.Add(new ListItem(item.Name, item.Guid.ToString())); } _cblStepType.Visible = _cblStepType.Items.Count > 0; // Step Status list _cblStepStatus.Items.Clear(); var stepStatusService = new StepStatusService(dataContext); var stepStatuses = stepStatusService.Queryable().Where(x => x.StepProgramId == stepProgramId); foreach (var item in stepStatuses) { _cblStepStatus.Items.Add(new ListItem(item.Name, item.Guid.ToString())); } _cblStepStatus.Visible = _cblStepStatus.Items.Count > 0; } else { _cblStepType.Visible = false; _cblStepStatus.Visible = false; } }
private void CreateWellKnownGuidToIdMap() { _GuidToIdMap = new Dictionary <Guid, int>(); Dictionary <Guid, int> guidDictionary; var dataContext = new RockContext(); // Add Step Types var stepTypeService = new StepTypeService(dataContext); guidDictionary = stepTypeService.Queryable().ToDictionary(k => k.Guid, v => v.Id); _GuidToIdMap = _GuidToIdMap.Union(guidDictionary).ToDictionary(k => k.Key, v => v.Value); // Add Step Statuses var stepStatusService = new StepStatusService(dataContext); guidDictionary = stepStatusService.Queryable().ToDictionary(k => k.Guid, v => v.Id); _GuidToIdMap = _GuidToIdMap.Union(guidDictionary).ToDictionary(k => k.Key, v => v.Value); // Add Person Aliases - Map Person Guid to PersonAlias.Id var personAliasService = new PersonAliasService(dataContext); var personService = new PersonService(dataContext); var personKnownGuids = new List <Guid>(); personKnownGuids.Add(Constants.AlishaMarblePersonGuid); personKnownGuids.Add(Constants.BenJonesPersonGuid); personKnownGuids.Add(Constants.BillMarblePersonGuid); personKnownGuids.Add(Constants.BrianJonesPersonGuid); personKnownGuids.Add(Constants.SarahSimmonsPersonGuid); personKnownGuids.Add(Constants.TedDeckerPersonGuid); var knownPeople = personService.Queryable().Where(x => personKnownGuids.Contains(x.Guid)); foreach (var knownPerson in knownPeople) { _GuidToIdMap.Add(knownPerson.Guid, knownPerson.PrimaryAliasId ?? 0); } }
/// <summary> /// Set the ordinal position of an item in the list. /// </summary> /// <param name="oldIndex"></param> /// <param name="newIndex"></param> private void ReorderStepType(int oldIndex, int newIndex) { if (_program == null) { return; } var rockContext = this.GetDataContext(); var service = new StepTypeService(rockContext); var stepTypes = service.Queryable() .Where(x => x.StepProgramId == _program.Id) .OrderBy(b => b.Order) .ToList(); service.Reorder(stepTypes, oldIndex, newIndex); rockContext.SaveChanges(); this.BindGrid(); }
/// <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); } } }
/// <summary> /// Gets the expression. /// </summary> /// <param name="entityType">Type of the entity.</param> /// <param name="serviceInstance">The service instance.</param> /// <param name="parameterExpression">The parameter expression.</param> /// <param name="selection">The selection.</param> /// <returns></returns> public override Expression GetExpression(Type entityType, IService serviceInstance, ParameterExpression parameterExpression, string selection) { var settings = new FilterSettings(selection); if (!settings.IsValid) { return(null); } var dataContext = ( RockContext )serviceInstance.Context; int stepProgramId = 0; var stepProgram = GetStepProgram(dataContext, settings.StepProgramGuid); if (stepProgram != null) { stepProgramId = stepProgram.Id; } var stepService = new StepService(dataContext); // Filter by Step Program var stepQuery = stepService.Queryable().Where(x => x.StepType.StepProgramId == stepProgramId); // Filter by Step Types if (settings.StepTypeGuids.Count() > 0) { var stepTypeService = new StepTypeService(dataContext); var stepTypeIds = stepTypeService.Queryable() .Where(a => settings.StepTypeGuids.Contains(a.Guid)) .Select(a => a.Id).ToList(); stepQuery = stepQuery.Where(x => stepTypeIds.Contains(x.StepTypeId)); } // Filter by Step Status if (settings.StepStatusGuids.Count() > 0) { var stepStatusService = new StepStatusService(dataContext); var stepStatusIds = stepStatusService.Queryable() .Where(a => settings.StepStatusGuids.Contains(a.Guid)) .Select(a => a.Id).ToList(); stepQuery = stepQuery.Where(x => x.StepStatusId.HasValue && stepStatusIds.Contains(x.StepStatusId.Value)); } // Filter by Date Started if (settings.StartedInPeriod != null) { var startDateRange = settings.StartedInPeriod.GetDateRange(TimePeriodDateRangeBoundarySpecifier.Exclusive); if (startDateRange.Start != null) { stepQuery = stepQuery.Where(x => x.StartDateTime > startDateRange.Start.Value); } if (startDateRange.End != null) { stepQuery = stepQuery.Where(x => x.StartDateTime < startDateRange.End.Value); } } // Filter by Date Completed if (settings.CompletedInPeriod != null) { var completedDateRange = settings.CompletedInPeriod.GetDateRange(TimePeriodDateRangeBoundarySpecifier.Exclusive); if (completedDateRange.Start != null) { stepQuery = stepQuery.Where(x => x.CompletedDateTime > completedDateRange.Start.Value); } if (completedDateRange.End != null) { stepQuery = stepQuery.Where(x => x.CompletedDateTime < completedDateRange.End.Value); } } // Filter by Step Campus if (settings.StepCampusGuids.Count() > 0) { var campusService = new CampusService(dataContext); var stepCampusIds = campusService.Queryable() .Where(a => settings.StepCampusGuids.Contains(a.Guid)) .Select(a => a.Id).ToList(); stepQuery = stepQuery.Where(x => x.CampusId.HasValue && stepCampusIds.Contains(x.CampusId.Value)); } // Create Person Query. var personService = new PersonService(( RockContext )serviceInstance.Context); var qry = personService.Queryable() .Where(p => stepQuery.Any(x => x.PersonAlias.PersonId == p.Id)); var extractedFilterExpression = FilterExpressionExtractor.Extract <Rock.Model.Person>(qry, parameterExpression, "p"); return(extractedFilterExpression); }
/// <summary> /// Formats the selection. /// </summary> /// <param name="entityType">Type of the entity.</param> /// <param name="selection">The selection.</param> /// <returns></returns> public override string FormatSelection(Type entityType, string selection) { string result = "Steps taken"; var settings = new FilterSettings(selection); if (!settings.IsValid) { return(result); } var dataContext = new RockContext(); // Step Program var stepProgram = this.GetStepProgram(dataContext, settings.StepProgramGuid); if (stepProgram == null) { return(result); } // Step Types List <StepType> stepTypes; if (settings.StepTypeGuids != null) { var stepTypeService = new StepTypeService(dataContext); stepTypes = stepTypeService.Queryable().Where(a => settings.StepTypeGuids.Contains(a.Guid)).ToList(); } else { stepTypes = new List <StepType>(); } // Step Statuses List <StepStatus> stepStatuses; if (settings.StepStatusGuids != null) { var stepStatusService = new StepStatusService(dataContext); stepStatuses = stepStatusService.Queryable().Where(a => settings.StepStatusGuids.Contains(a.Guid)).ToList(); } else { stepStatuses = new List <StepStatus>(); } // Step Campuses List <CampusCache> stepCampuses; if (settings.StepCampusGuids != null) { stepCampuses = CampusCache.All().Where(a => settings.StepCampusGuids.Contains(a.Guid)).ToList(); } else { stepCampuses = new List <CampusCache>(); } result += string.Format(" in Program: {0}", stepProgram.Name); if (stepTypes.Any()) { result += string.Format(", in Step: {0}", stepTypes.Select(a => a.Name).ToList().AsDelimited(",")); } if (stepStatuses.Any()) { result += string.Format(", with Status: {0}", stepStatuses.Select(a => a.Name).ToList().AsDelimited(",")); } // Start Date if (settings.StartedInPeriod != null && settings.StartedInPeriod.Range != TimePeriodRangeSpecifier.All) { result += string.Format(", with Date Started: {0}", settings.StartedInPeriod.GetDescription()); } // Completion Date if (settings.CompletedInPeriod != null && settings.CompletedInPeriod.Range != TimePeriodRangeSpecifier.All) { result += string.Format(", with Date Completed: {0}", settings.CompletedInPeriod.GetDescription()); } if (stepCampuses.Any()) { result += string.Format(", at Campus: {0}", stepCampuses.Select(a => a.Name).ToList().AsDelimited(",")); } return(result); }
/// <summary> /// Executes the specified context. /// </summary> /// <param name="context">The context.</param> /// <exception cref="System.NotImplementedException"></exception> public void Execute(IJobExecutionContext context) { JobDataMap dataMap = context.JobDetail.JobDataMap; // get the configured timeout, or default to 60 minutes if it is blank var commandTimeout = dataMap.GetString(AttributeKey.CommandTimeout).AsIntegerOrNull() ?? AttributeDefaults.CommandTimeout; var isProcessingComplete = false; var batchSize = 2; var totalBatchSize = 0; var currentBatch = 1; totalBatchSize = new StepService(new RockContext()) .Queryable() .Where(a => a.CompletedDateTime.HasValue && !a.StepProgramCompletionId.HasValue) .Select(a => a.PersonAlias.PersonId) .Distinct() .Count(); var runtime = System.Diagnostics.Stopwatch.StartNew(); var lastProcessedPersonId = 0; while (!isProcessingComplete) { using (var rockContext = new RockContext()) { var stepTypeService = new StepTypeService(rockContext); var stepService = new StepService(rockContext); var personQry = stepService .Queryable() .Where(a => a.CompletedDateTime.HasValue && !a.StepProgramCompletionId.HasValue && a.PersonAlias.PersonId > lastProcessedPersonId) .Select(a => a.PersonAlias.PersonId) .Distinct() .OrderBy(a => a) .Take(batchSize); var stepProgramStepTypeMappings = stepTypeService .Queryable() .Where(a => a.IsActive) .GroupBy(a => a.StepProgramId) .ToDictionary(a => a.Key, b => b.Select(c => c.Id).ToList()); var steps = new StepService(rockContext) .Queryable("PersonAlias") .AsNoTracking() .Where(a => personQry.Contains(a.PersonAlias.PersonId) && !a.StepProgramCompletionId.HasValue && a.CompletedDateTime.HasValue) .ToList(); isProcessingComplete = personQry.Count() < batchSize; var batchPersonIds = personQry.ToList(); foreach (var personId in batchPersonIds) { var personSteps = steps.Where(a => a.PersonAlias.PersonId == personId); if (!personSteps.Any()) { continue; } foreach (var stepProgramId in stepProgramStepTypeMappings.Keys) { var stepTypeIds = stepProgramStepTypeMappings[stepProgramId]; var stepsByProgram = personSteps.Where(a => stepTypeIds.Contains(a.StepTypeId)).OrderBy(a => a.CompletedDateTime).ToList(); if (!stepsByProgram.Any()) { continue; } while (stepsByProgram.Any() && stepTypeIds.All(a => stepsByProgram.Any(b => b.StepTypeId == a))) { var stepSet = new List <Step>(); foreach (var stepTypeId in stepTypeIds) { var step = stepsByProgram.Where(a => a.StepTypeId == stepTypeId).FirstOrDefault(); if (step == null) { continue; } stepSet.Add(step); stepsByProgram.RemoveAll(a => a.Id == step.Id); } var personAliasId = stepSet.Select(a => a.PersonAliasId).FirstOrDefault(); StepService.UpdateStepProgramCompletion(stepSet, personAliasId, stepProgramId); } } lastProcessedPersonId = personId; } var processTime = runtime.ElapsedMilliseconds; var recordsProcessed = ( double )(batchSize * currentBatch) + batchPersonIds.Count; var recordsPerMillisecond = recordsProcessed / processTime; var recordsRemaining = totalBatchSize - recordsProcessed; var minutesRemaining = recordsRemaining / recordsPerMillisecond / 1000 / 60; context.UpdateLastStatusMessage($"Processing {recordsProcessed} of {totalBatchSize} records. Approximately {minutesRemaining:N0} minutes remaining."); currentBatch++; } } ServiceJobService.DeleteJob(context.GetJobId()); }
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."); }