commit d0d17a7fb663a142a282213b786229baf365dca5 Author: Malar Kannan Date: Thu Jun 8 14:33:52 2017 +0530 implemented the rule engine diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7ecbf91 --- /dev/null +++ b/.gitignore @@ -0,0 +1,104 @@ + +# Created by https://www.gitignore.io/api/python + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# dotenv +.env + +# virtualenv +.venv +venv/ +ENV/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# End of https://www.gitignore.io/api/python diff --git a/EN-RuleEngineChallenge-080417-1436.pdf b/EN-RuleEngineChallenge-080417-1436.pdf new file mode 100644 index 0000000..56eae20 Binary files /dev/null and b/EN-RuleEngineChallenge-080417-1436.pdf differ diff --git a/quartic_rule.py b/quartic_rule.py new file mode 100644 index 0000000..e15302f --- /dev/null +++ b/quartic_rule.py @@ -0,0 +1,111 @@ +import json +import datetime + +class Rule(object): + """docstring for Rule.""" + def __init__(self, rule_line,lineno): + super(Rule, self).__init__() + self.pattern,constraint = [x.strip() for x in rule_line.split("->")] + space_sep = constraint.split() + (self.value_type,op),const = space_sep[:2]," ".join(space_sep[2:]) + self.const = get_const_for_type(self.value_type,const) + self.neg = True if op[0] == "!" else False + self.op = op[1:] if self.neg else op + self.lineno = lineno + + def __repr__(self): + neg_val = "not" if self.neg else "" + rule_val = [self.pattern,self.value_type,neg_val,self.op,self.const] + return "{}:{} {} {} {}".format(*rule_val) + + def apply_neg(self,x): + return not x if self.neg else x + + def apply(self,signal): + if signal.signal == self.pattern: + if signal.value_type != self.value_type: + return True + if self.op == ">": + return self.apply_neg(signal.value > self.const) + if self.op == "<": + return self.apply_neg(signal.value < self.const) + if self.op == "=": + return self.apply_neg(signal.value == self.const) + return True + + @classmethod + def rule_engine_from(cls,rule_str): + return Rule.rule_engine_gen([cls(rule_str,1)]) + + @staticmethod + def rule_engine_gen(rule_list): + def check_rules(signal): + for rule in rule_list: + if not rule.apply(signal): + return (False,rule) + return (True,None) + return check_rules + + @classmethod + def read_rules(cls,filename): + rule_list = [] + with open(filename,"r") as rule_file: + lineno = 1 + for line in rule_file.readlines(): + rulestr = line.strip() + if rulestr != "" and not rulestr.startswith("#"): + rule_list.append(cls(rulestr,lineno)) + lineno+=1 + return Rule.rule_engine_gen(rule_list) + +date_parse = lambda x : datetime.datetime.strptime(x, "%Y-%m-%d %H:%M:%S") +def get_const_for_type(value_type,value_data): + if value_type == "Integer": + value = int(float(value_data)) + elif value_type == "String": + value = str(value_data) + elif value_type == "Datetime": + if value_data == "now": + value = datetime.datetime.now() + else: + value = date_parse(value_data) + else: + value = "invalid" + return value + +class SignalItem(object): + """docstring for SignalItem.""" + def __init__(self, sig_dict): + super(SignalItem, self).__init__() + self.signal = sig_dict["signal"] + self.value_type = sig_dict["value_type"] + self.value = get_const_for_type(self.value_type,sig_dict["value"]) + def __repr__(self): + return self.signal+":"+str(self.value) + +def load_sigs(filename): + with open(filename,"r") as sig_file: + return json.load(sig_file) + +def validate_sigs(signals,rule_engine): + invalids = [] + index = 0 + for sig in signals: + sig_obj = SignalItem(sig) + index+=1 + status,rule = rule_engine(sig_obj) + if not status: + invalids.append((sig_obj,rule,index)) + return invalids + +def main(): + check_rules = Rule.read_rules("./rules.txt") + signals = load_sigs("./raw_data.json") + invalids = validate_sigs(signals,check_rules) + invalid_idx = [i[2] for i in invalids] + print invalid_idx + for sig_obj,rule,index in invalids: + print index,sig_obj," Cause Rule ",rule.lineno + +if __name__ == '__main__': + main() diff --git a/quartic_rule_test.py b/quartic_rule_test.py new file mode 100644 index 0000000..785022e --- /dev/null +++ b/quartic_rule_test.py @@ -0,0 +1,41 @@ +import unittest +import quartic_rule + +class QuarticTest(unittest.TestCase): + """docstring for QuarticTest.""" + + def setUp(self): + self.signals = quartic_rule.load_sigs("./raw_data.json") + pass + + def test_rules(self): + check_rules = quartic_rule.Rule.read_rules("./rules.txt") + invalids = quartic_rule.validate_sigs(self.signals,check_rules) + invalid_idx = [i[2] for i in invalids] + expected = [16 ,26 ,37 ,77 ,88 ,96 ,155,172,190] + self.assertEqual(invalid_idx ,expected) + + + def test_rules_test(self): + check_rules = quartic_rule.Rule.read_rules("./rules_test.txt") + invalids = quartic_rule.validate_sigs(self.signals,check_rules) + invalid_idx = [i[2] for i in invalids] + expected = [1, 9, 45, 53, 85, 109, 110, 119, 122, 142, 152, 199] + self.assertEqual(invalid_idx ,expected) + + def test_rule1(self): + check_rules = quartic_rule.Rule.rule_engine_from("ATL3 -> Datetime !> now") + invalids = quartic_rule.validate_sigs(self.signals,check_rules) + invalid_idx = [i[2] for i in invalids] + expected = [26, 37, 96, 172, 190] + self.assertEqual(invalid_idx ,expected) + + def test_rule2(self): + check_rules = quartic_rule.Rule.rule_engine_from("ATL2 -> Datetime != 2017-05-13 06:22:35") + invalids = quartic_rule.validate_sigs(self.signals,check_rules) + invalid_idx = [i[2] for i in invalids] + expected = [9] + self.assertEqual(invalid_idx ,expected) + +if __name__ == '__main__': + unittest.main() diff --git a/raw_data.json b/raw_data.json new file mode 100644 index 0000000..1820757 --- /dev/null +++ b/raw_data.json @@ -0,0 +1 @@ +[{"signal": "ATL2", "value_type": "String", "value": "HIGH"}, {"signal": "ATL9", "value_type": "Datetime", "value": "2017-06-13 22:40:10"}, {"signal": "ATL3", "value_type": "String", "value": "LOW"}, {"signal": "ATL8", "value_type": "String", "value": "LOW"}, {"signal": "ATL1", "value_type": "String", "value": "HIGH"}, {"signal": "ATL1", "value_type": "Datetime", "value": "2017-04-10 10:16:55"}, {"signal": "ATL6", "value_type": "Integer", "value": "75.361"}, {"signal": "ATL3", "value_type": "Datetime", "value": "2017-04-21 07:55:28"}, {"signal": "ATL2", "value_type": "Datetime", "value": "2017-05-13 06:22:35"}, {"signal": "ATL10", "value_type": "String", "value": "LOW"}, {"signal": "ATL10", "value_type": "String", "value": "LOW"}, {"signal": "ATL6", "value_type": "String", "value": "LOW"}, {"signal": "ATL10", "value_type": "String", "value": "LOW"}, {"signal": "ATL5", "value_type": "Datetime", "value": "2017-07-21 20:20:21"}, {"signal": "ATL10", "value_type": "Datetime", "value": "2017-07-26 16:35:11"}, {"signal": "ATL2", "value_type": "String", "value": "LOW"}, {"signal": "ATL8", "value_type": "Datetime", "value": "2017-05-18 03:51:34"}, {"signal": "ATL3", "value_type": "Integer", "value": "65.236"}, {"signal": "ATL1", "value_type": "Datetime", "value": "2017-02-18 16:49:03"}, {"signal": "ATL4", "value_type": "Integer", "value": "5.128"}, {"signal": "ATL10", "value_type": "Integer", "value": "46.691"}, {"signal": "ATL8", "value_type": "String", "value": "HIGH"}, {"signal": "ATL1", "value_type": "String", "value": "LOW"}, {"signal": "ATL5", "value_type": "String", "value": "LOW"}, {"signal": "ATL10", "value_type": "String", "value": "HIGH"}, {"signal": "ATL3", "value_type": "Datetime", "value": "2017-07-14 19:13:37"}, {"signal": "ATL7", "value_type": "String", "value": "HIGH"}, {"signal": "ATL10", "value_type": "Datetime", "value": "2017-04-21 16:22:11"}, {"signal": "ATL8", "value_type": "String", "value": "HIGH"}, {"signal": "ATL9", "value_type": "Datetime", "value": "2017-01-10 01:13:47"}, {"signal": "ATL9", "value_type": "String", "value": "HIGH"}, {"signal": "ATL5", "value_type": "String", "value": "HIGH"}, {"signal": "ATL7", "value_type": "String", "value": "LOW"}, {"signal": "ATL5", "value_type": "Integer", "value": "48.987"}, {"signal": "ATL5", "value_type": "Datetime", "value": "2017-06-20 10:53:20"}, {"signal": "ATL2", "value_type": "Integer", "value": "54.026"}, {"signal": "ATL3", "value_type": "Datetime", "value": "2017-10-06 04:19:46"}, {"signal": "ATL1", "value_type": "Integer", "value": "12.515"}, {"signal": "ATL5", "value_type": "Integer", "value": "34.458"}, {"signal": "ATL10", "value_type": "String", "value": "HIGH"}, {"signal": "ATL9", "value_type": "Integer", "value": "5.534"}, {"signal": "ATL9", "value_type": "Integer", "value": "79.476"}, {"signal": "ATL2", "value_type": "Integer", "value": "84.813"}, {"signal": "ATL9", "value_type": "Datetime", "value": "2017-08-09 15:44:47"}, {"signal": "ATL2", "value_type": "Datetime", "value": "2017-08-20 07:25:29"}, {"signal": "ATL10", "value_type": "String", "value": "LOW"}, {"signal": "ATL10", "value_type": "Datetime", "value": "2017-08-02 05:20:21"}, {"signal": "ATL4", "value_type": "String", "value": "HIGH"}, {"signal": "ATL5", "value_type": "String", "value": "HIGH"}, {"signal": "ATL9", "value_type": "Datetime", "value": "2017-07-15 02:40:17"}, {"signal": "ATL9", "value_type": "String", "value": "HIGH"}, {"signal": "ATL5", "value_type": "String", "value": "HIGH"}, {"signal": "ATL1", "value_type": "Integer", "value": "63.679"}, {"signal": "ATL9", "value_type": "String", "value": "HIGH"}, {"signal": "ATL7", "value_type": "Integer", "value": "34.937"}, {"signal": "ATL9", "value_type": "Datetime", "value": "2017-05-29 14:58:42"}, {"signal": "ATL9", "value_type": "Integer", "value": "23.512"}, {"signal": "ATL1", "value_type": "String", "value": "LOW"}, {"signal": "ATL8", "value_type": "Integer", "value": "10.712"}, {"signal": "ATL8", "value_type": "Datetime", "value": "2017-09-15 05:52:58"}, {"signal": "ATL7", "value_type": "Integer", "value": "76.395"}, {"signal": "ATL8", "value_type": "Datetime", "value": "2017-05-02 17:26:18"}, {"signal": "ATL9", "value_type": "Datetime", "value": "2017-10-08 11:42:36"}, {"signal": "ATL9", "value_type": "Datetime", "value": "2017-07-26 11:42:51"}, {"signal": "ATL1", "value_type": "Datetime", "value": "2017-05-15 01:37:18"}, {"signal": "ATL1", "value_type": "String", "value": "HIGH"}, {"signal": "ATL6", "value_type": "Integer", "value": "88.602"}, {"signal": "ATL6", "value_type": "Integer", "value": "92.739"}, {"signal": "ATL2", "value_type": "Integer", "value": "29.529"}, {"signal": "ATL1", "value_type": "String", "value": "LOW"}, {"signal": "ATL4", "value_type": "Datetime", "value": "2017-08-02 21:18:13"}, {"signal": "ATL5", "value_type": "String", "value": "LOW"}, {"signal": "ATL6", "value_type": "Datetime", "value": "2017-05-17 02:27:57"}, {"signal": "ATL10", "value_type": "Datetime", "value": "2017-09-22 03:24:18"}, {"signal": "ATL10", "value_type": "Integer", "value": "26.025"}, {"signal": "ATL3", "value_type": "Integer", "value": "51.779"}, {"signal": "ATL2", "value_type": "String", "value": "LOW"}, {"signal": "ATL5", "value_type": "Integer", "value": "13.381"}, {"signal": "ATL3", "value_type": "String", "value": "HIGH"}, {"signal": "ATL6", "value_type": "Integer", "value": "98.069"}, {"signal": "ATL2", "value_type": "Integer", "value": "17.263"}, {"signal": "ATL4", "value_type": "Datetime", "value": "2017-10-04 18:12:22"}, {"signal": "ATL1", "value_type": "Datetime", "value": "2017-01-19 23:08:25"}, {"signal": "ATL8", "value_type": "Integer", "value": "55.558"}, {"signal": "ATL2", "value_type": "Datetime", "value": "2017-07-04 17:39:19"}, {"signal": "ATL10", "value_type": "Integer", "value": "83.678"}, {"signal": "ATL10", "value_type": "Integer", "value": "50.377"}, {"signal": "ATL2", "value_type": "String", "value": "LOW"}, {"signal": "ATL10", "value_type": "Integer", "value": "1.432"}, {"signal": "ATL7", "value_type": "String", "value": "HIGH"}, {"signal": "ATL9", "value_type": "Integer", "value": "50.096"}, {"signal": "ATL4", "value_type": "String", "value": "LOW"}, {"signal": "ATL2", "value_type": "Integer", "value": "24.831"}, {"signal": "ATL10", "value_type": "String", "value": "LOW"}, {"signal": "ATL4", "value_type": "String", "value": "LOW"}, {"signal": "ATL3", "value_type": "Datetime", "value": "2017-08-15 06:26:17"}, {"signal": "ATL9", "value_type": "String", "value": "LOW"}, {"signal": "ATL8", "value_type": "String", "value": "HIGH"}, {"signal": "ATL5", "value_type": "Datetime", "value": "2017-09-17 17:50:42"}, {"signal": "ATL3", "value_type": "Integer", "value": "0.632"}, {"signal": "ATL3", "value_type": "Datetime", "value": "2017-02-22 09:42:17"}, {"signal": "ATL9", "value_type": "Integer", "value": "25.096"}, {"signal": "ATL6", "value_type": "Datetime", "value": "2017-08-22 19:23:20"}, {"signal": "ATL2", "value_type": "Integer", "value": "49.989"}, {"signal": "ATL7", "value_type": "Datetime", "value": "2017-07-24 18:02:12"}, {"signal": "ATL9", "value_type": "Integer", "value": "48.584"}, {"signal": "ATL3", "value_type": "String", "value": "LOW"}, {"signal": "ATL4", "value_type": "Datetime", "value": "2017-08-13 01:16:03"}, {"signal": "ATL1", "value_type": "Integer", "value": "13.292"}, {"signal": "ATL2", "value_type": "String", "value": "HIGH"}, {"signal": "ATL4", "value_type": "String", "value": "HIGH"}, {"signal": "ATL7", "value_type": "Datetime", "value": "2017-09-14 21:38:09"}, {"signal": "ATL7", "value_type": "Integer", "value": "65.006"}, {"signal": "ATL1", "value_type": "String", "value": "LOW"}, {"signal": "ATL9", "value_type": "Integer", "value": "63.368"}, {"signal": "ATL6", "value_type": "String", "value": "LOW"}, {"signal": "ATL5", "value_type": "String", "value": "HIGH"}, {"signal": "ATL8", "value_type": "Integer", "value": "86.448"}, {"signal": "ATL1", "value_type": "Integer", "value": "99.571"}, {"signal": "ATL4", "value_type": "String", "value": "HIGH"}, {"signal": "ATL7", "value_type": "String", "value": "HIGH"}, {"signal": "ATL2", "value_type": "String", "value": "HIGH"}, {"signal": "ATL7", "value_type": "String", "value": "LOW"}, {"signal": "ATL7", "value_type": "Datetime", "value": "2017-01-25 13:03:43"}, {"signal": "ATL6", "value_type": "Integer", "value": "81.376"}, {"signal": "ATL9", "value_type": "String", "value": "HIGH"}, {"signal": "ATL9", "value_type": "Datetime", "value": "2017-08-28 09:20:21"}, {"signal": "ATL9", "value_type": "Integer", "value": "45.66"}, {"signal": "ATL5", "value_type": "Integer", "value": "53.429"}, {"signal": "ATL7", "value_type": "Integer", "value": "97.328"}, {"signal": "ATL9", "value_type": "Datetime", "value": "2017-05-27 15:55:49"}, {"signal": "ATL2", "value_type": "Datetime", "value": "2017-05-04 04:48:18"}, {"signal": "ATL8", "value_type": "Datetime", "value": "2017-03-13 06:13:41"}, {"signal": "ATL7", "value_type": "Datetime", "value": "2017-06-12 22:10:18"}, {"signal": "ATL9", "value_type": "String", "value": "LOW"}, {"signal": "ATL2", "value_type": "Datetime", "value": "2017-01-10 12:51:10"}, {"signal": "ATL1", "value_type": "Integer", "value": "3.3"}, {"signal": "ATL2", "value_type": "Integer", "value": "36.609"}, {"signal": "ATL7", "value_type": "String", "value": "LOW"}, {"signal": "ATL5", "value_type": "Integer", "value": "78.006"}, {"signal": "ATL5", "value_type": "Integer", "value": "18.849"}, {"signal": "ATL2", "value_type": "Datetime", "value": "2017-09-28 17:57:59"}, {"signal": "ATL7", "value_type": "Datetime", "value": "2017-01-08 05:44:30"}, {"signal": "ATL9", "value_type": "String", "value": "LOW"}, {"signal": "ATL9", "value_type": "String", "value": "LOW"}, {"signal": "ATL2", "value_type": "Datetime", "value": "2017-02-20 03:52:20"}, {"signal": "ATL3", "value_type": "Datetime", "value": "2017-01-16 17:22:30"}, {"signal": "ATL1", "value_type": "Datetime", "value": "2017-10-09 02:06:30"}, {"signal": "ATL3", "value_type": "Integer", "value": "24.193"}, {"signal": "ATL9", "value_type": "Datetime", "value": "2017-03-05 03:39:40"}, {"signal": "ATL7", "value_type": "String", "value": "HIGH"}, {"signal": "ATL1", "value_type": "Integer", "value": "56.679"}, {"signal": "ATL4", "value_type": "String", "value": "HIGH"}, {"signal": "ATL3", "value_type": "Integer", "value": "40.472"}, {"signal": "ATL2", "value_type": "String", "value": "LOW"}, {"signal": "ATL8", "value_type": "Integer", "value": "82.074"}, {"signal": "ATL4", "value_type": "String", "value": "HIGH"}, {"signal": "ATL3", "value_type": "Integer", "value": "60.58"}, {"signal": "ATL8", "value_type": "String", "value": "LOW"}, {"signal": "ATL4", "value_type": "Datetime", "value": "2017-04-26 03:45:58"}, {"signal": "ATL9", "value_type": "String", "value": "HIGH"}, {"signal": "ATL3", "value_type": "String", "value": "LOW"}, {"signal": "ATL6", "value_type": "Datetime", "value": "2017-04-01 13:29:31"}, {"signal": "ATL9", "value_type": "Datetime", "value": "2017-09-30 17:31:23"}, {"signal": "ATL8", "value_type": "Integer", "value": "92.425"}, {"signal": "ATL3", "value_type": "String", "value": "LOW"}, {"signal": "ATL9", "value_type": "String", "value": "HIGH"}, {"signal": "ATL2", "value_type": "Datetime", "value": "2017-04-01 22:08:57"}, {"signal": "ATL7", "value_type": "Integer", "value": "72.677"}, {"signal": "ATL4", "value_type": "Integer", "value": "19.937"}, {"signal": "ATL5", "value_type": "String", "value": "HIGH"}, {"signal": "ATL3", "value_type": "Datetime", "value": "2017-08-28 22:21:13"}, {"signal": "ATL7", "value_type": "String", "value": "HIGH"}, {"signal": "ATL9", "value_type": "Integer", "value": "92.812"}, {"signal": "ATL3", "value_type": "String", "value": "LOW"}, {"signal": "ATL7", "value_type": "String", "value": "HIGH"}, {"signal": "ATL4", "value_type": "Integer", "value": "66.747"}, {"signal": "ATL6", "value_type": "String", "value": "LOW"}, {"signal": "ATL10", "value_type": "String", "value": "LOW"}, {"signal": "ATL2", "value_type": "Datetime", "value": "2017-03-19 02:47:28"}, {"signal": "ATL10", "value_type": "Datetime", "value": "2017-07-10 09:59:09"}, {"signal": "ATL6", "value_type": "Datetime", "value": "2017-08-21 17:07:40"}, {"signal": "ATL6", "value_type": "Datetime", "value": "2017-02-23 20:53:00"}, {"signal": "ATL6", "value_type": "Datetime", "value": "2017-07-27 02:00:32"}, {"signal": "ATL7", "value_type": "Integer", "value": "5.504"}, {"signal": "ATL1", "value_type": "String", "value": "LOW"}, {"signal": "ATL1", "value_type": "String", "value": "LOW"}, {"signal": "ATL4", "value_type": "String", "value": "LOW"}, {"signal": "ATL3", "value_type": "Integer", "value": "50.117"}, {"signal": "ATL3", "value_type": "Datetime", "value": "2017-08-30 20:06:27"}, {"signal": "ATL9", "value_type": "String", "value": "LOW"}, {"signal": "ATL7", "value_type": "Datetime", "value": "2017-08-04 06:02:04"}, {"signal": "ATL1", "value_type": "Datetime", "value": "2017-06-25 09:32:40"}, {"signal": "ATL4", "value_type": "Integer", "value": "12.49"}, {"signal": "ATL8", "value_type": "Integer", "value": "21.0"}, {"signal": "ATL8", "value_type": "Datetime", "value": "2017-04-04 19:14:59"}, {"signal": "ATL7", "value_type": "Integer", "value": "17.574"}, {"signal": "ATL6", "value_type": "String", "value": "LOW"}, {"signal": "ATL1", "value_type": "Integer", "value": "46.593"}, {"signal": "ATL10", "value_type": "Integer", "value": "89.241"}] diff --git a/rules.txt b/rules.txt new file mode 100644 index 0000000..d2e5a4d --- /dev/null +++ b/rules.txt @@ -0,0 +1,3 @@ +ATL1 -> Integer !> 240 +ATL2 -> String != LOW +ATL3 -> Datetime !> now diff --git a/rules_test.txt b/rules_test.txt new file mode 100644 index 0000000..f03b769 --- /dev/null +++ b/rules_test.txt @@ -0,0 +1,3 @@ +ATL1 -> Integer !> 12 +ATL2 -> String != HIGH +ATL2 -> Datetime !> 2017-05-13 06:22:34