Skip to content
This repository has been archived by the owner on Mar 24, 2021. It is now read-only.

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
In this commit I've changed Query so that it still works in terms of a
delta that can be positive or negative. This is calculated form a
duration in the Query.create method. The reason for having a difference
between the exposed interface and the one used internally is that it's
a lot easier to work with a delta that describes the direction movement
once we've calculated the start/end dates. We are calculating the start_at
and end_at when creating the Query object. This means that we had no
way of determining whether the delta was positive or negative once we've
created the Query.
  • Loading branch information
robyoung committed Jan 28, 2014
1 parent 25e7617 commit d734269
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 49 deletions.
21 changes: 9 additions & 12 deletions backdrop/read/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,22 +58,23 @@ def parse_filter_by(filter_by):

_Query = namedtuple(
'_Query',
['start_at', 'end_at', 'duration', 'period',
['start_at', 'end_at', 'delta', 'period',
'filter_by', 'group_by', 'sort_by', 'limit', 'collect'])


class Query(_Query):
@classmethod
def create(cls,
start_at=None, end_at=None, duration=None,
start_at=None, end_at=None, duration=None, delta=None,
period=None, filter_by=None, group_by=None,
sort_by=None, limit=None, collect=None):
delta = None
if duration is not None:
date = start_at or end_at or now()
delta = duration if start_at else -duration
start_at, end_at = cls.__calculate_start_and_end(period, date,
delta)
return Query(start_at, end_at, duration, period,
return Query(start_at, end_at, delta, period,
filter_by or [], group_by, sort_by, limit, collect or [])

@classmethod
Expand Down Expand Up @@ -101,14 +102,10 @@ def __skip_blank_periods(self, results, repository):

def get_shifted_query(self, shift):
"""Return a new Query where the date is shifted by n periods"""
if self.start_at:
new_date = self.start_at + (self.period.delta * shift)
args = self._asdict()
args['start_at'] = new_date
elif self.end_at:
new_date = self.end_at - (self.period.delta * shift)
args = self._asdict()
args['end_at'] = new_date
args = self._asdict()

args['start_at'] = args['start_at'] + (self.period.delta * shift)
args['end_at'] = args['end_at'] + (self.period.delta * shift)

return Query.create(**args)

Expand All @@ -134,7 +131,7 @@ def execute(self, repository):
else:
result = self.__execute_query(repository)

if self.duration:
if self.delta:
result = self.__skip_blank_periods(result, repository)

return result
Expand Down
2 changes: 1 addition & 1 deletion backdrop/read/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def first_nonempty(data, is_reversed):

first_nonempty_index = next(
(i for i, d in enumerate(data) if d['_count'] > 0),
None)
0)

if is_reversed:
first_nonempty_index = -first_nonempty_index
Expand Down
29 changes: 16 additions & 13 deletions backdrop/read/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,17 +71,19 @@ def validate(self, request_args, context):

class PeriodQueryValidator(Validator):
def validate(self, request_args, context):
if 'period' in request_args:
if 'duration' not in request_args:
if 'start_at' not in request_args or \
'end_at' not in request_args:
self.add_error("both 'start_at' and 'end_at' are required "
"for a period query")
if 'group_by' not in request_args and 'limit' in request_args:
# When executing a grouped periodic query, the limit is
# applied to the list of groups rather than the time series
# inside them
self.add_error("A grouped period query cannot be limited")
if 'period' not in request_args:
return

if 'duration' not in request_args:
if 'start_at' not in request_args or 'end_at' not in request_args:
self.add_error("Either 'duration' or both 'start_at' and "
"'end_at' are required for a period query")

if 'group_by' not in request_args and 'limit' in request_args:
# When executing a grouped periodic query, the limit is
# applied to the list of groups rather than the time series
# inside them
self.add_error("A grouped period query cannot be limited")


class PositiveIntegerValidator(Validator):
Expand Down Expand Up @@ -288,8 +290,8 @@ def validate(self, request_args, context):
if duration == '0':
self.add_error("'duration' must not be zero")
if not period:
self.add_error("If 'duration' is requested (for relative time), "
"'period' is required")
self.add_error("If 'duration' is requested (for relative "
"time), 'period' is required")
try:
int(duration)
except ValueError:
Expand All @@ -312,6 +314,7 @@ def validate_request_args(request_args, raw_queries_allowed=False):
SortByValidator(request_args),
GroupByValidator(request_args),
PositiveIntegerValidator(request_args, param_name='limit'),
PositiveIntegerValidator(request_args, param_name='duration'),
ParamDependencyValidator(request_args, param_name='collect',
depends_on=['group_by', 'period']),
RelativeTimeValidator(request_args),
Expand Down
32 changes: 16 additions & 16 deletions features/read_api/relative_date_ranges.feature
Original file line number Diff line number Diff line change
@@ -1,44 +1,44 @@
@use_read_api_client
Feature: relative date queries for read api

Scenario: querying for periodic data when given one point and a positive delta
Scenario: querying for periodic data from the start_at
Given "licensing_2.json" is in "foo" bucket
when I go to "/foo?start_at=2012-12-12T00:00:00%2B00:00&delta=2&period=day"
when I go to "/foo?start_at=2012-12-12T00:00:00%2B00:00&duration=2&period=day"
then I should get back a status of "200"
and the JSON should have "2" results
and the "1st" result should be "{"_start_at": "2012-12-12T00:00:00+00:00", "_end_at": "2012-12-13T00:00:00+00:00", "_count": 2.0}"
and the "2nd" result should be "{"_start_at": "2012-12-13T00:00:00+00:00", "_end_at": "2012-12-14T00:00:00+00:00", "_count": 1.0}"

Scenario: querying for periodic data when given one point and a negative delta
Scenario: querying for periodic data from the end_at
Given "licensing_2.json" is in "foo" bucket
when I go to "/foo?start_at=2012-12-14T00:00:00%2B00:00&delta=-2&period=day"
when I go to "/foo?end_at=2012-12-14T00:00:00%2B00:00&duration=2&period=day"
then I should get back a status of "200"
and the JSON should have "2" results
and the "1st" result should be "{"_start_at": "2012-12-12T00:00:00+00:00", "_end_at": "2012-12-13T00:00:00+00:00", "_count": 2.0}"
and the "2nd" result should be "{"_start_at": "2012-12-13T00:00:00+00:00", "_end_at": "2012-12-14T00:00:00+00:00", "_count": 1.0}"

Scenario: querying for periodic data when given one point, a positive delta and first results are empty
Scenario: querying for periodic data from the start_at where the first results are empty
Given "licensing_2.json" is in "foo" bucket
when I go to "/foo?start_at=2012-11-01T00:00:00%2B00:00&delta=10&period=week"
when I go to "/foo?start_at=2012-11-05T00:00:00%2B00:00&duration=10&period=week"
then I should get back a status of "200"
and the JSON should have "10" results
and the "1st" result should be "{"_start_at": "2012-12-03T00:00:00+00:00", "_end_at": "2012-12-10T00:00:00+00:00", "_count": 1.0}"
and the "2nd" result should be "{"_start_at": "2012-12-10T00:00:00+00:00", "_end_at": "2012-12-17T00:00:00+00:00", "_count": 4.0}"
and the "3rd" result should be "{"_start_at": "2012-12-17T00:00:00+00:00", "_end_at": "2012-12-24T00:00:00+00:00", "_count": 0.0}"

Scenario: querying for periodic data when given one point, a negative delta and first results are empty
Scenario: querying for periodic data from the end_at where the first results are empty
Given "licensing_2.json" is in "foo" bucket
when I go to "/foo?start_at=2013-02-01T00:00:00%2B00:00&delta=-10&period=week"
when I go to "/foo?end_at=2013-02-04T00:00:00%2B00:00&duration=10&period=week"
then I should get back a status of "200"
and the JSON should have "10" results
and the "10th" result should be "{"_start_at": "2012-12-10T00:00:00+00:00", "_end_at": "2012-12-17T00:00:00+00:00", "_count": 4.0}"
and the "9th" result should be "{"_start_at": "2012-12-03T00:00:00+00:00", "_end_at": "2012-12-10T00:00:00+00:00", "_count": 1.0}"
and the "8th" result should be "{"_start_at": "2012-11-26T00:00:00+00:00", "_end_at": "2012-12-03T00:00:00+00:00", "_count": 0.0}"


Scenario: querying for grouped periodic data when given a positive delta
Scenario: querying for grouped periodic data from the start_at
Given "licensing_2.json" is in "foo" bucket
when I go to "/foo?start_at=2012-12-12T00:00:00%2B00:00&delta=2&period=day&group_by=authority"
when I go to "/foo?start_at=2012-12-12T00:00:00%2B00:00&duration=2&period=day&group_by=authority"
then I should get back a status of "200"
and the JSON should have "2" results
and the "1st" result should have "authority" equaling "Camden"
Expand All @@ -49,9 +49,9 @@ Feature: relative date queries for read api
and the "2nd" result should have "values" with item "{"_start_at": "2012-12-13T00:00:00+00:00", "_end_at": "2012-12-14T00:00:00+00:00", "_count": 1.0}"


Scenario: querying for grouped periodic data when given a negative delta
Scenario: querying for grouped periodic data from the end_at
Given "licensing_2.json" is in "foo" bucket
when I go to "/foo?start_at=2012-12-14T00:00:00%2B00:00&delta=-2&period=day&group_by=authority"
when I go to "/foo?end_at=2012-12-14T00:00:00%2B00:00&duration=2&period=day&group_by=authority"
then I should get back a status of "200"
and the JSON should have "2" results
and the "1st" result should have "authority" equaling "Camden"
Expand All @@ -62,9 +62,9 @@ Feature: relative date queries for read api
and the "2nd" result should have "values" with item "{"_start_at": "2012-12-13T00:00:00+00:00", "_end_at": "2012-12-14T00:00:00+00:00", "_count": 1.0}"


Scenario: querying for grouped periodic data when given a positive delta and first results are empty
Scenario: querying for grouped periodic data from the start_at where the first results are empty
Given "licensing_2.json" is in "foo" bucket
when I go to "/foo?start_at=2012-12-10T00:00:00%2B00:00&delta=4&period=day&group_by=authority"
when I go to "/foo?start_at=2012-12-10T00:00:00%2B00:00&duration=4&period=day&group_by=authority"
then I should get back a status of "200"
and the JSON should have "2" results
and the "1st" result should have "authority" equaling "Camden"
Expand All @@ -73,9 +73,9 @@ Feature: relative date queries for read api
and the "2nd" result should have "_count" equaling the integer "3"


Scenario: querying for grouped periodic data when given a negative delta and first results are empty
Scenario: querying for grouped periodic data from the end_at where the first results are empty
Given "licensing_2.json" is in "foo" bucket
when I go to "/foo?start_at=2012-12-16T00:00:00%2B00:00&delta=-2&period=day&group_by=authority"
when I go to "/foo?end_at=2012-12-16T00:00:00%2B00:00&duration=2&period=day&group_by=authority"
then I should get back a status of "200"
and the JSON should have "1" results
and the "1st" result should have "authority" equaling "Westminster"
Expand Down
7 changes: 4 additions & 3 deletions tests/read/test_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,14 +343,15 @@ def test_period_with_just_positive_duration(self):
})

assert_that(validation_result, is_valid())

def test_negative_dutaion_isnt_allowed(self):
validation_result = validate_request_args({
'period': 'day',
'duration': '-3',
})

assert_that(validation_result, is_valid())
assert_that(validation_result, is_invalid_with_message(
"duration must be a positive integer"))

def test_period_with_start_at_and_duration(self):
validation_result = validate_request_args({
Expand Down Expand Up @@ -413,7 +414,7 @@ def test_duration_is_a_valid_number(self):
})

assert_that(validation_result, is_invalid_with_message(
"'duration' is not a valid Integer"))
"duration must be a positive integer"))


class TestRequestValidationWithNoRawQueries(TestCase):
Expand Down
8 changes: 4 additions & 4 deletions tests/read/test_validation_of_queries_accessing_raw_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,8 @@ def test_that_start_at_alone_is_disallowed(self):
})
assert_that(validation_result,
is_invalid_with_message(
"both 'start_at' and 'end_at' are required for "
"a period query"))
"Either 'duration' or both 'start_at' and 'end_at' "
"are required for a period query"))

def test_that_end_at_alone_is_disallowed(self):
validation_result = validate_request_args({
Expand All @@ -138,8 +138,8 @@ def test_that_end_at_alone_is_disallowed(self):
})
assert_that(validation_result,
is_invalid_with_message(
"both 'start_at' and 'end_at' are required for "
"a period query"))
"Either 'duration' or both 'start_at' and 'end_at' "
"are required for a period query"))

def test_that_grouping_by_month_requires_dates_at_start_of_month(self):
validation_result = validate_request_args({
Expand Down

0 comments on commit d734269

Please sign in to comment.