Exemplo n.º 1
0
        public ValidationReportSummary SetupValidationRun(SubmissionCycle submissionCycle, string collectionId)
        {
            var    schoolYear         = _schoolYearService.GetSchoolYearById(submissionCycle.SchoolYearId.Value);
            string fourDigitOdsDbYear = schoolYear.EndYear;

            ValidationReportSummary newReportSummary = null;

            using (var odsRawDbContext = new RawOdsDbContext(fourDigitOdsDbYear))
            {
                using (var validationDbContext = DbContextFactory.Create())
                {
                    _loggingService.LogDebugMessage(
                        $"Connecting to the Ed Fi ODS {fourDigitOdsDbYear} to run the Rules Engine. Submitting the RulesValidation run ID.");

                    // Run the rules - This code is adapted from an example in the Rule Engine project.

                    #region 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.");

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

                    #region 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"
                    };

                    validationDbContext.ValidationReportSummaries.Add(newReportSummary);
                    validationDbContext.SaveChanges();
                    _loggingService.LogDebugMessage(
                        $"Successfully submitted Validation Report Summary ID {newReportSummary.ValidationReportSummaryId} to the Validation Portal database for Rules Validation Run {newRuleValidationExecution.RuleValidationId.ToString()}.");

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

            return(newReportSummary);
        }
Exemplo n.º 2
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.");
                }
            }
        }