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