Beispiel #1
0
        public ActionResult RunEngine(int submissionCycleId)
        {
            SubmissionCycle submissionCycle = _submissionCycleService.GetSubmissionCycle(submissionCycleId);

            if (submissionCycle == null)
            {
                string strMessage = $"Collection cycle with id {submissionCycleId} not found.";
                _loggingService.LogErrorMessage(strMessage);
                throw new InvalidOperationException(strMessage);
            }

            var session = _appUserService.GetSession();
            var edOrg   = _edOrgService.GetEdOrgById(session.FocusedEdOrgId, session.FocusedSchoolYearId);

            _rulesEngineService.DeleteOldValidationRuns(submissionCycle, edOrg.Id);

            // TODO: Validate the user's access to district, action, school year
            // todo: all security

            ValidationReportSummary summary = _rulesEngineService.SetupValidationRun(
                submissionCycle,
                submissionCycle.CollectionId);

            HostingEnvironment.QueueBackgroundWorkItem(
                cancellationToken => _rulesEngineService.RunValidationAsync(
                    submissionCycle,
                    summary.ValidationReportSummaryId));

            return(Json(summary));
        }
Beispiel #2
0
        private void PopulateDropDownLists(SubmissionCycle submissionCycle = null)
        {
            List <SelectListItem> schoolYears = _submissionCycleService.GetSchoolYearsSelectList(submissionCycle);
            var ruleCollections = _rulesEngineService.GetCollections().Select(c => c.CollectionId);

            ViewData["schoolYears"]     = schoolYears;
            ViewData["RuleCollections"] = ruleCollections;
        }
Beispiel #3
0
        public ActionResult AddSubmissionCycle()
        {
            PopulateDropDownLists();
            var submissionCycle = new SubmissionCycle {
                StartDate = DateTime.Now, EndDate = DateTime.Now
            };

            return(PartialView("Partials/SubmissionCycleEditModal", submissionCycle));
        }
Beispiel #4
0
        public SubmissionCycle SchoolYearCollectionAlreadyExists(SubmissionCycle submissionCycle)
        {
            using (var validationPortalDataContext = ValidationPortalDataContextFactory.Create())
            {
                var duplicateCycle = validationPortalDataContext.SubmissionCycles
                                     .FirstOrDefault(x => x.SchoolYearId == submissionCycle.SchoolYearId && x.CollectionId == submissionCycle.CollectionId);

                return(duplicateCycle);
            }
        }
Beispiel #5
0
        public bool AddSubmissionCycle(SubmissionCycle submissionCycle)
        {
            using (var validationPortalDataContext = ValidationPortalDataContextFactory.Create())
            {
                validationPortalDataContext.SubmissionCycles.Add(submissionCycle);
                validationPortalDataContext.SaveChanges();

                return(true); // why have a return type at all? todo
            }
        }
Beispiel #6
0
        public List <SelectListItem> GetSchoolYearsSelectList(SubmissionCycle submissionCycle = null)
        {
            var schoolYearsEnumerable = SchoolYearService.GetSubmittableSchoolYearsDictionary().OrderByDescending(x => x.Value);

            List <SelectListItem> schoolYears = schoolYearsEnumerable.Select(kvPair => new SelectListItem
            {
                Value    = kvPair.Key.ToString(),
                Text     = kvPair.Value,
                Selected = (submissionCycle != null) && (kvPair.Key == submissionCycle.SchoolYearId)
            }).ToList();

            if (submissionCycle != null)
            {
                schoolYears[0].Selected = true;
            }
            return(schoolYears);
        }
Beispiel #7
0
        public void SaveSubmissionCycle(SubmissionCycle submissionCycle)
        {
            try
            {
                using (var validationPortalDataContext = ValidationPortalDataContextFactory.Create())
                {
                    if (submissionCycle == null)
                    {
                        throw new Exception("Attempted to save a null SubmissionCycle.");
                    }

                    if (submissionCycle.Id == 0)
                    {
                        validationPortalDataContext.SubmissionCycles.Add(submissionCycle);
                        validationPortalDataContext.SaveChanges();
                    }
                    else
                    {
                        var existingSubmissionCycle = validationPortalDataContext.SubmissionCycles
                                                      .FirstOrDefault(s => s.Id == submissionCycle.Id);

                        if (existingSubmissionCycle == null)
                        {
                            throw new Exception($"SubmissionCycle with id {submissionCycle.Id} does not exist.");
                        }

                        existingSubmissionCycle.CollectionId = submissionCycle.CollectionId;
                        existingSubmissionCycle.StartDate    = submissionCycle.StartDate;
                        existingSubmissionCycle.EndDate      = submissionCycle.EndDate;
                        existingSubmissionCycle.SchoolYearId = submissionCycle.SchoolYearId;
                        validationPortalDataContext.SaveChanges();
                    }
                }
            }
            catch (Exception ex)
            {
                string strMessage = string.Empty;
                if (submissionCycle != null)
                {
                    strMessage = $" with id = { submissionCycle.Id }, StartDate = { submissionCycle.StartDate }, EndDate = { submissionCycle.EndDate}, SchoolYearID = { submissionCycle.SchoolYearId }";
                }

                LoggingService.LogErrorMessage($"An error occurred while saving announcement {strMessage}: {ex.ChainInnerExceptionMessages()}");
                throw new Exception("An error occurred while saving announcement.");
            }
        }
Beispiel #8
0
        public bool AddSubmissionCycle(string collectionId, DateTime startDate, DateTime endDate)
        {
            if (collectionId == null)
            {
                // todo: better null/invalid argument handling
                return(false);
            }

            using (var validationPortalDataContext = ValidationPortalDataContextFactory.Create())
            {
                var newSubmissionCycle = new SubmissionCycle(collectionId, startDate, endDate);
                validationPortalDataContext.SubmissionCycles.Add(newSubmissionCycle);
                validationPortalDataContext.SaveChanges();

                return(true);
            }
        }
        public void DeleteOldValidationRuns(SubmissionCycle submissionCycle, int edOrgId)
        {
            if (submissionCycle?.SchoolYearId == null)
            {
                throw new ArgumentException("Submission cycle is null, or SchoolYearId is null", nameof(submissionCycle));
            }

            using (var validationDbContext = DbContextFactory.Create())
            {
                var reportSummaries = validationDbContext.ValidationReportSummaries
                                      .Where(x =>
                                             x.Collection == submissionCycle.CollectionId &&
                                             x.SchoolYearId == submissionCycle.SchoolYearId &&
                                             x.EdOrgId == edOrgId);

                if (reportSummaries.Count() > 1)
                {
                    reportSummaries = reportSummaries.OrderByDescending(x => x.RequestedWhen).Skip(1);

                    foreach (var reportSummary in reportSummaries)
                    {
                        var reportDetails = validationDbContext.ValidationReportDetails.Where(
                            x => x.ValidationReportSummaryId == reportSummary.ValidationReportSummaryId);

                        validationDbContext.ValidationReportDetails.RemoveRange(reportDetails);
                        validationDbContext.ValidationReportSummaries.Remove(reportSummary);

                        var    schoolYear         = SchoolYearService.GetSchoolYearById(reportSummary.SchoolYearId);
                        string fourDigitOdsDbYear = schoolYear.EndYear;

                        using (var odsRawDbContext = OdsDbContextFactory.CreateWithParameter(fourDigitOdsDbYear))
                        {
                            var ruleValidations = odsRawDbContext.RuleValidations.Where(
                                x => x.RuleValidationId == reportSummary.RuleValidationId);

                            odsRawDbContext.RuleValidations.RemoveRange(ruleValidations);
                            odsRawDbContext.SaveChanges();
                        }
                    }

                    validationDbContext.SaveChanges();
                }
            }
        }
Beispiel #10
0
        public void SetupValidationRun_Should_ThrowForNullSchoolYearId()
        {
            var rulesEngineService = new RulesEngineService(
                AppUserServiceMock.Object,
                EdOrgServiceMock.Object,
                SchoolYearServiceMock.Object,
                EngineConfigMock.Object,
                LoggingServiceMock.Object,
                DbContextFactoryMock.Object,
                SchoolYearDbContextFactoryMock.Object,
                EngineObjectModel,
                null);

            var submissionCycle = new SubmissionCycle {
                SchoolYearId = null
            };

            Assert.Throws <ArgumentException>(() => rulesEngineService.SetupValidationRun(submissionCycle, null));
        }
Beispiel #11
0
        public Task RunValidationAsync(SubmissionCycle submissionCycle, long ruleValidationId)
        {
            var    schoolYear         = _schoolYearService.GetSchoolYearById(submissionCycle.SchoolYearId.Value);
            string fourDigitOdsDbYear = schoolYear.EndYear;

            _loggingService.LogInfoMessage($"===== Starting validation run for year {fourDigitOdsDbYear}, ruleValidationId {ruleValidationId}");

            return(Task.Factory
                   .StartNew(() => RunValidation(submissionCycle, ruleValidationId))
                   .ContinueWith(task =>
            {
                _loggingService.LogInfoMessage($"===== Completed validation run for year {fourDigitOdsDbYear}, ruleValidationId {ruleValidationId}");

                if (task.Exception != null)
                {
                    _loggingService.LogErrorMessage(task.Exception.Flatten().ChainInnerExceptionMessages());
                }
            }));
        }
Beispiel #12
0
        public void SetupValidationRun_Should_ReturnValidationReportSummary()
        {
            var rulesEngineService = new RulesEngineService(
                AppUserServiceMock.Object,
                EdOrgServiceMock.Object,
                SchoolYearServiceMock.Object,
                EngineConfigMock.Object,
                LoggingServiceMock.Object,
                DbContextFactoryMock.Object,
                SchoolYearDbContextFactoryMock.Object,
                EngineObjectModel,
                null);

            var schoolYear = new SchoolYear
            {
                Id             = 1,
                Enabled        = true,
                ErrorThreshold = null,
                StartYear      = "2019",
                EndYear        = "2020"
            };

            var submissionCycle = new SubmissionCycle {
                SchoolYearId = schoolYear.Id, CollectionId = "collection"
            };

            SchoolYearServiceMock.Setup(x => x.GetSchoolYearById(schoolYear.Id)).Returns(schoolYear);

            AppUserServiceMock.Setup(x => x.GetSession()).Returns(DefaultTestAppUserSession);
            AppUserServiceMock.Setup(x => x.GetUser()).Returns(DefaultTestAppUserSession.UserIdentity);

            var result = rulesEngineService.SetupValidationRun(submissionCycle, submissionCycle.CollectionId);

            result.ShouldNotBeNull();
            result.SchoolYearId.ShouldEqual(schoolYear.Id);
            result.Collection.ShouldEqual(submissionCycle.CollectionId);

            SchoolYearDbContextMock.Verify(
                x => x.RuleValidations.Add(
                    It.Is <RuleValidation>(y => y.CollectionId == submissionCycle.CollectionId)));
        }
Beispiel #13
0
        public ActionResult SaveSubmissionCycle(SubmissionCycle submissionCycle)
        {
            if (submissionCycle.EndDate < submissionCycle.StartDate)
            {
                ModelState.AddModelError("EndDate", "End Date needs to be later than Start Date");
            }

            // We should not let the user add a collection cycle with the same school year and collectionId combination
            // that already exists in the database, or set the school year and collectionId of an existing cycle
            // to ones of another existing collection cycle.
            SubmissionCycle duplicate = _submissionCycleService.SchoolYearCollectionAlreadyExists(submissionCycle);

            if (duplicate != null && (submissionCycle.Id == 0 || submissionCycle.Id != duplicate.Id))
            {
                ModelState.AddModelError("CollectionId", "A collection cycle with this School Year and Collection already exists.");
            }

            if (ModelState.IsValid)
            {
                try
                {
                    _submissionCycleService.SaveSubmissionCycle(submissionCycle);
                    return(RedirectToAction("Index", new { tab = "submissioncycles" }));
                }
                catch (Exception ex)
                {
                    ModelState.AddModelError("General Error", ex.Message);
                    PopulateDropDownLists(submissionCycle);
                    return(PartialView("Partials/SubmissionCycleEditModal", submissionCycle));
                }
            }
            else
            {
                PopulateDropDownLists(submissionCycle);
                return(PartialView("Partials/SubmissionCycleEditModal", submissionCycle));
            }
        }
        public ValidationReportSummary SetupValidationRun(SubmissionCycle submissionCycle, string collectionId)
        {
            if (submissionCycle?.SchoolYearId == null)
            {
                throw new ArgumentException("Submission cycle is null or contains null SchoolYearId", nameof(submissionCycle));
            }

            var    schoolYear         = SchoolYearService.GetSchoolYearById(submissionCycle.SchoolYearId.Value);
            string fourDigitOdsDbYear = schoolYear.EndYear;

            ValidationReportSummary newReportSummary;

            using (var odsRawDbContext = OdsDbContextFactory.CreateWithParameter(fourDigitOdsDbYear))
            {
                LoggingService.LogDebugMessage(
                    $"Connecting to the Ed Fi ODS {fourDigitOdsDbYear} to run the Rules Engine. Submitting the RulesValidation run ID.");

                // Add a new execution of the Validation Engine to the ODS database, (required by the Engine) and get an ID back representing this execution.
                var newRuleValidationExecution = new RuleValidation {
                    CollectionId = collectionId
                };
                odsRawDbContext.RuleValidations.Add(newRuleValidationExecution);
                odsRawDbContext.SaveChanges();

                LoggingService.LogDebugMessage(
                    $"Successfully submitted RuleValidationId {newRuleValidationExecution.RuleValidationId.ToString()} to the Rules Engine database table.");

                // Add a new execution of the Validation Engine to the Validation database, (required by the Portal) and get an ID back representing this execution.

                /* todo: using this (Id, SchoolYearId) as a PK - this isn't reliable because it comes from the ods's id.
                 *  we can stomp other execution runs from other districts etc. the ID is the identity column in another database.
                 *  it doesn't know about what we're doing ... change the ID to the ods's execution id and set up our own identity column
                 *  that's independent (and change all references to this "id"
                 */

                newReportSummary = new ValidationReportSummary
                {
                    Collection       = collectionId,
                    CompletedWhen    = null,
                    ErrorCount       = null,
                    WarningCount     = null,
                    TotalCount       = 0,
                    RuleValidationId = newRuleValidationExecution.RuleValidationId,
                    EdOrgId          = AppUserService.GetSession().FocusedEdOrgId,
                    SchoolYearId     = schoolYear.Id,
                    InitiatedBy      = AppUserService.GetUser().FullName,
                    RequestedWhen    = DateTime.UtcNow,
                    Status           = "In Progress - Starting"
                };

                LoggingService.LogDebugMessage(
                    $"Successfully submitted Validation Report Summary ID {newReportSummary.ValidationReportSummaryId} " +
                    $"to the Validation Portal database for Rules Validation Run {newRuleValidationExecution.RuleValidationId.ToString()}.");
            }

            using (var validationDbContext = DbContextFactory.Create())
            {
                validationDbContext.ValidationReportSummaries.Add(newReportSummary);
                validationDbContext.SaveChanges();
            }

            return(newReportSummary);
        }
        public void RunValidation(SubmissionCycle submissionCycle, long ruleValidationId)
        {
            if (submissionCycle?.SchoolYearId == null)
            {
                throw new ArgumentException("Submission cycle is null, or SchoolYearId is null", nameof(submissionCycle));
            }

            var schoolYear         = SchoolYearService.GetSchoolYearById(submissionCycle.SchoolYearId.Value);
            var fourDigitOdsDbYear = schoolYear.EndYear;

            using (var odsRawDbContext = OdsDbContextFactory.CreateWithParameter(fourDigitOdsDbYear))
            {
                using (var validationDbContext = DbContextFactory.Create())
                {
                    var newReportSummary = validationDbContext
                                           .ValidationReportSummaries
                                           .Include(x => x.SchoolYear)
                                           .FirstOrDefault(x =>
                                                           x.ValidationReportSummaryId == ruleValidationId &&
                                                           x.SchoolYearId == schoolYear.Id);

                    if (newReportSummary == null)
                    {
                        throw new InvalidOperationException($"Unable to find report summary {ruleValidationId} for year {schoolYear.Id}");
                    }

                    string savedSqlPath = string.Empty;

                    if (EngineConfig.SaveGeneratedRulesSqlToFiles)
                    {
                        savedSqlPath = Path.Combine(
                            Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
                            "ValidationRulesSql",
                            ruleValidationId.ToString());

                        LoggingService.LogInfoMessage("Saved SQL Directory is " + savedSqlPath);

                        if (Directory.Exists(savedSqlPath))
                        {
                            LoggingService.LogInfoMessage("Clearing contents of " + savedSqlPath);
                            Directory.Delete(savedSqlPath, true);
                        }

                        Directory.CreateDirectory(savedSqlPath);
                    }
                    var newRuleValidationExecution = odsRawDbContext.RuleValidations
                                                     .FirstOrDefault(x =>
                                                                     x.RuleValidationId == newReportSummary.RuleValidationId);

                    if (newRuleValidationExecution == null)
                    {
                        throw new InvalidOperationException($"Unable to find execution {ruleValidationId}");
                    }

                    var collectionId = newRuleValidationExecution.CollectionId;

                    // todo: check collectionId for null/empty and why is it a string!?

                    // Now, store each Ruleset ID and Rule ID that the engine will run. Save it in the Engine database.
                    LoggingService.LogDebugMessage($"Getting the rules to run for the chosen collection {collectionId}.");
                    var rules          = EngineObjectModel.GetRules(collectionId).ToArray();
                    var ruleComponents = rules.SelectMany(
                        r => r.Components.Distinct().Select(
                            c => new
                    {
                        r.RulesetId,
                        r.RuleId,
                        Component = c
                    }));

                    foreach (var singleRuleNeedingToBeValidated in ruleComponents)
                    {
                        odsRawDbContext.RuleValidationRuleComponents.Add(
                            new RuleValidationRuleComponent
                        {
                            RuleValidationId = newRuleValidationExecution.RuleValidationId,
                            RulesetId        = singleRuleNeedingToBeValidated.RulesetId,
                            RuleId           = singleRuleNeedingToBeValidated.RuleId,
                            Component        = singleRuleNeedingToBeValidated.Component
                        });
                    }

                    odsRawDbContext.SaveChanges();
                    LoggingService.LogDebugMessage($"Saved the rules to run for the chosen collection {collectionId}.");

                    // The ValidationReportDetails is one-for-one with the ValidationReportSummary - it should be refactored away. It contains the error/warning details.
                    LoggingService.LogDebugMessage(
                        $"Adding additional Validation Report details to the Validation Portal database for EdOrgID {newReportSummary.EdOrgId}.");

                    var newReportDetails = new ValidationReportDetails
                    {
                        CollectionName            = collectionId,
                        SchoolYearId              = newReportSummary.SchoolYear.Id,
                        DistrictName              = $"{EdOrgService.GetEdOrgById(newReportSummary.EdOrgId, newReportSummary.SchoolYear.Id).OrganizationName} ({newReportSummary.EdOrgId.ToString()})",
                        ValidationReportSummaryId = newReportSummary.ValidationReportSummaryId
                    };
                    validationDbContext.ValidationReportDetails.Add(newReportDetails);
                    try
                    {
                        validationDbContext.SaveChanges();
                    }
                    catch (Exception ex)
                    {
                        LoggingService.LogErrorMessage(ex.ChainInnerExceptionMessages());
                    }

                    LoggingService.LogDebugMessage(
                        $"Successfully added additional Validation Report details to the Validation Portal database for EdOrgID {newReportSummary.EdOrgId}.");

                    // Execute each individual rule.
                    List <RulesEngineExecutionException> rulesEngineExecutionExceptions = new List <RulesEngineExecutionException>();

                    for (var i = 0; i < rules.Length; i++)
                    {
                        var rule = rules[i];

                        try
                        {
                            //Execute the SQL files in here? We have the RuleSetName and the Rule Id
                            //e.g.  RuleSetId = MultipleEnrollment
                            //      RuleId = 10.10.6175
                            var toExecute = ManualRuleExecutionService.GetManualSqlFile(rule.RulesetId, rule.RuleId).Result;

                            // By default, rules are run against ALL districts in the Ed Fi ODS. This line filters for multi-district/multi-tenant ODS's.
                            rule.AddDistrictWhereFilter(newReportSummary.EdOrgId);

                            LoggingService.LogDebugMessage($"Executing Rule {rule.RuleId}.");

                            var detailParams = new List <SqlParameter>
                            {
                                new SqlParameter(
                                    "@RuleValidationId",
                                    newRuleValidationExecution.RuleValidationId)
                            };

                            detailParams.AddRange(
                                EngineObjectModel.GetParameters(collectionId)
                                .Select(x => new SqlParameter(x.ParameterName, x.Value)));

                            odsRawDbContext.Database.CommandTimeout = EngineConfig.RulesExecutionTimeout;

                            if (toExecute.Count > 0)
                            {
                                foreach (var sql in toExecute)
                                {
                                    detailParams.Add(
                                        new SqlParameter(
                                            "@DistrictId",
                                            newReportSummary.EdOrgId));

                                    LoggingService.LogDebugMessage($"Executing Rule SQL {sql}.");
                                    var resultManualSql = odsRawDbContext.Database.ExecuteSqlCommand(sql, detailParams.ToArray <object>());
                                    LoggingService.LogDebugMessage($"Executing Rule {rule.RuleId} rows affected = {resultManualSql}.");
                                }
                            }
                            else
                            {
                                if (EngineConfig.SaveGeneratedRulesSqlToFiles && !string.IsNullOrWhiteSpace(savedSqlPath))
                                {
                                    var rulesetPath = Path.Combine(savedSqlPath, rule.RulesetId);
                                    if (!Directory.Exists(rulesetPath))
                                    {
                                        Directory.CreateDirectory(rulesetPath);
                                    }

                                    var rulePath = Path.Combine(rulesetPath, rule.RuleId + ".sql");
                                    File.WriteAllText(rulePath, rule.Sql);
                                }

                                LoggingService.LogDebugMessage($"Executing Rule SQL {rule.ExecSql}.");
                                var result = odsRawDbContext.Database.ExecuteSqlCommand(rule.ExecSql, detailParams.ToArray <object>());
                                LoggingService.LogDebugMessage($"Executing Rule {rule.RuleId} rows affected = {result}.");
                            }

                            // Record the results of this rule in the Validation Portal database, accompanied by more detailed information.
                            PopulateErrorDetailsFromViews(
                                rule,
                                odsRawDbContext,
                                newRuleValidationExecution.RuleValidationId,
                                newReportDetails.Id);

                            newReportSummary.Status = $"In Progress - {(int)((float)i / rules.Length * 100)}% complete";
                            validationDbContext.SaveChanges();
                        }
                        catch (Exception ex)
                        {
                            rulesEngineExecutionExceptions.Add(
                                new RulesEngineExecutionException
                            {
                                RuleId         = rule.RuleId,
                                Sql            = rule.Sql,
                                ExecSql        = rule.ExecSql,
                                DataSourceName =
                                    $"Database Server: {odsRawDbContext.Database.Connection.DataSource}{Environment.NewLine} " +
                                    $"Database: {odsRawDbContext.Database.Connection.Database}",
                                ChainedErrorMessages = ex.ChainInnerExceptionMessages()
                            });
                        }
                    }

                    LoggingService.LogDebugMessage("Counting errors and warnings.");
                    newReportSummary.CompletedWhen = DateTime.UtcNow;

                    newReportSummary.ErrorCount = odsRawDbContext.RuleValidationDetails.Count(
                        rvd => rvd.RuleValidation.RuleValidationId == newRuleValidationExecution.RuleValidationId &&
                        rvd.IsError);

                    newReportSummary.WarningCount = odsRawDbContext.RuleValidationDetails.Count(
                        rvd => rvd.RuleValidation.RuleValidationId == newRuleValidationExecution.RuleValidationId &&
                        !rvd.IsError);

                    var hasExecutionErrors = rulesEngineExecutionExceptions.Count > 0;
                    newReportSummary.Status = hasExecutionErrors
                                                  ? $"Completed - {rulesEngineExecutionExceptions.Count} rules did not execute, ask an administrator to check the log for errors, Report Summary Number {newReportSummary.ValidationReportSummaryId.ToString()}"
                                                  : "Completed";
                    LoggingService.LogDebugMessage($"Saving status {newReportSummary.Status}.");

                    // Log Execution Errors
                    LoggingService.LogErrorMessage(
                        GetLogExecutionErrorsMessage(rulesEngineExecutionExceptions, newReportSummary.ValidationReportSummaryId));

                    newReportDetails.CompletedWhen = newReportDetails.CompletedWhen ?? DateTime.UtcNow;
                    validationDbContext.SaveChanges();
                    LoggingService.LogDebugMessage("Saved status.");
                }
            }
        }
Beispiel #16
0
        public void RunValidation(SubmissionCycle submissionCycle, long ruleValidationId)
        {
            var    schoolYear         = _schoolYearService.GetSchoolYearById(submissionCycle.SchoolYearId.Value);
            string fourDigitOdsDbYear = schoolYear.EndYear;

            // todo: dependency inject the initialization of RawOdsDbContext with a year. introduce a factory for most simple implementation
            using (var odsRawDbContext = new RawOdsDbContext(fourDigitOdsDbYear))
            {
                using (var validationDbContext = DbContextFactory.Create())
                {
                    var newReportSummary = validationDbContext
                                           .ValidationReportSummaries
                                           .Include(x => x.SchoolYear)
                                           .FirstOrDefault(x =>
                                                           x.ValidationReportSummaryId == ruleValidationId &&
                                                           x.SchoolYearId == schoolYear.Id);

                    var newRuleValidationExecution =
                        odsRawDbContext.RuleValidations.FirstOrDefault(x => x.RuleValidationId == newReportSummary.RuleValidationId);

                    var collectionId = newRuleValidationExecution.CollectionId;

                    #region Now, store each Ruleset ID and Rule ID that the engine will run. Save it in the Engine database.

                    _loggingService.LogDebugMessage($"Getting the rules to run for the chosen collection {collectionId}.");
                    var rules          = _engineObjectModel.GetRules(collectionId).ToArray();
                    var ruleComponents = rules.SelectMany(
                        r => r.Components.Distinct().Select(
                            c => new
                    {
                        r.RulesetId,
                        r.RuleId,
                        Component = c
                    }));

                    foreach (var singleRuleNeedingToBeValidated in ruleComponents)
                    {
                        odsRawDbContext.RuleValidationRuleComponents.Add(
                            new RuleValidationRuleComponent
                        {
                            RuleValidationId = newRuleValidationExecution.RuleValidationId,
                            RulesetId        = singleRuleNeedingToBeValidated.RulesetId,
                            RuleId           = singleRuleNeedingToBeValidated.RuleId,
                            Component        = singleRuleNeedingToBeValidated.Component
                        });
                    }

                    odsRawDbContext.SaveChanges();
                    _loggingService.LogDebugMessage($"Saved the rules to run for the chosen collection {collectionId}.");

                    #endregion Now, store each Ruleset ID and Rule ID that the engine will run. Save it in the Engine database.

                    #region The ValidationReportDetails is one-for-one with the ValidationReportSummary - it should be refactored away. It contains the error/warning details.

                    _loggingService.LogDebugMessage(
                        $"Adding additional Validation Report details to the Validation Portal database for EdOrgID {newReportSummary.EdOrgId}.");

                    var newReportDetails = new ValidationReportDetails
                    {
                        CollectionName            = collectionId,
                        SchoolYearId              = newReportSummary.SchoolYear.Id,
                        DistrictName              = $"{_edOrgService.GetEdOrgById(newReportSummary.EdOrgId, newReportSummary.SchoolYear.Id).OrganizationName} ({newReportSummary.EdOrgId.ToString()})",
                        ValidationReportSummaryId = newReportSummary.ValidationReportSummaryId
                    };
                    validationDbContext.ValidationReportDetails.Add(newReportDetails);
                    try
                    {
                        validationDbContext.SaveChanges();
                    }
                    catch (Exception ex)
                    {
                        _loggingService.LogErrorMessage(ex.ChainInnerExceptionMessages());
                    }

                    _loggingService.LogDebugMessage(
                        $"Successfully added additional Validation Report details to the Validation Portal database for EdOrgID {newReportSummary.EdOrgId}.");

                    #endregion The ValidationReportDetails is one-for-one with the ValidationReportSummary - it should be refactored away. It contains the error/warning details.

                    #region Execute each individual rule.

                    List <RulesEngineExecutionException> rulesEngineExecutionExceptions =
                        new List <RulesEngineExecutionException>();

                    for (var i = 0; i < rules.Length; i++)
                    {
                        var rule = rules[i];

                        try
                        {
                            // By default, rules are run against ALL districts in the Ed Fi ODS. This line filters for multi-district/multi-tenant ODS's.
                            // rule.AddDistrictWhereFilter(newReportSummary.EdOrgId);

                            _loggingService.LogDebugMessage($"Executing Rule {rule.RuleId}.");
                            _loggingService.LogDebugMessage($"Executing Rule SQL {rule.Sql}.");

                            var detailParams = new List <SqlParameter>
                            {
                                new SqlParameter(
                                    "@RuleValidationId",
                                    newRuleValidationExecution.RuleValidationId)
                            };
                            detailParams.AddRange(
                                _engineObjectModel.GetParameters(collectionId)
                                .Select(x => new SqlParameter(x.ParameterName, x.Value)));
                            odsRawDbContext.Database.CommandTimeout = 60;

                            var result = odsRawDbContext.Database.ExecuteSqlCommand(rule.ExecSql, detailParams.ToArray());
                            _loggingService.LogDebugMessage($"Executing Rule {rule.RuleId} rows affected = {result}.");

                            #region Record the results of this rule in the Validation Portal database, accompanied by more detailed information.

                            PopulateErrorDetailsFromViews(
                                rule,
                                odsRawDbContext,
                                newRuleValidationExecution.RuleValidationId,
                                newReportDetails.Id);

                            newReportSummary.Status = $"In Progress - {(int)((float)i / rules.Length * 100)}% complete";
                            validationDbContext.SaveChanges();

                            #endregion Record the results of this rule in the Validation Portal database, accompanied by more detailed information.
                        }
                        catch (Exception ex)
                        {
                            rulesEngineExecutionExceptions.Add(
                                new RulesEngineExecutionException
                            {
                                RuleId         = rule.RuleId,
                                Sql            = rule.Sql,
                                ExecSql        = rule.ExecSql,
                                DataSourceName =
                                    $"Database Server: {odsRawDbContext.Database.Connection.DataSource}{Environment.NewLine} Database: {odsRawDbContext.Database.Connection.Database}",
                                ChainedErrorMessages = ex.ChainInnerExceptionMessages()
                            });
                        }
                    }

                    #endregion Execute each individual rule.

                    _loggingService.LogDebugMessage($"Counting errors and warnings.");
                    newReportSummary.CompletedWhen = DateTime.UtcNow;

                    newReportSummary.ErrorCount = odsRawDbContext.RuleValidationDetails.Count(
                        rvd => rvd.RuleValidation.RuleValidationId == newRuleValidationExecution.RuleValidationId &&
                        rvd.IsError);

                    newReportSummary.WarningCount = odsRawDbContext.RuleValidationDetails.Count(
                        rvd => rvd.RuleValidation.RuleValidationId == newRuleValidationExecution.RuleValidationId &&
                        !rvd.IsError);

                    var hasExecutionErrors = rulesEngineExecutionExceptions.Count > 0;
                    newReportSummary.Status = hasExecutionErrors
                                                  ? $"Completed - {rulesEngineExecutionExceptions.Count} rules did not execute, ask an administrator to check the log for errors, Report Summary Number {newReportSummary.ValidationReportSummaryId.ToString()}"
                                                  : "Completed";
                    _loggingService.LogDebugMessage($"Saving status {newReportSummary.Status}.");

                    // Log Execution Errors
                    _loggingService.LogErrorMessage(
                        GetLogExecutionErrorsMessage(rulesEngineExecutionExceptions, newReportSummary.ValidationReportSummaryId));

                    newReportDetails.CompletedWhen = newReportDetails.CompletedWhen ?? DateTime.UtcNow;
                    validationDbContext.SaveChanges();
                    _loggingService.LogDebugMessage($"Saved status.");
                }
            }
        }