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
  • Loading branch information
nick-gravgaard committed Jan 28, 2014
1 parent 2ae217d commit e8bb5fc
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 114 deletions.
31 changes: 16 additions & 15 deletions backdrop/read/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,7 @@ def parse_request_args(request_args):
args['end_at'] = if_present(parse_time_as_utc,
request_args.get('end_at'))

args['delta'] = if_present(int, request_args.get('delta'))

args['date'] = if_present(parse_time_as_utc,
request_args.get('date'))
args['duration'] = if_present(int, request_args.get('duration'))

args['period'] = if_present(parse_period,
request_args.get('period'))
Expand Down Expand Up @@ -61,20 +58,22 @@ def parse_filter_by(filter_by):

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


class Query(_Query):
@classmethod
def create(cls,
start_at=None, end_at=None, date=None, delta=None,
start_at=None, end_at=None, duration=None,
period=None, filter_by=None, group_by=None,
sort_by=None, limit=None, collect=None):
if delta is not 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, date, delta, period,
return Query(start_at, end_at, duration, period,
filter_by or [], group_by, sort_by, limit, collect or [])

@classmethod
Expand All @@ -84,8 +83,6 @@ def parse(cls, request_args):

@staticmethod
def __calculate_start_and_end(period, date, delta):
date = date or now()

duration = period.delta * delta
start_of_period = period.start(date)

Expand All @@ -104,10 +101,14 @@ 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"""
new_date = self.date + (self.period.delta * shift)

args = self._asdict()
args['date'] = new_date
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

return Query.create(**args)

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

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

return result
Expand Down
36 changes: 19 additions & 17 deletions backdrop/read/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ def __init__(self, request_args):
self.allowed_parameters = set([
'start_at',
'end_at',
'date',
'delta',
'duration',
'period',
'filter_by',
'group_by',
Expand Down Expand Up @@ -73,7 +72,7 @@ def validate(self, request_args, context):
class PeriodQueryValidator(Validator):
def validate(self, request_args, context):
if 'period' in request_args:
if 'delta' not 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 "
Expand Down Expand Up @@ -269,29 +268,32 @@ def validate(self, request_args, context):
class RelativeTimeValidator(Validator):
def validate(self, request_args, context):

start_at = request_args.get('start_at')
end_at = request_args.get('end_at')
period = request_args.get('period')
date = request_args.get('date')
delta = request_args.get('delta')
duration = request_args.get('duration')

if (request_args.get('start_at') or request_args.get('end_at')) \
and (delta or date):
if start_at and end_at and duration:
self.add_error("Absolute ('start_at' and 'end_at') and relative "
"('delta' and/or 'date') time cannot be requested "
"at the same time")
"('duration') time cannot be requested at the "
"same time")

if date and not delta:
self.add_error("Use of 'date' requires 'delta'")
if start_at and end_at is None and duration is None:
self.add_error("Use of 'start_at' requires 'end_at' or 'duration'")

if delta:
if delta == '0':
self.add_error("'delta' must not be zero")
if end_at and start_at is None and duration is None:
self.add_error("Use of 'end_at' requires 'start_at' or 'duration'")

if duration:
if duration == '0':
self.add_error("'duration' must not be zero")
if not period:
self.add_error("If 'delta' is requested (for relative time), "
self.add_error("If 'duration' is requested (for relative time), "
"'period' is required")
try:
int(delta)
int(duration)
except ValueError:
self.add_error("'delta' is not a valid Integer")
self.add_error("'duration' is not a valid Integer")


def validate_request_args(request_args, raw_queries_allowed=False):
Expand Down
16 changes: 8 additions & 8 deletions features/read_api/relative_date_ranges.feature
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@ Feature: relative date queries for read api

Scenario: querying for periodic data when given one point and a positive delta
Given "licensing_2.json" is in "foo" bucket
when I go to "/foo?date=2012-12-12T01:01:02%2B00:00&delta=2&period=day"
when I go to "/foo?start_at=2012-12-12T00:00:00%2B00:00&delta=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
Given "licensing_2.json" is in "foo" bucket
when I go to "/foo?date=2012-12-14T01:01:02%2B00:00&delta=-2&period=day"
when I go to "/foo?start_at=2012-12-14T00:00:00%2B00:00&delta=-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
Given "licensing_2.json" is in "foo" bucket
when I go to "/foo?date=2012-11-01T01:01:02%2B00:00&delta=10&period=week"
when I go to "/foo?start_at=2012-11-01T00:00:00%2B00:00&delta=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}"
Expand All @@ -28,7 +28,7 @@ Feature: relative date queries for read api

Scenario: querying for periodic data when given one point, a negative delta and first results are empty
Given "licensing_2.json" is in "foo" bucket
when I go to "/foo?date=2013-02-01T01:01:02%2B00:00&delta=-10&period=week"
when I go to "/foo?start_at=2013-02-01T00:00:00%2B00:00&delta=-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}"
Expand All @@ -38,7 +38,7 @@ Feature: relative date queries for read api

Scenario: querying for grouped periodic data when given a positive delta
Given "licensing_2.json" is in "foo" bucket
when I go to "/foo?date=2012-12-12T01:01:02%2B00:00&delta=2&period=day&group_by=authority"
when I go to "/foo?start_at=2012-12-12T00:00:00%2B00:00&delta=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 @@ -51,7 +51,7 @@ Feature: relative date queries for read api

Scenario: querying for grouped periodic data when given a negative delta
Given "licensing_2.json" is in "foo" bucket
when I go to "/foo?date=2012-12-14T01:01:02%2B00:00&delta=-2&period=day&group_by=authority"
when I go to "/foo?start_at=2012-12-14T00:00:00%2B00:00&delta=-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 @@ -64,7 +64,7 @@ Feature: relative date queries for read api

Scenario: querying for grouped periodic data when given a positive delta and first results are empty
Given "licensing_2.json" is in "foo" bucket
when I go to "/foo?date=2012-12-10T01:01:02%2B00:00&delta=4&period=day&group_by=authority"
when I go to "/foo?start_at=2012-12-10T00:00:00%2B00:00&delta=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 @@ -75,7 +75,7 @@ Feature: relative date queries for read api

Scenario: querying for grouped periodic data when given a negative delta and first results are empty
Given "licensing_2.json" is in "foo" bucket
when I go to "/foo?date=2012-12-16T01:01:02%2B00:00&delta=-2&period=day&group_by=authority"
when I go to "/foo?start_at=2012-12-16T00:00:00%2B00:00&delta=-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
54 changes: 14 additions & 40 deletions tests/read/test_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,20 @@ def test_build_query_with_start_and_end_at(self):
}))

@freeze_time('2014, 1, 09 00:00:00')
def test_no_date_means_now(self):
def test_no_end_at_means_now(self):
query = Query.create(
period=Day(),
delta=3,
duration=3,
)

assert_that(query.start_at, is_(
assert_that(query.end_at, is_(
datetime(2014, 1, 9, 0, 0, 0, tzinfo=pytz.UTC)))

def test_date_on_boundary_and_positive_delta(self):
def test_start_at_and_duration(self):
query = Query.create(
date=d_tz(2014, 1, 9, 0, 0, 0),
start_at=d_tz(2014, 1, 9, 0, 0, 0),
period=Day(),
delta=3,
duration=3,
)

assert_that(query.start_at, is_(
Expand All @@ -54,11 +54,11 @@ def test_date_on_boundary_and_positive_delta(self):
assert_that(query.end_at, is_(
datetime(2014, 1, 12, 0, 0, 0, tzinfo=pytz.UTC)))

def test_date_on_boundary_and_negative_delta(self):
def test_end_at_and_duration(self):
query = Query.create(
date=d_tz(2014, 1, 11, 0, 0, 0),
end_at=d_tz(2014, 1, 11, 0, 0, 0),
period=Day(),
delta=-3,
duration=3,
)

assert_that(query.start_at, is_(
Expand All @@ -67,37 +67,11 @@ def test_date_on_boundary_and_negative_delta(self):
assert_that(query.end_at, is_(
datetime(2014, 1, 11, 0, 0, 0, tzinfo=pytz.UTC)))

def test_date_off_boundary_and_positive_delta(self):
query = Query.create(
date=d_tz(2014, 1, 9, 1, 2, 3),
period=Day(),
delta=3,
)

assert_that(query.start_at, is_(
datetime(2014, 1, 9, 0, 0, 0, tzinfo=pytz.UTC)))

assert_that(query.end_at, is_(
datetime(2014, 1, 12, 0, 0, 0, tzinfo=pytz.UTC)))

def test_date_off_boundary_and_negative_delta(self):
query = Query.create(
date=d_tz(2014, 1, 11, 23, 58, 57),
period=Day(),
delta=-3,
)

assert_that(query.start_at, is_(
d_tz(2014, 1, 8, 0, 0, 0)))

assert_that(query.end_at, is_(
d_tz(2014, 1, 11, 0, 0, 0)))

def test_shift_query_forward(self):
def test_shift_query_forwards(self):
query = Query.create(
date=d_tz(2014, 1, 9, 0, 0, 0),
start_at=d_tz(2014, 1, 9, 0, 0, 0),
period=Day(),
delta=6,
duration=6,
)

shifted = query.get_shifted_query(5)
Expand All @@ -110,9 +84,9 @@ def test_shift_query_forward(self):

def test_shift_query_backwards(self):
query = Query.create(
date=d_tz(2014, 1, 9, 0, 0, 0),
start_at=d_tz(2014, 1, 9, 0, 0, 0),
period=Day(),
delta=6,
duration=6,
)

shifted = query.get_shifted_query(-5)
Expand Down
43 changes: 9 additions & 34 deletions tests/read/test_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,10 +352,10 @@ def test_period_with_just_negative_delta(self):

assert_that(validation_result, is_valid())

def test_period_with_delta_and_date(self):
def test_period_with_start_at_and_delta(self):
validation_result = validate_request_args({
'period': 'day',
'date': '2000-02-02T00:00:00+00:00',
'start_at': '2000-02-02T00:00:00+00:00',
'delta': '3',
})

Expand All @@ -379,57 +379,32 @@ def test_period_with_zero_delta(self):
assert_that(validation_result, is_invalid_with_message(
"'delta' must not be zero"))

def test_just_date_isnt_allowed(self):
validation_result = validate_request_args({
'date': '2000-02-02T00:00:00+00:00',
})

assert_that(validation_result, is_invalid_with_message(
"Use of 'date' requires 'delta'"))

def test_delta_and_start_end_isnt_allowed(self):
def test_just_start_at_isnt_allowed(self):
validation_result = validate_request_args({
'start_at': '2000-02-02T00:00:00+00:00',
'end_at': '2000-02-02T00:00:00+00:00',
'delta': '3',
})

assert_that(validation_result, is_invalid_with_message(
"Absolute ('start_at' and 'end_at') and relative ('delta' "
"and/or 'date') time cannot be requested at the same time"))
"Use of 'start_at' requires 'end_at' or 'delta'"))

def test_date_and_start_end_isnt_allowed(self):
def test_just_end_at_isnt_allowed(self):
validation_result = validate_request_args({
'start_at': '2000-02-02T00:00:00+00:00',
'end_at': '2000-02-02T00:00:00+00:00',
'date': '2000-02-02T00:00:00+00:00',
})

assert_that(validation_result, is_invalid_with_message(
"Absolute ('start_at' and 'end_at') and relative ('delta' "
"and/or 'date') time cannot be requested at the same time"))
"Use of 'end_at' requires 'start_at' or 'delta'"))

def test_date_delta_and_start_end_isnt_allowed(self):
def test_delta_and_start_end_isnt_allowed(self):
validation_result = validate_request_args({
'start_at': '2000-02-02T00:00:00+00:00',
'end_at': '2000-02-02T00:00:00+00:00',
'date': '2000-02-02T00:00:00+00:00',
'delta': '3',
})

assert_that(validation_result, is_invalid_with_message(
"Absolute ('start_at' and 'end_at') and relative ('delta' "
"and/or 'date') time cannot be requested at the same time"))

def test_date_is_a_valid_date(self):
validation_result = validate_request_args({
'period': 'day',
'date': 'leirguherag',
'delta': '3',
})

assert_that(validation_result, is_invalid_with_message(
'date is not a valid datetime'))
"Absolute ('start_at' and 'end_at') and relative ('delta') time "
"cannot be requested at the same time"))

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

0 comments on commit e8bb5fc

Please sign in to comment.