Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Return Fired rules actions results. #34

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions business_rules/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
__version__ = '1.0.1'

from .engine import run_all
from .engine import run_all_with_results
from .utils import export_rule_data

# Appease pyflakes by "using" these exports
assert run_all
assert run_all_with_results
assert export_rule_data
61 changes: 57 additions & 4 deletions business_rules/engine.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from .fields import FIELD_NO_INPUT


def run_all(rule_list,
defined_variables,
defined_actions,
stop_on_first_trigger=False):

rule_was_triggered = False
for rule in rule_list:
result = run(rule, defined_variables, defined_actions)
Expand All @@ -14,6 +14,40 @@ def run_all(rule_list,
return True
return rule_was_triggered


def run_all_with_results(rule_list, defined_variables, defined_actions,
stop_on_first_trigger=False):
'''Run all Rules and return the rules actions results
Returns:
rule_results(list): list of dictionaries. Each dictionary is a rule
actions' results
'''
rule_results = []
for rule in rule_list:
actionsResults = run_and_get_results(rule, defined_variables,
defined_actions)
if actionsResults:
rule_results.append(actionsResults)
if stop_on_first_trigger:
return rule_results
return rule_results


def run_and_get_results(rule, defined_variables, defined_actions):
'''Run the rule and get the action returned result
Attributes:
rule(dict): the rule dictionary
defined_variables(BaseVariables): the defined set of variables object
defined_actions(BaseActions): the actions object
'''
actions_results = {}
conditions, actions = rule.get('conditions'), rule.get('actions')
if conditions and actions:
rule_triggered = check_conditions_recursively(conditions, defined_variables)
if rule_triggered:
actions_results = do_actions(actions, defined_actions)
return actions_results

def run(rule, defined_variables, defined_actions):
conditions, actions = rule['conditions'], rule['actions']
rule_triggered = check_conditions_recursively(conditions, defined_variables)
Expand Down Expand Up @@ -68,6 +102,7 @@ def fallback(*args, **kwargs):
val = method()
return method.field_type(val)


def _do_operator_comparison(operator_type, operator_name, comparison_value):
""" Finds the method on the given operator_type and compares it to the
given comparison_value.
Expand All @@ -86,11 +121,29 @@ def fallback(*args, **kwargs):


def do_actions(actions, defined_actions):
''' Run the actions
Attributes:
actions(list): list of dictionaries of actions. e.g: [
{ "name": "put_on_sale",
"params": {"sale_percentage": 0.25},
}
]
Returns:
actionsResults(dict): Dictionary of actions results
e.g: {"put_on_sale: [product1, product2, ...]}
'''
actions_results = {}
for action in actions:
method_name = action['name']

def fallback(*args, **kwargs):
raise AssertionError("Action {0} is not defined in class {1}"\
.format(method_name, defined_actions.__class__.__name__))
raise AssertionError(
"Action {0} is not defined in class {1}".format(method_name,
defined_actions.__class__.__name__))

params = action.get('params') or {}
method = getattr(defined_actions, method_name, fallback)
method(**params)
actions_results[method_name] = method(**params)

return dict((method_name, actions_results[method_name]) for method_name
in actions_results.keys() if actions_results.get(method_name))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if None is expected result from method? you should not filter None value

61 changes: 61 additions & 0 deletions tests/test_get_actions_results.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from business_rules.actions import BaseActions, rule_action
from business_rules.fields import FIELD_TEXT
from business_rules.variables import BaseVariables, boolean_rule_variable
from business_rules.engine import run_all_with_results
from . import TestCase


class ActionsResultsClassTests(TestCase):
""" Test methods on getting fired rules actions results
"""
def test_get_actions_results(self):
class SomeVariables(BaseVariables):
@boolean_rule_variable
def this_is_rule_1(self):
return True

@boolean_rule_variable
def this_is_rule_2(self):
return False

class SomeActions(BaseActions):

@rule_action(params={'foo':FIELD_TEXT})
def some_action_1(self, foo):
return foo

@rule_action(params={'foobar':FIELD_TEXT})
def some_action_2(self, foobar):
return foobar

@rule_action()
def some_action_3(self):
pass

rule1 = {'conditions': {'all': [
{
'name': 'this_is_rule_1',
'value': True,
'operator': 'is_true'
}]},
'actions': [
{'name': 'some_action_1',
'params': {'foo': 'fooValue'}
}]}
rule2 = {'conditions': {'all': [
{
'name': 'this_is_rule_2',
'value': True,
'operator': 'is_false'
}]},
'actions': [
{'name': 'some_action_2',
'params': {'foobar': 'foobarValue'}
},
{'name': 'some_action_3'
}]}

variables = SomeVariables()
actions = SomeActions()
result = run_all_with_results([rule1, rule2], variables, actions)
self.assertEqual(result, [{'some_action_1': 'fooValue'}, {'some_action_2': 'foobarValue'}])