/// <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());
        }