Skip to content

Commit

Permalink
Merge pull request #27 from fedora-infra/feature/formattable-operations
Browse files Browse the repository at this point in the history
Feature/formattable operations
  • Loading branch information
ralphbean committed Feb 14, 2014
2 parents d21a528 + abf5700 commit d22581c
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 3 deletions.
36 changes: 33 additions & 3 deletions fedbadges/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,20 +368,50 @@ def __init__(self, *args, **kwargs):
self.condition = functools.partial(
self.condition_callbacks[condition_key], condition_val)

def construct_query(self, msg):
def _construct_query(self, msg):
""" Construct a datanommer query for this message.
The "filter" section of this criteria object will be used. It will
first be formatted with any substitutions present in the incoming
message. This is used, for instance, to construct a query like "give
me all the messages bearing the same topic as the message that just
arrived".
"""
subs = construct_substitutions(msg)
kwargs = format_args(copy.copy(self._d['filter']), subs)
kwargs = recursive_lambda_factory(kwargs, msg, name='msg')
kwargs['defer'] = True
total, pages, query = datanommer.models.Message.grep(**kwargs)
return total, pages, query

def _format_lambda_operation(self, msg):
""" Format the string representation of a lambda operation.
The lambda operation can be formatted here to include strings that
appear in the message being evaluated like
%(msg.comment.update_submitter)s. Placeholders like that will have
their value substituted with whatever appears in the incoming message.
"""
subs = construct_substitutions(msg)
operation = format_args(copy.copy(self._d['operation']), subs)
return operation['lambda']

def matches(self, msg):
total, pages, query = self.construct_query(msg)
""" A datanommer criteria check is composed of three steps.
- A datanommer query is constructed by combining our yaml definition
with the incoming fedmsg message that triggered us.
- An operation in python is constructed by comining our yaml definition
with the incoming fedmsg message that triggered us. That operation
is then executed against the datanommer query object.
- A condition, derived from our yaml definition, is evaluated with the
result of the operation from the previous step and is returned.
"""
total, pages, query = self._construct_query(msg)
if self._d['operation'] == 'count':
result = total
elif isinstance(self._d['operation'], dict):
expression = self._d['operation']['lambda']
expression = self._format_lambda_operation(msg)
result = single_argument_lambda_factory(
expression=expression, argument=query, name='query')
else:
Expand Down
56 changes: 56 additions & 0 deletions tests/test_criteria.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,62 @@ def test_datanommer_with_lambda_condition_query_overshoot(self):
eq_(result, expectation)


class TestCriteriaLambdaOperationWithFormatting(unittest.TestCase):
def setUp(self):
self.criteria = fedbadges.rules.Criteria(dict(
datanommer={
"filter": {
"topics": ["%(topic)s"],
},
"operation": {
"lambda": "query.count() == %(msg.some_value)s",
},
"condition": {
"lambda": "value",
}
}
))
self.message = dict(
topic="org.fedoraproject.dev.something.sometopic",
msg=dict(
some_value=5,
),
)

class MockQuery(object):
def count(query):
return self.returned_count

self.mock_query = MockQuery()

def test_datanommer_formatted_operations_undershoot(self):
self.returned_count = 4
expectation = False

with mock.patch('datanommer.models.Message.grep') as f:
f.return_value = self.returned_count, 1, self.mock_query
result = self.criteria.matches(self.message)
eq_(result, expectation)

def test_datanommer_formatted_operations_overshoot(self):
self.returned_count = 6
expectation = False

with mock.patch('datanommer.models.Message.grep') as f:
f.return_value = self.returned_count, 1, self.mock_query
result = self.criteria.matches(self.message)
eq_(result, expectation)

def test_datanommer_formatted_operations_righton(self):
self.returned_count = 5
expectation = True

with mock.patch('datanommer.models.Message.grep') as f:
f.return_value = self.returned_count, 1, self.mock_query
result = self.criteria.matches(self.message)
eq_(result, expectation)


class TestCriteriaLambdaOperationsAndConditions(unittest.TestCase):
def setUp(self):
self.criteria = fedbadges.rules.Criteria(dict(
Expand Down

0 comments on commit d22581c

Please sign in to comment.