public DeferredEval( IOperatorEvalStrategyFactory operatorEvalStrategyFactory, RulesEngineOptions rulesEngineOptions) { this.operatorEvalStrategyFactory = operatorEvalStrategyFactory; this.rulesEngineOptions = rulesEngineOptions; }
private void ApplyDefaultDeletionRule(RulesEngineOptions context, StudyEntry study) { if (!context.ApplyDeleteActions) { return; } // TODO (CR Jun 2012): Again, seem to use "work item" mutex for all database updates. Should just pass in a boolean. using (var dac = new DataAccessContext(DataAccessContext.WorkItemMutex)) { var broker = dac.GetStudyBroker(); var dbStudy = broker.GetStudy(study.Study.StudyInstanceUid); var storageConfiguration = StudyStore.GetConfiguration(); var defaultRule = storageConfiguration.DefaultDeletionRule; if (defaultRule.Enabled) { dbStudy.SetDeleteTime(defaultRule.TimeValue, defaultRule.TimeUnit, TimeOrigin.ReceivedDate, false); } else { dbStudy.ClearDeleteTime(); } dac.Commit(); } }
public void EnsureValid_GivenOptionsNullReference_ThrowsInvalidRulesEngineOptionsExceptionClaimingNullOptions() { // Arrange RulesEngineOptions rulesEngineOptions = null; InvalidRulesEngineOptionsException actual = Assert.Throws <InvalidRulesEngineOptionsException>(() => { // Act RulesEngineOptionsValidator.EnsureValid(rulesEngineOptions); }); actual.Message.Should().Be("Specified null rulesEngineOptions."); }
public void EnsureValid_GivenOptionsWithInvalidDefaultForDataType_ThrowsInvalidRulesEngineOptionsExceptionClaimingInvalidDefault(DataTypes dataType, object defaultValue) { // Arrange RulesEngineOptions rulesEngineOptions = RulesEngineOptions.NewWithDefaults(); rulesEngineOptions.DataTypeDefaults[dataType] = defaultValue; InvalidRulesEngineOptionsException actual = Assert.Throws <InvalidRulesEngineOptionsException>(() => { // Act RulesEngineOptionsValidator.EnsureValid(rulesEngineOptions); }); actual.Message.Should().Be($"Specified invalid default value for data type {dataType}: {defaultValue ?? "null"}."); }
private void ProcessStudiesInDatabase() { var rulesEngine = RulesEngine.Create(); foreach (var oid in StudyOidList) { try { // TODO (CR Jun 2012): We don't modify any work items - do we need the mutex? using (var context = new DataAccessContext(DataAccessContext.WorkItemMutex)) { var broker = context.GetStudyBroker(); var study = broker.GetStudy(oid); var studyEntry = study.ToStoreEntry(); var rulesEngineOptions = new RulesEngineOptions { ApplyDeleteActions = _request.ApplyDeleteActions, ApplyRouteActions = _request.ApplyRouteActions }; if (!string.IsNullOrEmpty(_request.RuleId)) { rulesEngine.ApplyStudyRule(studyEntry, _request.RuleId, rulesEngineOptions); } else { rulesEngine.ApplyStudyRules(studyEntry, rulesEngineOptions); } EventsHelper.Fire(_studyProcessedEvent, this, new StudyEventArgs { StudyInstanceUid = study.StudyInstanceUid }); } } catch (Exception x) { Platform.Log(LogLevel.Warn, "Unexpected exception attempting to reapply rules for StudyOid {0}: {1}", oid, x.Message); } } }
public void GetDeferredEvalFor_GivenStringConditionNodeWithNoConditionSuppliedAndRulesEngineConfiguredToUseDataTypeDefaultWhenMissing_ReturnsFuncThatEvalsFalse() { // Arrange StringConditionNode <ConditionType> conditionNode = new StringConditionNode <ConditionType>(ConditionType.IsoCurrency, Operators.Equal, "EUR"); Mock <IOperatorEvalStrategy> mockOperatorEvalStrategy = new Mock <IOperatorEvalStrategy>(); mockOperatorEvalStrategy.Setup(x => x.Eval(It.IsAny <string>(), It.IsAny <string>())) .Returns(false); Mock <IOperatorEvalStrategyFactory> mockOperatorEvalStrategyFactory = new Mock <IOperatorEvalStrategyFactory>(); mockOperatorEvalStrategyFactory.Setup(x => x.GetOperatorEvalStrategy(It.IsAny <Operators>())) .Returns(mockOperatorEvalStrategy.Object); IEnumerable <Condition <ConditionType> > conditions = new Condition <ConditionType>[] { new Condition <ConditionType> { Type = ConditionType.IsoCountryCode, Value = "PT" } }; MatchModes matchMode = MatchModes.Exact; RulesEngineOptions rulesEngineOptions = RulesEngineOptions.NewWithDefaults(); rulesEngineOptions.MissingConditionBehavior = MissingConditionBehaviors.UseDataTypeDefault; DeferredEval sut = new DeferredEval(mockOperatorEvalStrategyFactory.Object, rulesEngineOptions); // Act Func <IEnumerable <Condition <ConditionType> >, bool> actual = sut.GetDeferredEvalFor(conditionNode, matchMode); bool actualEvalResult = actual.Invoke(conditions); // Assert actualEvalResult.Should().BeFalse(); mockOperatorEvalStrategyFactory.Verify(x => x.GetOperatorEvalStrategy(It.IsAny <Operators>()), Times.Once()); mockOperatorEvalStrategy.Verify(x => x.Eval(It.IsAny <string>(), It.IsAny <string>()), Times.Once()); }
public void GetDeferredEvalFor_GivenUnknownConditionNodeType_ThrowsNotSupportedException() { // Arrange Mock <IValueConditionNode <ConditionType> > mockValueConditionNode = new Mock <IValueConditionNode <ConditionType> >(); Mock <IOperatorEvalStrategyFactory> mockOperatorEvalStrategyFactory = new Mock <IOperatorEvalStrategyFactory>(); MatchModes matchMode = MatchModes.Exact; RulesEngineOptions rulesEngineOptions = RulesEngineOptions.NewWithDefaults(); DeferredEval sut = new DeferredEval(mockOperatorEvalStrategyFactory.Object, rulesEngineOptions); // Act NotSupportedException notSupportedException = Assert.Throws <NotSupportedException>(() => sut.GetDeferredEvalFor(mockValueConditionNode.Object, matchMode)); // Assert notSupportedException.Should().NotBeNull(); notSupportedException.Message.Should().Be($"Unsupported value condition node: '{mockValueConditionNode.Object.GetType().Name}'."); }
/// <summary> /// Reapply specified rule. /// </summary> /// <param name="ruleId"></param> /// <param name="ruleName"></param> /// <param name="context"></param> public void Reapply(string ruleId, string ruleName, RulesEngineOptions context) { var request = new ReapplyRulesRequest { RuleId = ruleId, RuleName = ruleName, ApplyDeleteActions = context.ApplyDeleteActions, ApplyRouteActions = context.ApplyRouteActions }; try { InsertRequest(request, new ReapplyRulesProgress()); } catch (Exception ex) { Exception = ex; Platform.Log(LogLevel.Error, ex, SR.MessageFailedToStartReapplyRules); throw; } }
public void GetDeferredEvalFor_GivenDecimalConditionNode_ReturnsFuncToEvalConditionsCollection() { // Arrange DecimalConditionNode <ConditionType> conditionNode = new DecimalConditionNode <ConditionType>(ConditionType.PluviosityRate, Operators.GreaterThan, 50); Mock <IOperatorEvalStrategy> mockOperatorEvalStrategy = new Mock <IOperatorEvalStrategy>(); mockOperatorEvalStrategy.Setup(x => x.Eval(It.IsAny <decimal>(), It.IsAny <decimal>())) .Returns(true); Mock <IOperatorEvalStrategyFactory> mockOperatorEvalStrategyFactory = new Mock <IOperatorEvalStrategyFactory>(); mockOperatorEvalStrategyFactory.Setup(x => x.GetOperatorEvalStrategy(It.IsAny <Operators>())) .Returns(mockOperatorEvalStrategy.Object); IEnumerable <Condition <ConditionType> > conditions = new Condition <ConditionType>[] { new Condition <ConditionType> { Type = ConditionType.PluviosityRate, Value = 78 } }; MatchModes matchMode = MatchModes.Exact; RulesEngineOptions rulesEngineOptions = RulesEngineOptions.NewWithDefaults(); DeferredEval sut = new DeferredEval(mockOperatorEvalStrategyFactory.Object, rulesEngineOptions); // Act Func <IEnumerable <Condition <ConditionType> >, bool> actual = sut.GetDeferredEvalFor(conditionNode, matchMode); bool actualEvalResult = actual.Invoke(conditions); // Assert actualEvalResult.Should().BeTrue(); mockOperatorEvalStrategyFactory.Verify(x => x.GetOperatorEvalStrategy(It.IsAny <Operators>()), Times.Once()); mockOperatorEvalStrategy.Verify(x => x.Eval(It.IsAny <decimal>(), It.IsAny <decimal>()), Times.Once()); }
public static void EnsureValid(RulesEngineOptions rulesEngineOptions) { if (rulesEngineOptions == null) { throw new InvalidRulesEngineOptionsException($"Specified null {nameof(rulesEngineOptions)}."); } EnsureValidDataTypeDefault( rulesEngineOptions.DataTypeDefaults, DataTypes.Boolean, value => value != null && bool.TryParse(value.ToString(), out bool boolRes)); EnsureValidDataTypeDefault( rulesEngineOptions.DataTypeDefaults, DataTypes.Decimal, value => value != null && decimal.TryParse(value.ToString(), out decimal decimalRes)); EnsureValidDataTypeDefault( rulesEngineOptions.DataTypeDefaults, DataTypes.Integer, value => value != null && int.TryParse(value.ToString(), out int intRes)); EnsureValidDataTypeDefault( rulesEngineOptions.DataTypeDefaults, DataTypes.String, value => value is string); }
public ConfiguredRulesEngineBuilder(IRulesDataSource <TContentType, TConditionType> rulesDataSource) { this.rulesDataSource = rulesDataSource; this.rulesEngineOptions = RulesEngineOptions.NewWithDefaults(); }
/// <summary> /// Reapply all rules. /// </summary> /// <param name="context"></param> public void ReapplyAll(RulesEngineOptions context) { Reapply(null, null, context); }
private void ReprocessFolder() { try { var studyXml = Location.LoadStudyXml(); var fileList = new List <ProcessStudyUtility.ProcessorFile>(); // This code will cleanup a folder and move images around to the proper location. // It in essence allows you to just copy a bunch of files into the filestore, and reindex will clean them up and organize them. FileProcessor.Process(Location.StudyFolder, "*.dcm", delegate(string file, out bool cancel) { cancel = _cancelRequested; try { var dicomFile = new DicomFile(file); dicomFile.Load(DicomReadOptions.StorePixelDataReferences | DicomReadOptions.Default); String studyInstanceUid = dicomFile.DataSet[DicomTags.StudyInstanceUid].GetString(0, string.Empty); if (!Location.Study.StudyInstanceUid.Equals(studyInstanceUid)) { Platform.Log(LogLevel.Warn, "Importing file that was in the wrong study folder: {0}", file); var context = new ImportStudyContext(dicomFile.SourceApplicationEntityTitle, StudyStore.GetConfiguration(), EventSource.CurrentProcess); var importer = new ImportFilesUtility(context); var result = importer.Import(dicomFile, BadFileBehaviourEnum.Delete, FileImportBehaviourEnum.Move); if (!result.DicomStatus.Equals(DicomStatuses.Success)) { try { Platform.Log(LogLevel.Error, "Unable to import file: {0}, deleting: {1}", result.ErrorMessage, file); FileUtils.Delete(file); } catch (Exception x) { Platform.Log(LogLevel.Warn, x, "Unexpected exception deleting file: {0}", file); Failed = true; FailureMessage = x.Message; } } } else { fileList.Add(new ProcessStudyUtility.ProcessorFile(dicomFile, null)); if (fileList.Count > 19) { var p = new ProcessStudyUtility(Location) { IsReprocess = true }; p.ProcessBatch(fileList, studyXml); fileList.Clear(); } } } catch (Exception x) { Platform.Log(LogLevel.Error, "Exception when reindexing {0} files, last file: {1}: {2}", fileList.Count, file, x.Message); fileList.Clear(); // Clear out the failed entries Failed = true; FailureMessage = x.Message; } }, true); if (fileList.Count > 0) { var p = new ProcessStudyUtility(Location) { IsReprocess = true }; p.ProcessBatch(fileList, studyXml); // Now apply Deletion rules var ruleContext = new RulesEngineOptions { ApplyDeleteActions = true, ApplyRouteActions = false }; RulesEngine.Create().ApplyStudyRules(p.StudyLocation.Study.ToStoreEntry(), ruleContext); } } catch (Exception x) { Platform.Log(LogLevel.Error, x, "Unexpected exception reindexing folder: {0}", Location.StudyFolder); Failed = true; FailureMessage = x.Message; } if (_cancelRequested) { Platform.Log(LogLevel.Info, "Cancel requested while reprocessing folder: {0}", Location.StudyFolder); } else { Platform.Log(LogLevel.Info, "Completed reprocessing study folder: {0}", Location.Study.StudyInstanceUid); } }
public async Task MatchManyAsync_GivenContentTypeDateAndConditions_FetchesRulesForDayEvalsAndReturnsAllMatches() { // Arrange DateTime matchDateTime = new DateTime(2018, 07, 01, 18, 19, 30); ContentType contentType = ContentType.Type1; IEnumerable <Condition <ConditionType> > conditions = new[] { new Condition <ConditionType> { Type = ConditionType.IsoCountryCode, Value = "USA" }, new Condition <ConditionType> { Type = ConditionType.IsoCurrency, Value = "USD" } }; Rule <ContentType, ConditionType> expected1 = new Rule <ContentType, ConditionType> { ContentContainer = new ContentContainer <ContentType>(contentType, (t) => new object()), DateBegin = new DateTime(2018, 01, 01), DateEnd = new DateTime(2019, 01, 01), Name = "Expected rule 1", Priority = 3, RootCondition = new StringConditionNode <ConditionType>(ConditionType.IsoCountryCode, Operators.Equal, "USA") }; Rule <ContentType, ConditionType> expected2 = new Rule <ContentType, ConditionType> { ContentContainer = new ContentContainer <ContentType>(contentType, (t) => new object()), DateBegin = new DateTime(2010, 01, 01), DateEnd = new DateTime(2021, 01, 01), Name = "Expected rule 2", Priority = 200, RootCondition = new StringConditionNode <ConditionType>(ConditionType.IsoCountryCode, Operators.Equal, "USA") }; Rule <ContentType, ConditionType> notExpected = new Rule <ContentType, ConditionType> { ContentContainer = new ContentContainer <ContentType>(contentType, (t) => new object()), DateBegin = new DateTime(2018, 01, 01), DateEnd = new DateTime(2019, 01, 01), Name = "Not expected rule", Priority = 1, // Topmost rule, should be the one that wins if options are set to topmost wins. RootCondition = new StringConditionNode <ConditionType>(ConditionType.IsoCountryCode, Operators.Equal, "CHE") }; IEnumerable <Rule <ContentType, ConditionType> > rules = new[] { expected1, expected2, notExpected }; EvaluationOptions evaluationOptions = new EvaluationOptions { MatchMode = MatchModes.Exact }; Mock <IRulesDataSource <ContentType, ConditionType> > mockRulesDataSource = SetupMockForRulesDataSource(rules); Mock <IConditionsEvalEngine <ConditionType> > mockConditionsEvalEngine = SetupMockForConditionsEvalEngine((rootConditionNode, inputConditions, evalOptions) => { switch (rootConditionNode) { case StringConditionNode <ConditionType> stringConditionNode: return(stringConditionNode.Operand == "USA"); default: return(false); } }, evaluationOptions); RulesEngineOptions rulesEngineOptions = RulesEngineOptions.NewWithDefaults(); RulesEngine <ContentType, ConditionType> sut = new RulesEngine <ContentType, ConditionType>(mockConditionsEvalEngine.Object, mockRulesDataSource.Object, rulesEngineOptions); // Act IEnumerable <Rule <ContentType, ConditionType> > actual = await sut.MatchManyAsync(contentType, matchDateTime, conditions); // Assert actual.Should().Contain(expected1) .And.Contain(expected2) .And.NotContain(notExpected); mockRulesDataSource.Verify(x => x.GetRulesAsync(It.IsAny <ContentType>(), It.IsAny <DateTime>(), It.IsAny <DateTime>()), Times.Once()); mockConditionsEvalEngine.Verify(x => x.Eval( It.IsAny <IConditionNode <ConditionType> >(), It.IsAny <IEnumerable <Condition <ConditionType> > >(), It.Is <EvaluationOptions>(eo => eo == evaluationOptions)), Times.AtLeastOnce()); }
/// <summary> /// Reapply all rules. /// </summary> /// <param name="context"></param> public void ReapplyAll(RulesEngineOptions context) { Reapply(null, null, context); }
/// <summary> /// Reapply specified rule. /// </summary> /// <param name="ruleId"></param> /// <param name="ruleName"></param> /// <param name="context"></param> public void Reapply(string ruleId, string ruleName, RulesEngineOptions context) { var request = new ReapplyRulesRequest { RuleId = ruleId, RuleName = ruleName, ApplyDeleteActions = context.ApplyDeleteActions, ApplyRouteActions = context.ApplyRouteActions }; try { InsertRequest(request, new ReapplyRulesProgress()); } catch (Exception ex) { Exception = ex; Platform.Log(LogLevel.Error, ex, SR.MessageFailedToStartReapplyRules); throw; } }
public async Task MatchOneAsync_GivenContentTypeDateAndConditions_FetchesRulesForDayFailsEvalsAndReturnsNull() { // Arrange DateTime matchDateTime = new DateTime(2018, 07, 01, 18, 19, 30); ContentType contentType = ContentType.Type1; IEnumerable <Condition <ConditionType> > conditions = new[] { new Condition <ConditionType> { Type = ConditionType.IsoCountryCode, Value = "BRZ" }, new Condition <ConditionType> { Type = ConditionType.IsoCurrency, Value = "USD" } }; IEnumerable <Rule <ContentType, ConditionType> > rules = new[] { new Rule <ContentType, ConditionType> { ContentContainer = new ContentContainer <ContentType>(contentType, (t) => new object()), DateBegin = new DateTime(2018, 01, 01), DateEnd = new DateTime(2019, 01, 01), Name = "Expected rule", Priority = 3, RootCondition = new StringConditionNode <ConditionType>(ConditionType.IsoCountryCode, Operators.Equal, "USA") }, new Rule <ContentType, ConditionType> { ContentContainer = new ContentContainer <ContentType>(contentType, (t) => new object()), DateBegin = new DateTime(2010, 01, 01), DateEnd = new DateTime(2021, 01, 01), Name = "Expected rule", Priority = 200, RootCondition = new StringConditionNode <ConditionType>(ConditionType.IsoCountryCode, Operators.Equal, "USA") } }; EvaluationOptions evaluationOptions = new EvaluationOptions { MatchMode = MatchModes.Exact }; Mock <IRulesDataSource <ContentType, ConditionType> > mockRulesDataSource = SetupMockForRulesDataSource(rules); Mock <IConditionsEvalEngine <ConditionType> > mockConditionsEvalEngine = SetupMockForConditionsEvalEngine(false, evaluationOptions); RulesEngineOptions rulesEngineOptions = RulesEngineOptions.NewWithDefaults(); RulesEngine <ContentType, ConditionType> sut = new RulesEngine <ContentType, ConditionType>(mockConditionsEvalEngine.Object, mockRulesDataSource.Object, rulesEngineOptions); // Act Rule <ContentType, ConditionType> actual = await sut.MatchOneAsync(contentType, matchDateTime, conditions); // Assert actual.Should().BeNull(); mockRulesDataSource.Verify(x => x.GetRulesAsync(It.IsAny <ContentType>(), It.IsAny <DateTime>(), It.IsAny <DateTime>()), Times.Once()); mockConditionsEvalEngine.Verify(x => x.Eval( It.IsAny <IConditionNode <ConditionType> >(), It.IsAny <IEnumerable <Condition <ConditionType> > >(), It.Is <EvaluationOptions>(eo => eo == evaluationOptions)), Times.AtLeastOnce()); }
public void ApplyStudyRule(StudyEntry study, string ruleId, RulesEngineOptions options) { ApplyDefaultDeletionRule(options, study); }
/// <summary> /// Main Processing routine. /// </summary> public override void Process() { bool stillProcessing = ProcessUidList(); if (CancelPending) { Proxy.Cancel(); return; } if (StopPending) { Proxy.Idle(); return; } if (!stillProcessing) { bool failed = false; bool complete = true; bool filesMissing = false; foreach (WorkItemUid sop in WorkQueueUidList) { if (sop.Failed) { //If any items failed simply because the file doesn't exist, then fail outright. if (!File.Exists(GetFilePath(sop))) { filesMissing = true; } failed = true; break; } if (!sop.Complete) { complete = false; break; } } DateTime now = Platform.Time; if (failed) { var failureType = filesMissing ? WorkItemFailureType.Fatal : WorkItemFailureType.NonFatal; Proxy.Fail(failureType); if (Proxy.Item.Status == WorkItemStatusEnum.Failed) { var auditedInstances = new AuditedInstances(); auditedInstances.AddInstance(Request.Patient.PatientId, Request.Patient.PatientsName, Request.Study.StudyInstanceUid); AuditHelper.LogImportStudies(auditedInstances, string.IsNullOrEmpty(Request.UserName) ? EventSource.CurrentProcess : EventSource.GetUserEventSource(Request.UserName), EventResult.MajorFailure); } } else if (!complete) { Proxy.Idle(); } else if (now > Proxy.Item.ExpirationTime) { if (Study == null) { Study = LoadRelatedStudy(); } var ruleOptions = new RulesEngineOptions { ApplyDeleteActions = true, ApplyRouteActions = true }; RulesEngine.Create().ApplyStudyRules(Study.ToStoreEntry(), ruleOptions); Proxy.Complete(); var auditedInstances = new AuditedInstances(); auditedInstances.AddInstance(Request.Patient.PatientId, Request.Patient.PatientsName, Request.Study.StudyInstanceUid); AuditHelper.LogImportStudies(auditedInstances, string.IsNullOrEmpty(Request.UserName) ? EventSource.CurrentProcess : EventSource.GetUserEventSource(Request.UserName), EventResult.Success); } else { Proxy.Idle(); } } else { Proxy.Idle(); } }