Drop the active reports API route and use API filtering abilities
instead Fix for #49.
This commit is contained in:
parent
0f03700f71
commit
4345c3f4e3
@ -23,6 +23,8 @@ versions, meaning that the `0.3.py` script handles the migration of the
|
||||
database from the version immediately before `0.3` to the `0.3` version of the
|
||||
app.
|
||||
|
||||
**Always** make a backup of your database prior to running a migration on it.
|
||||
|
||||
If you upgrade through several versions at once, you should run all the
|
||||
migrations scripts for all the intermediate versions, in the ascending order.
|
||||
There are currently no automated way to handle the updates of the database
|
||||
|
@ -56,11 +56,11 @@ If you want to make an `OR` condition, satisfying either one of the filters,
|
||||
you should make two different API calls.
|
||||
|
||||
|
||||
#### Commplex filtering
|
||||
#### Complex filtering
|
||||
|
||||
You can also use more complex elaborations through the syntax
|
||||
`filter[FIELD][OPERATION]=VALUE`. The available operations are `eq` (equal,
|
||||
default operation), `ne` (not equal), `gt` (greater than), `ge` (greater or
|
||||
`filter[FIELD][OPERATOR]=VALUE`. The available operators are `eq` (equal,
|
||||
default operator), `ne` (not equal), `gt` (greater than), `ge` (greater or
|
||||
equal), `lt` (lower than) and `le` (lower or equal). For instance,
|
||||
|
||||
```
|
||||
@ -75,3 +75,14 @@ reports in a geographical bounding box:
|
||||
```
|
||||
> GET /api/v1/reports?filter[lat][gt]=LAT_MIN&filter[lat][lt]=LAT_MAX&filter[lng][gt]=LNG_MIN&filter[lng][lt]=LNG_MAX
|
||||
```
|
||||
|
||||
All operators can have a trailing `?` to make them consider fields without
|
||||
value as matching the condition as well. This means you can use something like
|
||||
|
||||
```
|
||||
> /api/v1/reports?filter[expiration_datetime][gt?]=2018-10-18T09:06:38.538375
|
||||
```
|
||||
|
||||
to get the all the reports with an expiration datetime in the future or no
|
||||
expiration datetime, that is all currently active reports, assuming the
|
||||
current datetime is `2018-10-18T09:06:38.538375`.
|
||||
|
@ -29,6 +29,7 @@ def run_migration():
|
||||
migrate(
|
||||
migrator.add_column('report', 'first_report_datetime',
|
||||
peewee.DateTimeField(default=UTC_now)),
|
||||
migrator.drop_column('report', 'is_open'),
|
||||
)
|
||||
query = Report.select()
|
||||
for report in query:
|
||||
|
@ -10,7 +10,7 @@ import re
|
||||
|
||||
import bottle
|
||||
|
||||
FILTER_RE = re.compile(r"filter\[([A-z0-9_]+?)\](\[([A-z0-9_]+)\])?")
|
||||
FILTER_RE = re.compile(r"filter\[([A-z0-9_]+?)\](\[([A-z0-9_]+\??)\])?")
|
||||
|
||||
|
||||
class DateAwareJSONEncoder(json.JSONEncoder):
|
||||
@ -76,21 +76,36 @@ def JsonApiParseQuery(query, model, default_sorting=None):
|
||||
for value in query.getall(param):
|
||||
# Handle operation
|
||||
operation = filter_match.group(3)
|
||||
if operation == 'eq' or operation is None:
|
||||
filters.append(field == value)
|
||||
if operation is None:
|
||||
# Default operation is 'eq'
|
||||
operation = 'eq'
|
||||
|
||||
# Handle '?' modifier
|
||||
modifier_filter = None
|
||||
if operation.endswith('?'):
|
||||
modifier_filter = (field == None)
|
||||
operation = operation.rstrip('?')
|
||||
|
||||
if operation == 'eq':
|
||||
operation_filter = (field == value)
|
||||
elif operation == 'ne':
|
||||
filters.append(field != value)
|
||||
operation_filter = (field != value)
|
||||
elif operation == 'gt':
|
||||
filters.append(field > value)
|
||||
operation_filter = (field > value)
|
||||
elif operation == 'ge':
|
||||
filters.append(field >= value)
|
||||
operation_filter = (field >= value)
|
||||
elif operation == 'lt':
|
||||
filters.append(field < value)
|
||||
operation_filter = (field < value)
|
||||
elif operation == 'le':
|
||||
filters.append(field <= value)
|
||||
operation_filter = (field <= value)
|
||||
else:
|
||||
raise ValueError("Invalid filtering operator provided.")
|
||||
|
||||
if modifier_filter:
|
||||
filters.append(modifier_filter | operation_filter)
|
||||
else:
|
||||
filters.append(operation_filter)
|
||||
|
||||
# Handle pagination according to JSON API spec
|
||||
page_number, page_size = 0, None
|
||||
try:
|
||||
|
@ -48,7 +48,6 @@ class Report(BaseModel):
|
||||
default=UTC_now
|
||||
)
|
||||
expiration_datetime = peewee.DateTimeField(null=True)
|
||||
is_open = peewee.BooleanField(default=True)
|
||||
upvotes = peewee.IntegerField(default=0)
|
||||
downvotes = peewee.IntegerField(default=0)
|
||||
|
||||
|
142
server/routes.py
142
server/routes.py
@ -37,9 +37,34 @@ def check_auth():
|
||||
return
|
||||
|
||||
|
||||
def get_reports(only_active=False):
|
||||
@bottle.route('/api/v1/reports', ["GET", "OPTIONS"])
|
||||
def get_all_reports():
|
||||
"""
|
||||
Get reports for the reports getting routes.
|
||||
API v1 GET reports route. Get all reports.
|
||||
|
||||
Example::
|
||||
|
||||
> GET /api/v1/reports
|
||||
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"attributes": {
|
||||
"expiration_datetime": null,
|
||||
"downvotes": 0,
|
||||
"datetime": "2018-06-27T16:44:12+00:00",
|
||||
"lat": 48.842005,
|
||||
"upvotes": 1,
|
||||
"lng": 2.386278,
|
||||
"type": "interrupt",
|
||||
…
|
||||
},
|
||||
"type": "reports",
|
||||
"id": 1
|
||||
},
|
||||
…
|
||||
]
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
@ -78,11 +103,6 @@ def get_reports(only_active=False):
|
||||
query = Report.select()
|
||||
if filters:
|
||||
query = query.where(*filters)
|
||||
if only_active:
|
||||
query = query.where(
|
||||
(Report.expiration_datetime == None) |
|
||||
(Report.expiration_datetime > UTC_now())
|
||||
)
|
||||
query = query.order_by(*sorting)
|
||||
if page_number and page_size:
|
||||
query = query.paginate(page_number, page_size)
|
||||
@ -95,111 +115,6 @@ def get_reports(only_active=False):
|
||||
}
|
||||
|
||||
|
||||
@bottle.route('/api/v1/reports', ["GET", "OPTIONS"])
|
||||
def get_all_reports():
|
||||
"""
|
||||
API v1 GET reports route. Get all reports.
|
||||
|
||||
Example::
|
||||
|
||||
> GET /api/v1/reports
|
||||
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"attributes": {
|
||||
"expiration_datetime": null,
|
||||
"downvotes": 0,
|
||||
"datetime": "2018-06-27T16:44:12+00:00",
|
||||
"is_open": true,
|
||||
"lat": 48.842005,
|
||||
"upvotes": 1,
|
||||
"lng": 2.386278,
|
||||
"type": "interrupt",
|
||||
…
|
||||
},
|
||||
"type": "reports",
|
||||
"id": 1
|
||||
},
|
||||
…
|
||||
]
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
Filtering can be done through the ``filter`` GET param, according
|
||||
to JSON API spec (http://jsonapi.org/recommendations/#filtering).
|
||||
|
||||
.. note::
|
||||
|
||||
By default no pagination is done. Pagination can be forced using
|
||||
``page[size]`` to specify a number of items per page and
|
||||
``page[number]`` to specify which page to return. Pages are numbered
|
||||
starting from 0.
|
||||
|
||||
.. note::
|
||||
|
||||
Sorting can be handled through the ``sort`` GET param, according to
|
||||
JSON API spec (http://jsonapi.org/format/#fetching-sorting).
|
||||
|
||||
:return: The available reports objects in a JSON ``data`` dict.
|
||||
"""
|
||||
return get_reports(only_active=False)
|
||||
|
||||
|
||||
@bottle.route('/api/v1/reports/active', ["GET", "OPTIONS"])
|
||||
def get_active_reports():
|
||||
"""
|
||||
API v1 GET reports route. Only get active reports (those with
|
||||
``expiration_datetime`` in the future).
|
||||
|
||||
Example::
|
||||
|
||||
> GET /api/v1/reports/active
|
||||
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"attributes": {
|
||||
"expiration_datetime": null,
|
||||
"downvotes": 0,
|
||||
"datetime": "2018-06-27T16:44:12+00:00",
|
||||
"is_open": true,
|
||||
"lat": 48.842005,
|
||||
"upvotes": 1,
|
||||
"lng": 2.386278,
|
||||
"type": "interrupt",
|
||||
…
|
||||
},
|
||||
"type": "reports",
|
||||
"id": 1
|
||||
},
|
||||
…
|
||||
]
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
Filtering can be done through the ``filter`` GET param, according
|
||||
to JSON API spec (http://jsonapi.org/recommendations/#filtering).
|
||||
|
||||
.. note::
|
||||
|
||||
By default no pagination is done. Pagination can be forced using
|
||||
``page[size]`` to specify a number of items per page and
|
||||
``page[number]`` to specify which page to return. Pages are numbered
|
||||
starting from 0.
|
||||
|
||||
.. note::
|
||||
|
||||
Sorting can be handled through the ``sort`` GET param, according to
|
||||
JSON API spec (http://jsonapi.org/format/#fetching-sorting).
|
||||
|
||||
:return: The available reports objects in a JSON ``data`` dict.
|
||||
"""
|
||||
return get_reports(only_active=True)
|
||||
|
||||
|
||||
@bottle.route('/api/v1/reports', ["POST", "OPTIONS"])
|
||||
def post_report():
|
||||
"""
|
||||
@ -225,7 +140,6 @@ def post_report():
|
||||
"upvotes": 0,
|
||||
"lng": 2.385234797066081,
|
||||
"type": "pothole",
|
||||
"is_open": true,
|
||||
…
|
||||
},
|
||||
"type": "reports",
|
||||
@ -290,7 +204,6 @@ def upvote_report(id):
|
||||
"upvotes": 1,
|
||||
"lng": 2.385234797066081,
|
||||
"type": "pothole",
|
||||
"is_open": true,
|
||||
…
|
||||
},
|
||||
"type": "reports",
|
||||
@ -349,7 +262,6 @@ def downvote_report(id):
|
||||
"upvotes": 0,
|
||||
"lng": 2.385234797066081,
|
||||
"type": "pothole",
|
||||
"is_open": true,
|
||||
…
|
||||
},
|
||||
"type": "reports",
|
||||
|
@ -24,7 +24,7 @@ export function saveReport(type, lat, lng) {
|
||||
}
|
||||
|
||||
export function getActiveReports() {
|
||||
return fetch(`${BASE_URL}api/v1/reports/active`)
|
||||
return fetch(`${BASE_URL}api/v1/reports?filter[expiration_datetime][gt?]=${(new Date()).toISOString()}`)
|
||||
.then(response => response.json())
|
||||
.then(response => response.data)
|
||||
.catch((exc) => {
|
||||
|
Loading…
Reference in New Issue
Block a user