##// END OF EJS Templates
rule engine: add support for NOT rule
ergo -
Show More
@@ -29,6 +29,10 b' To run celery queue processing:'
29 29
30 30 celery worker -A appenlight.celery -Q "reports,logs,metrics,default" --ini=development.ini
31 31
32 To run celery beats scheduling:
33
34 celery beat -A appenlight.celery --ini=development.ini
35
32 36 You should also run the channelstream websocket server for real-time notifications
33 37
34 38 channelstream -i filename.ini
@@ -188,6 +188,10 b' class Rule(RuleBase):'
188 188 rule = OR(self.config['rules'], self.type_matrix,
189 189 config_manipulator=self.config_manipulator)
190 190 return rule.match(struct)
191 elif field_name == '__NOT__':
192 rule = NOT(self.config['rules'], self.type_matrix,
193 config_manipulator=self.config_manipulator)
194 return rule.match(struct)
191 195
192 196 if test_value is None:
193 197 return False
@@ -236,6 +240,16 b' class AND(Rule):'
236 240 in self.rules])
237 241
238 242
243 class NOT(Rule):
244 def __init__(self, rules, *args, **kwargs):
245 super(NOT, self).__init__({}, *args, **kwargs)
246 self.rules = rules
247
248 def match(self, struct):
249 return all([not self.subrule_check(r_conf, struct) for r_conf
250 in self.rules])
251
252
239 253 class OR(Rule):
240 254 def __init__(self, rules, *args, **kwargs):
241 255 super(OR, self).__init__({}, *args, **kwargs)
@@ -266,7 +280,8 b' class RuleService(object):'
266 280 if manipulator_func is None:
267 281 def label_rewriter_func(rule):
268 282 field = rule.config.get('field')
269 if not field or rule.config['field'] in ['__OR__', '__AND__']:
283 if not field or rule.config['field'] in ['__OR__',
284 '__AND__', '__NOT__']:
270 285 return
271 286
272 287 to_map = field_mappings.get(rule.config['field'])
@@ -5376,11 +5376,11 b' function kickstartAE() {'
5376 5376 " {{rule_ctrlr.readOnlyPossibleFields[rule_ctrlr.rule.field]}}\n" +
5377 5377 " </span>\n" +
5378 5378 "\n" +
5379 " <span ng-if=\"rule_ctrlr.rule.field != '__AND__' && rule_ctrlr.rule.field !='__OR__'\">\n" +
5379 " <span ng-if=\"rule_ctrlr.rule.field != '__AND__' && rule_ctrlr.rule.field !='__OR__' && rule_ctrlr.rule.field !='__NOT__'\">\n" +
5380 5380 " is {{rule_ctrlr.ruleDefinitions.allOps[rule_ctrlr.rule.op]}} {{rule_ctrlr.rule.value}}\n" +
5381 5381 " </span>\n" +
5382 5382 "\n" +
5383 " <span ng-if=\"rule_ctrlr.rule.field == '__AND__' || rule_ctrlr.rule.field =='__OR__'\">\n" +
5383 " <span ng-if=\"rule_ctrlr.rule.field == '__AND__' || rule_ctrlr.rule.field =='__OR__' || rule_ctrlr.rule.field =='__NOT__'\">\n" +
5384 5384 " <p ng-if=\"parent\"><strong>Subrules</strong></p>\n" +
5385 5385 " <div ng-repeat=\"subrule in rule_ctrlr.rule.rules\" class=\"m-l-2\">\n" +
5386 5386 "\n" +
@@ -5408,7 +5408,7 b' function kickstartAE() {'
5408 5408 " ng-options=\"key as label for (key, label) in rule_ctrlr.ruleDefinitions.possibleFields\"></select>\n" +
5409 5409 " </div>\n" +
5410 5410 "\n" +
5411 " <div ng-if=\"rule_ctrlr.rule.field != '__AND__' && rule_ctrlr.rule.field !='__OR__'\" class=\"form-group\">\n" +
5411 " <div ng-if=\"rule_ctrlr.rule.field != '__AND__' && rule_ctrlr.rule.field !='__OR__' && rule_ctrlr.rule.field !='__NOT__'\" class=\"form-group\">\n" +
5412 5412 "\n" +
5413 5413 " <select ng-model=\"rule_ctrlr.rule.op\" class=\"form-control\"\n" +
5414 5414 " ng-change=\"rule_ctrlr.setDirty()\"\n" +
@@ -5419,7 +5419,7 b' function kickstartAE() {'
5419 5419 "\n" +
5420 5420 " </div>\n" +
5421 5421 "\n" +
5422 " <span ng-if=\"rule_ctrlr.rule.field == '__AND__' || rule_ctrlr.rule.field =='__OR__'\">\n" +
5422 " <span ng-if=\"rule_ctrlr.rule.field == '__AND__' || rule_ctrlr.rule.field =='__OR__' || rule_ctrlr.rule.field =='__NOT__'\">\n" +
5423 5423 " <p ng-if=\"parent\"><strong>Subrules</strong></p>\n" +
5424 5424 " <div ng-repeat=\"subrule in rule_ctrlr.rule.rules\" class=\"m-l-2\">\n" +
5425 5425 " <div class=\"panel panel-default\">\n" +
@@ -10694,6 +10694,7 b' function AlertChannelsController(userSelfPropertyResource, applicationsNoIdResou'
10694 10694 var possibleFields = {
10695 10695 '__AND__': 'All met (composite rule)',
10696 10696 '__OR__': 'One met (composite rule)',
10697 '__NOT__': 'Not met (composite rule)',
10697 10698 'http_status': 'HTTP Status',
10698 10699 'duration': 'Request duration',
10699 10700 'group:priority': 'Group -> Priority',
@@ -11610,6 +11611,7 b" angular.module('appenlight.directives.postProcessAction', []).directive('postPro"
11610 11611 var possibleFields = {
11611 11612 '__AND__': 'All met (composite rule)',
11612 11613 '__OR__': 'One met (composite rule)',
11614 '__NOT__': 'Not met (composite rule)',
11613 11615 'http_status': 'HTTP Status',
11614 11616 'duration': 'Request duration',
11615 11617 'group:priority': 'Group -> Priority',
@@ -11927,8 +11929,9 b" angular.module('appenlight.directives.rule', []).directive('rule', function () {"
11927 11929 };
11928 11930
11929 11931 vm.fieldChange = function () {
11930 var new_is_compound = ['__AND__', '__OR__'].indexOf(vm.rule.field) !== -1;
11931 var old_was_compound = ['__AND__', '__OR__'].indexOf(vm.oldField) !== -1;
11932 var compound_types = ['__AND__', '__OR__', '__NOT__'];
11933 var new_is_compound = compound_types.indexOf(vm.rule.field) !== -1;
11934 var old_was_compound = compound_types.indexOf(vm.oldField) !== -1;
11932 11935
11933 11936 if (!new_is_compound) {
11934 11937 vm.rule.op = vm.ruleDefinitions.fieldOps[vm.rule.field][0];
@@ -1031,6 +1031,38 b' class TestRulesParsing():'
1031 1031
1032 1032 @pytest.mark.usefixtures('report_type_matrix')
1033 1033 class TestNestedRuleParsing():
1034
1035 @pytest.mark.parametrize("data, result", [
1036 ({"http_status": 501, "group": {"priority": 7, "occurences": 11}},
1037 False),
1038 ({"http_status": 101, "group": {"priority": 7, "occurences": 11}},
1039 False),
1040 ({"http_status": 500, "group": {"priority": 1, "occurences": 11}},
1041 False),
1042 ({"http_status": 101, "group": {"priority": 3, "occurences": 5}},
1043 True),
1044 ])
1045 def test_NOT_rule(self, data, result, report_type_matrix):
1046 from appenlight.lib.rule import Rule
1047 rule_config = {
1048 "field": "__NOT__",
1049 "rules": [
1050 {
1051 "op": "ge",
1052 "field": "group:occurences",
1053 "value": "10"
1054 },
1055 {
1056 "op": "ge",
1057 "field": "group:priority",
1058 "value": "4"
1059 }
1060 ]
1061 }
1062
1063 rule = Rule(rule_config, report_type_matrix)
1064 assert rule.match(data) is result
1065
1034 1066 @pytest.mark.parametrize("data, result", [
1035 1067 ({"http_status": 501, "group": {"priority": 7, "occurences": 11}},
1036 1068 True),
@@ -716,7 +716,7 b' def build_rule_schema(ruleset, check_matrix):'
716 716 schema = colander.SchemaNode(colander.Mapping())
717 717 schema.add(colander.SchemaNode(colander.String(), name='field'))
718 718
719 if ruleset['field'] in ['__AND__', '__OR__']:
719 if ruleset['field'] in ['__AND__', '__OR__', '__NOT__']:
720 720 subrules = colander.SchemaNode(colander.Tuple(), name='rules')
721 721 for rule in ruleset['rules']:
722 722 subrules.add(build_rule_schema(rule, check_matrix))
@@ -70,6 +70,7 b' function AlertChannelsController(userSelfPropertyResource, applicationsNoIdResou'
70 70 var possibleFields = {
71 71 '__AND__': 'All met (composite rule)',
72 72 '__OR__': 'One met (composite rule)',
73 '__NOT__': 'Not met (composite rule)',
73 74 'http_status': 'HTTP Status',
74 75 'duration': 'Request duration',
75 76 'group:priority': 'Group -> Priority',
@@ -61,6 +61,7 b" angular.module('appenlight.directives.postProcessAction', []).directive('postPro"
61 61 var possibleFields = {
62 62 '__AND__': 'All met (composite rule)',
63 63 '__OR__': 'One met (composite rule)',
64 '__NOT__': 'Not met (composite rule)',
64 65 'http_status': 'HTTP Status',
65 66 'duration': 'Request duration',
66 67 'group:priority': 'Group -> Priority',
@@ -56,8 +56,9 b" angular.module('appenlight.directives.rule', []).directive('rule', function () {"
56 56 };
57 57
58 58 vm.fieldChange = function () {
59 var new_is_compound = ['__AND__', '__OR__'].indexOf(vm.rule.field) !== -1;
60 var old_was_compound = ['__AND__', '__OR__'].indexOf(vm.oldField) !== -1;
59 var compound_types = ['__AND__', '__OR__', '__NOT__'];
60 var new_is_compound = compound_types.indexOf(vm.rule.field) !== -1;
61 var old_was_compound = compound_types.indexOf(vm.oldField) !== -1;
61 62
62 63 if (!new_is_compound) {
63 64 vm.rule.op = vm.ruleDefinitions.fieldOps[vm.rule.field][0];
@@ -7,7 +7,7 b''
7 7 ng-options="key as label for (key, label) in rule_ctrlr.ruleDefinitions.possibleFields"></select>
8 8 </div>
9 9
10 <div ng-if="rule_ctrlr.rule.field != '__AND__' && rule_ctrlr.rule.field !='__OR__'" class="form-group">
10 <div ng-if="rule_ctrlr.rule.field != '__AND__' && rule_ctrlr.rule.field !='__OR__' && rule_ctrlr.rule.field !='__NOT__'" class="form-group">
11 11
12 12 <select ng-model="rule_ctrlr.rule.op" class="form-control"
13 13 ng-change="rule_ctrlr.setDirty()"
@@ -18,7 +18,7 b''
18 18
19 19 </div>
20 20
21 <span ng-if="rule_ctrlr.rule.field == '__AND__' || rule_ctrlr.rule.field =='__OR__'">
21 <span ng-if="rule_ctrlr.rule.field == '__AND__' || rule_ctrlr.rule.field =='__OR__' || rule_ctrlr.rule.field =='__NOT__'">
22 22 <p ng-if="parent"><strong>Subrules</strong></p>
23 23 <div ng-repeat="subrule in rule_ctrlr.rule.rules" class="m-l-2">
24 24 <div class="panel panel-default">
@@ -4,11 +4,11 b''
4 4 {{rule_ctrlr.readOnlyPossibleFields[rule_ctrlr.rule.field]}}
5 5 </span>
6 6
7 <span ng-if="rule_ctrlr.rule.field != '__AND__' && rule_ctrlr.rule.field !='__OR__'">
7 <span ng-if="rule_ctrlr.rule.field != '__AND__' && rule_ctrlr.rule.field !='__OR__' && rule_ctrlr.rule.field !='__NOT__'">
8 8 is {{rule_ctrlr.ruleDefinitions.allOps[rule_ctrlr.rule.op]}} {{rule_ctrlr.rule.value}}
9 9 </span>
10 10
11 <span ng-if="rule_ctrlr.rule.field == '__AND__' || rule_ctrlr.rule.field =='__OR__'">
11 <span ng-if="rule_ctrlr.rule.field == '__AND__' || rule_ctrlr.rule.field =='__OR__' || rule_ctrlr.rule.field =='__NOT__'">
12 12 <p ng-if="parent"><strong>Subrules</strong></p>
13 13 <div ng-repeat="subrule in rule_ctrlr.rule.rules" class="m-l-2">
14 14
General Comments 0
You need to be logged in to leave comments. Login now