def test_missing(self): """ Takes an array of data keys to search for (same format as var). Returns an array of any keys that are missing from the data object, or an empty array. """ self.assertEqual( jsonLogic({"missing": ["a", "b"]}, { "a": "apple", "c": "carrot" }), ["b"]) self.assertEqual( jsonLogic({"missing": ["a", "b"]}, { "a": "apple", "b": "banana" }), []) # Note, in JsonLogic, empty arrays are falsy. So you can use missing # with if like: self.assertEqual( jsonLogic( { "if": [{ "missing": ["a", "b"] }, "Not enough fruit", "OK to proceed"] }, { "a": "apple", "b": "banana" }), "OK to proceed")
def test_strict_equality_ignores_numeric_type_differences(self): self.assertIs(jsonLogic({'===': [1, 1]}), True) self.assertIs(jsonLogic({'===': [1.23, 1.23]}), True) self.assertIs(jsonLogic({'===': [1, 1.0]}), True) self.assertIs( jsonLogic({'===': [10000000000000000000, 10000000000000000000.0]}), True)
def test_ternary_operator_does_not_evaluate_depth_first(self): # False consequent of '?:' operation condition should not run consequents = [] def push_then(arg): consequents.append(arg) return arg def push_else(arg): consequents.append(arg) return arg add_operation('push_then', push_then) add_operation('push_else', push_else) jsonLogic({ '?:': [True, { 'push_then': ["first"] }, { 'push_else': ["second"] }] }) self.assertSequenceEqual(consequents, ["first"]) del (consequents[:]) jsonLogic({ '?:': [False, { 'push_then': ["first"] }, { 'push_else': ["second"] }] }) self.assertSequenceEqual(consequents, ["second"])
def calculate_price(registration, campers): ''' Parameters ---------- registration: camphoric.models.Registration campers: [camphoric.models.Camper] In order to calculate the price without writing to the database, campers are passed as a separate array. Returns ------- dict: keys: registration level components, camper level components and total. values: subtotals of respective components and the total. ''' results = defaultdict(int) event = registration.event data = {"registration": registration.attributes, "pricing": event.pricing} for reg_component, logic in event.registration_pricing_logic.items(): results[reg_component] = jsonLogic(logic, data) for camper in campers: camper_data = { "registration": registration.attributes, "pricing": event.pricing, "camper": camper.attributes } for camper_component, logic in event.camper_pricing_logic.items(): results[camper_component] += jsonLogic(logic, camper_data) results['total'] = sum(results.values()) return dict(results)
def test_if_can_return_non_logic_dictionaries(self): logic = { 'if': [{ '==': [{ 'var': 'a' }, 1] }, { 'test': 1, 'data': 'if' }, { '==': [{ 'var': 'a' }, 2] }, { 'test': 2, 'data': 'else if' }, { 'test': 3, 'data': 'else' }] } self.assertEqual(jsonLogic(logic, {'a': 1}), {'test': 1, 'data': 'if'}) self.assertEqual(jsonLogic(logic, {'a': 2}), { 'test': 2, 'data': 'else if' }) self.assertEqual(jsonLogic(logic, {'a': 3}), { 'test': 3, 'data': 'else' })
def test_rm_operation_restores_overridden_operation(self): self.assertEqual(jsonLogic({'+': [2, 3]}), 5) add_operation('+', lambda *args: "Ha-ha!") try: self.assertEqual(jsonLogic({'+': [2, 3]}), "Ha-ha!") finally: rm_operation('+') self.assertEqual(jsonLogic({'+': [2, 3]}), 5) self.assertNotEqual(jsonLogic({'+': [2, 3]}), "Ha-ha!")
def main(): # Temp. in Fahrenheit [Normal = 98.6 - 100.4] rule1 = {"<=": [{"var": "temp"}, 98.6]} rule2 = { "and": [{ ">": [{ "var": "temp" }, 98.6] }, { "<=": [{ "var": "temp" }, 100.4] }] } # data = { "temp" : 50 } # data = { "temp" : 99 } # data = { "temp" : 500 } rule3 = { "and": [{ ">": [{ "var": "bp" }, 120] }, { ">": [{ "var": "oxygen" }, 100] }, { ">": [{ "var": "temp" }, 100.4] }] } data = sys.argv[1] try: data = json.loads(data) except Exception as e: print 'Error occured:', e data_temp = {"temp": float(data["temp"])} if jsonLogic(rule1, data_temp) == True: print 'Service Requested:' print "heat_up" elif jsonLogic(rule2, data_temp) == True: print 'Service Requested:' print "cool_down" elif jsonLogic(rule3, data) == True: print 'Next Workflow to execute:' print '_bp' else: print 'Next Workflow to execute:' print "_triplet"
def test_or(self): """ 'or' can be used for simple boolean tests, with 1 or more arguments. """ self.assertTrue(jsonLogic({"or": [True, False]})) # At a more sophisticated level, or returns the first truthy argument, # or the last argument. self.assertTrue(jsonLogic({"or": [False, True]})) self.assertEqual(jsonLogic({"or": [False, "apple"]}), "apple") self.assertEqual(jsonLogic({"or": [False, None, "apple"]}), "apple")
def test_cmp(self): """Arithmetic comparison functions.""" # Greater than: self.assertTrue(jsonLogic({">": [2, 1]})) # Greater than or equal to: self.assertTrue(jsonLogic({">=": [1, 1]})) # Less than: self.assertTrue(jsonLogic({"<": [1, 2]})) # Less than or equal to: self.assertTrue(jsonLogic({"<=": [1, 1]}))
def test_and(self): """ 'and' can be used for simple boolean tests, with 1 or more arguments. """ self.assertTrue(jsonLogic({"and": [True, True]})) self.assertFalse(jsonLogic({"and": [True, True, True, False]})) # At a more sophisticated level, and returns the first falsy argument, # or the last argument. self.assertFalse(jsonLogic({"and": [True, "apple", False]})) self.assertEqual(jsonLogic({"and": [True, "apple", 3.14]}), 3.14)
def test_cat(self): """ Concatenate all the supplied arguments. Note that this is not a join or implode operation, there is no "glue" string. """ self.assertEqual(jsonLogic({"cat": ["I love", " pie"]}), "I love pie") self.assertEqual( jsonLogic({"cat": ["I love ", { "var": "filling" }, " pie"]}, { "filling": "apple", "temp": 110 }), "I love apple pie")
def test_add_operation_with_simple_method(self): def add_to_five(*args): return sum((5, ) + args) self.assertRaisesRegex(ValueError, "Unrecognized operation", jsonLogic, {'add_to_five': [3]}) add_operation('add_to_five', add_to_five) try: self.assertEqual(jsonLogic({'add_to_five': 1}), 6) self.assertEqual(jsonLogic({'add_to_five': [3]}), 8) self.assertEqual(jsonLogic({'add_to_five': [3, 2]}), 10) finally: rm_operation('add_to_five')
def test_or_operator_does_not_evaluate_depth_first(self): # 'or' operator should stop at first truthy value evaluated_elements = [] def push(arg): evaluated_elements.append(arg) return arg add_operation('push', push) jsonLogic({'or': [{'push': [False]}, {'push': [False]}]}) self.assertSequenceEqual(evaluated_elements, [False, False]) del (evaluated_elements[:]) jsonLogic({'or': [{'push': [False]}, {'push': [True]}]}) self.assertSequenceEqual(evaluated_elements, [False, True]) del (evaluated_elements[:]) jsonLogic({'or': [{'push': [True]}, {'push': [False]}]}) self.assertSequenceEqual(evaluated_elements, [True]) del (evaluated_elements[:]) jsonLogic({'or': [{'push': [True]}, {'push': [True]}]}) self.assertSequenceEqual(evaluated_elements, [True])
def test_method_operation_with_method_without_arguments(self): todays_date = datetime.date.today() logic = {'method': [{'var': 'today'}, 'isoformat']} data = {'today': todays_date} returned_value = jsonLogic(logic, data) self.assertIsInstance(returned_value, six.string_types) self.assertEqual(returned_value, todays_date.isoformat())
def validate_logic(self, errors, data_row, rec_idx, date_conversion_errors, id_field, client_eps): """ 1. Checks the date format and converts dates into integer ('ordinal's) for easy comparison. 2. Checks MDS business logic defined in MDS_RULES.py (and imported in the global 'rule_definitions'). For example EPISODEs OVERLAP.(See 'ACT MDS Data Collection Guide' document for suggested logic checks) 3. If there errors in the date formats, all the logic checks (for the passed-in data row), that involve the malformed dates are excluded. The errors from these validations are added to the 'errors' array that is passed in. 4. client_eps : This is an (on-going) accumulated list of the start and end dates of the passed-in client's episodes. This allows us to calculate episode overlaps with the current data row. """ temp_rd = self.rule_definitions temp_rules = self.rules dce_fields = [] if any(date_conversion_errors): dce_fields = [dce['field'] for dce in date_conversion_errors] temp_rd = remove_vrules(dce_fields) temp_rules = [rd['rule'] for rd in temp_rd] result = (jsonLogic(r, data_row) for r in temp_rules) logic_errors = compile_logic_errors(result, temp_rd, data_row, rec_idx, id_field, date_conversion_errors) if rec_idx not in errors: errors[rec_idx] = logic_errors elif logic_errors: errors[rec_idx].extend(logic_errors) prep_and_check_overlap(data_row, client_eps, errors, rec_idx, dce_fields)
def test_method_operation_with_property(self): todays_date = datetime.date.today() logic = {'method': [{'var': 'today'}, 'month']} data = {'today': todays_date} returned_value = jsonLogic(logic, data) self.assertIsInstance(returned_value, int) self.assertEqual(returned_value, todays_date.month)
def test_type(self): """ Checks type of first value to the string representation of the second value. """ self.assertEqual( jsonLogic({"type": ["This is a string and should be None", None]}), True)
def test_array_of_logic_entries_can_return_non_logic_dictionaries(self): logic = [{'+': [1, 2]}, {'test': 1, 'data': 'if'}] self.assertSequenceEqual(jsonLogic(logic), [3, { 'test': 1, 'data': 'if' }])
def test_add_operation_with_packages(self): self.assertRaisesRegex(ValueError, "Unrecognized operation.*datetime", jsonLogic, {'datetime.datetime.now': []}) add_operation('datetime', datetime) try: # .now() start = datetime.datetime.now() returned_value = jsonLogic({'datetime.datetime.now': []}) self.assertIsInstance(returned_value, datetime.datetime) self.assertTrue(start <= returned_value <= datetime.datetime.now()) # .date() returned_value = jsonLogic({'datetime.date': [2018, 1, 1]}) self.assertIsInstance(returned_value, datetime.date) self.assertEqual(returned_value, datetime.date(2018, 1, 1)) finally: rm_operation('datetime')
def conditionally_visible(self): """ If conditional visibility applies, evaluate to see if it is visible. Note that the component can also be hidden, which is a separate concept. """ try: cond = self.raw['conditional'] if cond.get('json'): # Optional package try: from json_logic import jsonLogic context = {'data': self._all_data} try: context['row'] = self.component_owner.row except AttributeError: pass # only datagrid rows have a "row" attribute return jsonLogic(cond['json'], context) except ImportError: logger = logging.getLogger(__name__) logger.warn(f'Could not load json logic extension; will not evaluate visibility of {self.__class__.__name__} {self.id} ("{self.key}")') return True elif cond.get('when'): triggering_component = self.component_owner.input_components[cond['when']] triggering_value = cond['eq'] if triggering_component.value == triggering_value: return cond['show'] else: return not cond['show'] except KeyError: # Unknown component or no 'when', 'eq' or 'show' property pass # By default, it's visible return True
def main(): # Considering Systolic Pressure only rule1 = {"<=": [{"var": "bp"}, 90]} rule2 = {"and": [{">": [{"var": "bp"}, 90]}, {"<=": [{"var": "bp"}, 120]}]} # data = { "bp" : 40 } # data = { "bp" : 100 } rule3 = { "and": [{ ">": [{ "var": "bp" }, 120] }, { ">": [{ "var": "oxygen" }, 100] }, { ">": [{ "var": "temp" }, 100.4] }] } # data = '{ "bp" : 99, "temp" : 5000, "xyz":455}' data = sys.argv[1] try: data = json.loads(data) except Exception as e: print 'Error occured:', e data_bp = {"bp": float(data["bp"])} if jsonLogic(rule1, data_bp) == True: print 'Service Requested:' print "take_high_medicine" elif jsonLogic(rule2, data_bp) == True: print 'Service Requested:' print "take_low_medicine" elif jsonLogic(rule3, data) == True: print 'Next Workflow to execute:' print '_temp' else: print 'Next Workflow to execute:' print "_triplet"
def rule_triggers_event(rule, data, resources): if rule["resource"] not in resources: return False else: return rule["active"] and \ resources[rule["resource"]]["guard"] and \ rule["generates"] == "events" and \ jsonLogic(rule["rule"], data)
def test_arithmetic(self): """Arithmetic operators.""" self.assertEqual(jsonLogic({"+": [1, 1]}), 2) self.assertEqual(jsonLogic({"*": [2, 3]}), 6) self.assertEqual(jsonLogic({"-": [3, 2]}), 1) self.assertEqual(jsonLogic({"/": [2, 4]}), .5) self.assertEqual(jsonLogic({"+": [1, 1]}), 2) # Because addition and multiplication are associative, # they happily take as many args as you want: self.assertEqual(jsonLogic({"+": [1, 1, 1, 1, 1]}), 5) self.assertEqual(jsonLogic({"*": [2, 2, 2, 2, 2]}), 32) # Passing just one argument to - returns its arithmetic # negative (additive inverse). self.assertEqual(jsonLogic({"-": [2]}), -2) self.assertEqual(jsonLogic({"-": [-2]}), 2) # Passing just one argument to + casts it to a number. self.assertEqual(jsonLogic({"+": "0"}), 0)
def test_rm_operation_removes_custom_operation(self): add_operation('custom', lambda: "Ha-ha!") try: self.assertEqual(jsonLogic({'custom': []}), "Ha-ha!") finally: rm_operation('custom') self.assertRaisesRegex(ValueError, "Unrecognized operation", jsonLogic, {'custom': []})
def determine_score(rules, data): score = 0 if rules and isinstance(rules, List): for rule_obj in rules: rule = rule_obj.get(CONSTANTS.RULE_KEY) points = rule_obj.get(CONSTANTS.POINTS_KEY) if jsonLogic(rule, data): score = score + points return score
def property_filter_literal(candidate, simple_filter): ''' Given a single candidate entity ("uri", list_labels) and a filter where the value is a literal, it gives a tuple (true/false, score) where true/false depends on if the filter is respected and score is the product between similarity (or distance) and weight of the filter ''' #value = simple_filter.get_value() #threshold = simple_filter.get_threshold() results = find_property(candidate, simple_filter.get_property()) list_literal = [] list_uri = [] results_uri = [] list_literal_2 = [] for result in results["results"]["bindings"]: if result["object"]["type"] == 'literal': list_literal.append(result["object"]["value"]) elif result["object"]["type"] == 'uri': #list_uri.append(result["object"]["value"]) x = find_property(result["object"]["value"], "http://www.w3.org/2000/01/rdf-schema#label") y = find_property(result["object"]["value"], "http://xmlns.com/foaf/0.1/name") for j in x["results"]["bindings"]: list_literal_2.append(j["object"]["value"]) for k in y["results"]["bindings"]: list_literal_2.append(k["object"]["value"]) '''results_uri.append(x) # rdfs:label results_uri.append(y) # foaf:name''' #print("list literal:", list_literal) #print("list uri:", list_uri) #filter_literal = {"some" : [list_literal, {"<=" : [{"edit_distance" : [{"var" : ""}, value]}, threshold]}]} #print("filter literal:", jsonLogic(filter_literal)) '''for y in results_uri: for j in y["results"]["bindings"]: list_literal_2.append(j["object"]["value"])''' #print("list literal 2:", list_literal_2) list_total = list_literal + list_literal_2 #print("list total:", list_total) '''#filter_uri = {"some" : [list_literal_2, {"<=" : [{"edit_distance" : [{"var" : ""}, value]}, threshold]}]} filter = {"some" : [list_total, {"<=": [{"edit_distance": [{"var": ""}, value]}, threshold]}]} list_editdistance = jsonLogic({"map" : [list_total, {"edit_distance": [{"var": ""}, value]}]}) print("list editdistace:", list_editdistance) min_editdistance = jsonLogic({"min" : list_editdistance}) print("editdistance min:", jsonLogic(min_editdistance)) #print("filter uri:", jsonLogic(filter_uri)) print("filter total:", jsonLogic(filter)) print("score:", min_editdistance * simple_filter.get_weight())''' if (len(list_total) != 0): filter_and_score = similarity_filter(simple_filter, list_total) #print("filter and score[0]", filter_and_score[0]) #print("jsonlogic", jsonLogic(filter_and_score[0])) #print("result", (jsonLogic(filter_and_score[0]), filter_and_score[1] * simple_filter.get_weight())) return (jsonLogic(filter_and_score[0]), filter_and_score[1] * simple_filter.get_weight()) else: return ("No matches", None)
def test_operations_value_modifications_do_not_impact_fuctionality(self): global operations old_operations = operations try: operations['+'] = lambda *args: "Ha-ha!" result = jsonLogic({'+': [1, 2]}) self.assertNotEqual(result, "Ha-ha!") self.assertEqual(result, 3) finally: operations = old_operations # Restore exposed operations list
def filter_candidates(candidate_list, total_filter): ''' Given the list of candidate entities for a single value and the filter that aggregates all the filters, it returns only the candidate entities which satisfy total_filter ''' new_candidate_list = [] for x in candidate_list: if jsonLogic(total_filter): new_candidate_list.append(x) return new_candidate_list
def test_array(self): self.assertEqual( jsonLogic([{ "var": "a" }, { "var": "b" }], { "a": 1, "b": 2 }), [1, 2])
def test_depth_first_rule_still_applies_to_custom_operators(self): add_operation('sum_up', lambda *args: sum(args)) try: self.assertEqual( jsonLogic({'sum_up': [{ '-': [5, 3] }, { '*': [2, 3] }]}), 8) finally: rm_operation('sum_up')