diff --git a/business_rules/operators.py b/business_rules/operators.py index d43e5b1f..6887d848 100644 --- a/business_rules/operators.py +++ b/business_rules/operators.py @@ -71,31 +71,57 @@ def _assert_valid_value_and_cast(self, value): def equal_to(self, other_string): return self.value == other_string + @type_operator(FIELD_TEXT) + def not_equal_to(self, other_string): + return self.value != other_string + @type_operator(FIELD_TEXT, label="Equal To (case insensitive)") def equal_to_case_insensitive(self, other_string): return self.value.lower() == other_string.lower() + @type_operator(FIELD_TEXT, label="Not Equal To (case insensitive)") + def not_equal_to_case_insensitive(self, other_string): + return self.value.lower() != other_string.lower() + @type_operator(FIELD_TEXT) def starts_with(self, other_string): return self.value.startswith(other_string) + @type_operator(FIELD_TEXT) + def does_not_start_with(self, other_string): + return not self.value.startswith(other_string) + @type_operator(FIELD_TEXT) def ends_with(self, other_string): return self.value.endswith(other_string) + @type_operator(FIELD_TEXT) + def does_not_end_with(self, other_string): + return not self.value.endswith(other_string) + @type_operator(FIELD_TEXT) def contains(self, other_string): return other_string in self.value + @type_operator(FIELD_TEXT) + def does_not_contain(self, other_string): + return other_string not in self.value + @type_operator(FIELD_TEXT) def matches_regex(self, regex): return re.search(regex, self.value) + @type_operator(FIELD_TEXT) + def does_not_match_regex(self, regex): + return not re.search(regex, self.value) + @type_operator(FIELD_NO_INPUT) def non_empty(self): return bool(self.value) + + @export_type class NumericType(BaseType): EPSILON = Decimal('0.000001') diff --git a/tests/test_integration.py b/tests/test_integration.py index 008228cf..1573d331 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -126,48 +126,68 @@ def test_export_rule_data(self): 'options': []}]) self.assertEqual(all_data.get("variable_type_operators"), - {'boolean': [{'input_type': 'none', - 'label': 'Is False', - 'name': 'is_false'}, - {'input_type': 'none', - 'label': 'Is True', - 'name': 'is_true'}], - 'numeric': [{'input_type': 'numeric', - 'label': 'Equal To', - 'name': 'equal_to'}, - {'input_type': 'numeric', 'label': 'Greater Than', 'name': 'greater_than'}, - {'input_type': 'numeric', - 'label': 'Greater Than Or Equal To', - 'name': 'greater_than_or_equal_to'}, - {'input_type': 'numeric', 'label': 'Less Than', 'name': 'less_than'}, - {'input_type': 'numeric', - 'label': 'Less Than Or Equal To', - 'name': 'less_than_or_equal_to'}], - 'select': [{'input_type': 'select', 'label': 'Contains', 'name': 'contains'}, - {'input_type': 'select', - 'label': 'Does Not Contain', - 'name': 'does_not_contain'}], - 'select_multiple': [{'input_type': 'select_multiple', - 'label': 'Contains All', - 'name': 'contains_all'}, - {'input_type': 'select_multiple', - 'label': 'Is Contained By', - 'name': 'is_contained_by'}, - {'input_type': 'select_multiple', - 'label': 'Shares At Least One Element With', - 'name': 'shares_at_least_one_element_with'}, - {'input_type': 'select_multiple', - 'label': 'Shares Exactly One Element With', - 'name': 'shares_exactly_one_element_with'}, - {'input_type': 'select_multiple', - 'label': 'Shares No Elements With', - 'name': 'shares_no_elements_with'}], - 'string': [{'input_type': 'text', 'label': 'Contains', 'name': 'contains'}, - {'input_type': 'text', 'label': 'Ends With', 'name': 'ends_with'}, - {'input_type': 'text', 'label': 'Equal To', 'name': 'equal_to'}, - {'input_type': 'text', - 'label': 'Equal To (case insensitive)', - 'name': 'equal_to_case_insensitive'}, - {'input_type': 'text', 'label': 'Matches Regex', 'name': 'matches_regex'}, - {'input_type': 'none', 'label': 'Non Empty', 'name': 'non_empty'}, - {'input_type': 'text', 'label': 'Starts With', 'name': 'starts_with'}]}) + {'boolean': [{'input_type': 'none', 'label': 'Is False', 'name': 'is_false'}, + {'input_type': 'none', 'label': 'Is True', 'name': 'is_true'}], + 'numeric': [{'input_type': 'numeric', 'label': 'Equal To', 'name': 'equal_to'}, + {'input_type': 'numeric', + 'label': 'Greater Than', + 'name': 'greater_than'}, + {'input_type': 'numeric', + 'label': 'Greater Than Or Equal To', + 'name': 'greater_than_or_equal_to'}, + {'input_type': 'numeric', + 'label': 'Less Than', + 'name': 'less_than'}, + {'input_type': 'numeric', + 'label': 'Less Than Or Equal To', + 'name': 'less_than_or_equal_to'}], + 'select': [{'input_type': 'select', 'label': 'Contains', 'name': 'contains'}, + {'input_type': 'select', + 'label': 'Does Not Contain', + 'name': 'does_not_contain'}], + 'select_multiple': [{'input_type': 'select_multiple', + 'label': 'Contains All', + 'name': 'contains_all'}, + {'input_type': 'select_multiple', + 'label': 'Is Contained By', + 'name': 'is_contained_by'}, + {'input_type': 'select_multiple', + 'label': 'Shares At Least One Element With', + 'name': 'shares_at_least_one_element_with'}, + {'input_type': 'select_multiple', + 'label': 'Shares Exactly One Element With', + 'name': 'shares_exactly_one_element_with'}, + {'input_type': 'select_multiple', + 'label': 'Shares No Elements With', + 'name': 'shares_no_elements_with'}], + 'string': [{'input_type': 'text', 'label': 'Contains', 'name': 'contains'}, + {'input_type': 'text', + 'label': 'Does Not Contain', + 'name': 'does_not_contain'}, + {'input_type': 'text', + 'label': 'Does Not End With', + 'name': 'does_not_end_with'}, + {'input_type': 'text', + 'label': 'Does Not Match Regex', + 'name': 'does_not_match_regex'}, + {'input_type': 'text', + 'label': 'Does Not Start With', + 'name': 'does_not_start_with'}, + {'input_type': 'text', 'label': 'Ends With', 'name': 'ends_with'}, + {'input_type': 'text', 'label': 'Equal To', 'name': 'equal_to'}, + {'input_type': 'text', + 'label': 'Equal To (case insensitive)', + 'name': 'equal_to_case_insensitive'}, + {'input_type': 'text', + 'label': 'Matches Regex', + 'name': 'matches_regex'}, + {'input_type': 'none', 'label': 'Non Empty', 'name': 'non_empty'}, + {'input_type': 'text', + 'label': 'Not Equal To', + 'name': 'not_equal_to'}, + {'input_type': 'text', + 'label': 'Not Equal To (case insensitive)', + 'name': 'not_equal_to_case_insensitive'}, + {'input_type': 'text', + 'label': 'Starts With', + 'name': 'starts_with'}]}) diff --git a/tests/test_operators.py b/tests/test_operators.py index 824d4a69..9ee2ac29 100644 --- a/tests/test_operators.py +++ b/tests/test_operators.py @@ -15,21 +15,40 @@ def test_string_equal_to(self): self.assertTrue(StringType("foo").equal_to("foo")) self.assertFalse(StringType("foo").equal_to("Foo")) + def test_string_not_equal_to(self): + self.assertFalse(StringType("foo").not_equal_to("foo")) + self.assertTrue(StringType("foo").not_equal_to("Foo")) + def test_string_equal_to_case_insensitive(self): self.assertTrue(StringType("foo").equal_to_case_insensitive("FOo")) self.assertTrue(StringType("foo").equal_to_case_insensitive("foo")) self.assertFalse(StringType("foo").equal_to_case_insensitive("blah")) + def test_string_not_equal_to_case_insensitive(self): + self.assertFalse(StringType("foo").not_equal_to_case_insensitive("FOo")) + self.assertFalse(StringType("foo").not_equal_to_case_insensitive("foo")) + self.assertTrue(StringType("foo").not_equal_to_case_insensitive("blah")) + def test_string_starts_with(self): self.assertTrue(StringType("hello").starts_with("he")) self.assertFalse(StringType("hello").starts_with("hey")) self.assertFalse(StringType("hello").starts_with("He")) + def test_string_does_not_start_with(self): + self.assertFalse(StringType("hello").does_not_start_with("he")) + self.assertTrue(StringType("hello").does_not_start_with("hey")) + self.assertTrue(StringType("hello").does_not_start_with("He")) + def test_string_ends_with(self): self.assertTrue(StringType("hello").ends_with("lo")) self.assertFalse(StringType("hello").ends_with("boom")) self.assertFalse(StringType("hello").ends_with("Lo")) + def test_string_does_not_end_with(self): + self.assertFalse(StringType("hello").does_not_end_with("lo")) + self.assertTrue(StringType("hello").does_not_end_with("boom")) + self.assertTrue(StringType("hello").does_not_end_with("Lo")) + def test_string_contains(self): self.assertTrue(StringType("hello").contains("ell")) self.assertTrue(StringType("hello").contains("he")) @@ -37,10 +56,21 @@ def test_string_contains(self): self.assertFalse(StringType("hello").contains("asdf")) self.assertFalse(StringType("hello").contains("ElL")) + def test_string_does_not_contain(self): + self.assertFalse(StringType("hello").does_not_contain("ell")) + self.assertFalse(StringType("hello").does_not_contain("he")) + self.assertFalse(StringType("hello").does_not_contain("lo")) + self.assertTrue(StringType("hello").does_not_contain("asdf")) + self.assertTrue(StringType("hello").does_not_contain("ElL")) + def test_string_matches_regex(self): self.assertTrue(StringType("hello").matches_regex(r"^h")) self.assertFalse(StringType("hello").matches_regex(r"^sh")) + def test_string_does_not_match_regex(self): + self.assertFalse(StringType("hello").does_not_match_regex(r"^h")) + self.assertTrue(StringType("hello").does_not_match_regex(r"^sh")) + def test_non_empty(self): self.assertTrue(StringType("hello").non_empty()) self.assertFalse(StringType("").non_empty())