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

Feature/formattable operations #27

Merged
merged 3 commits into from
Feb 14, 2014
Merged
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
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