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