Skip to content

Commit

Permalink
Endpoint-shuffle (Use URL to signal the data-shape returned).
Browse files Browse the repository at this point in the history
  • Loading branch information
timstaley committed Dec 11, 2015
1 parent eeb5d5d commit 25507f3
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 78 deletions.
2 changes: 1 addition & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def get_routes(app, endpoint=None):
# OK, let's get ugly!
# sphinx.contrib.autohttp has no way to hyperlink the endpoint headings.
# So instead, we add hyperlinks in the docstrings of the APIV0 endpoints:
apiv1_rules = [r for r in sorted(app.url_map.iter_rules())
apiv1_rules = [r for r in sorted(app.url_map.iter_rules(), key=lambda x: str(x))
if r.endpoint.startswith('apiv1')]
import textwrap

Expand Down
2 changes: 1 addition & 1 deletion voeventdb/server/restapi/inspection_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def apiv1_endpoints():
# Grab all app endpoints, filter to apiv1
apiv1_rules = [r for r in app.url_map.iter_rules()
if r.endpoint.startswith('apiv1')]
# Filter duplicate listings for path-routed endpoints (xml_view, synopsis)
# Filter duplicate listings for path-routed endpoints (packet_xml, synopsis)
apiv1_rules = [r for r in apiv1_rules if
'<' not in str(r)]
return {str(r.endpoint)[6:]: str(r) for r in apiv1_rules}
Expand Down
30 changes: 20 additions & 10 deletions voeventdb/server/restapi/v1/apierror.py
Original file line number Diff line number Diff line change
@@ -1,42 +1,52 @@
from __future__ import absolute_import
from flask import url_for


class InvalidQueryString(Exception):
code = 400
description = "Invalid query-string"

def __init__(self, querystring_key, querystring_value, reason=None):
Exception.__init__(self)
self.message = """
Error parsing query-string - could not parse this section:
'{0}={1}'\n
""".format(querystring_key, querystring_value)
if reason:
self.message+= " - " + reason
self.message += " - " + reason


class IvornNotFound(Exception):
code = 422
description = 'IVORN not found'
def __init__(self, ivorn):

def __init__(self, ivorn, suggested_ivorn_url=None):
Exception.__init__(self)

self.message = """
Sorry, IVORN: '{0}' not found in the cache.
Sorry, IVORN: '{0}' not found in the database.
If your IVORN has been truncated at the '#' character,
then it probably just needs to be
<a href="http://meyerweb.com/eric/tools/dencoder/">URL-encoded</a>.
IVORN listings can be found at <a href="{1}">{1}</a>.
""".format(ivorn,url_for('apiv1.ivorn'))
""".format(ivorn)
if suggested_ivorn_url:
self.message += (
'IVORN listings can be found at <a href="{0}">{0}</a>.'.format(
suggested_ivorn_url))


class IvornNotSupplied(Exception):
code = 400
description = "No IVORN supplied"
def __init__(self):

def __init__(self, suggested_ivorn_url=None):
Exception.__init__(self)
self.message = """
Please append an
<a href="http://meyerweb.com/eric/tools/dencoder/">URL-encoded</a>
IVORN to the URL. IVORN listings can be found at
<a href="{0}">{0}</a>.
""".format(url_for('apiv1.ivorn'))
IVORN to the URL.
"""
if suggested_ivorn_url:
self.message += (
'IVORN listings can be found at <a href="{0}">{0}</a>.'.format(
suggested_ivorn_url))
6 changes: 3 additions & 3 deletions voeventdb/server/restapi/v1/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,13 +181,13 @@ def combinator(self, filters):
@add_to_filter_registry
class RefAny(QueryFilter):
"""
Return only VOEvents which have / do not have references to other VOEvents.
Return only VOEvents which make / don't make reference to any other VOEvents.
Applied via query-strings ``refs=true`` or ``refs=false``.
Applied via query-strings ``ref_any=true`` or ``ref_any=false``.
NB 'true'/'false' string-values are case-insensitive, so e.g.
'true', 'True', 'TRUE', 'tRUe' are all valid.
"""
querystring_key = 'ref'
querystring_key = 'ref_any'
example_values = ['true',
'True',
'false'
Expand Down
106 changes: 53 additions & 53 deletions voeventdb/server/restapi/v1/views.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
from __future__ import absolute_import

from flask import (
Blueprint, request, make_response, render_template, current_app,
jsonify
jsonify, url_for
)

import urllib

from voeventdb.server import __versiondict__ as package_version_dict
from voeventdb.server.restapi.annotate import lookup_relevant_urls
from voeventdb.server.database import session_registry as db_session
Expand All @@ -17,8 +14,6 @@
from voeventdb.server.restapi.v1.viewbase import (
QueryView, ListQueryView, _add_to_api, make_response_dict
)


# This import may look unused, but activates the filter registry -
# Do not delete!
import voeventdb.server.restapi.v1.filters
Expand Down Expand Up @@ -65,9 +60,12 @@ def validate_ivorn(url_encoded_ivorn):
else:
ivorn = url_encoded_ivorn
if ivorn is None:
raise apierror.IvornNotSupplied
raise apierror.IvornNotSupplied(
suggested_ivorn_url=url_for(apiv1.name + '.' + ListIvorn.view_name))
if not convenience.ivorn_present(db_session, ivorn):
raise apierror.IvornNotFound(ivorn)
raise apierror.IvornNotFound(
ivorn,
suggested_ivorn_url=url_for(apiv1.name + '.' + ListIvorn.view_name))
return ivorn


Expand All @@ -83,7 +81,7 @@ def apiv1_root_view():
'http://' + request.host + '/docs')
message = "Welcome to the voeventdb REST API, " \
"interface version '{}'.".format(
apiv1.name)
apiv1.name)
api_details = {
'message': message,
'api_version': apiv1.name,
Expand Down Expand Up @@ -133,32 +131,6 @@ def page_not_found(abort_error):
# Alphabetically ordered endpoints from here on
# -----------------------------------------------

@add_to_apiv1
class AuthoredMonthCount(QueryView):
"""
Result:
Dict: Mapping month -> packet counts per-month.
Here, 'month' refers to the month of the 'authoring' DateTime,
i.e. the ``Who.Date`` element of the VOEvent. NB, may be None.
"""
view_name = 'authored_month_count'

def get_query(self):
return query.authored_month_counts_q(db_session)

def process_query(self, q):
raw_results = q.all()
converted_results = []
for r in raw_results:
if r.month_id:
newrow = (r.month_id.date().isoformat()[:-3], r.month_count)
else:
newrow = r
converted_results.append(newrow)
return dict(converted_results)


@add_to_apiv1
Expand All @@ -179,15 +151,15 @@ def process_query(self, q):


@add_to_apiv1
class IvornList(ListQueryView):
class ListIvorn(ListQueryView):
"""
Result (list of strings):
``[ ivorn1, ivorn2, ... ]``
List of ivorns matching querystring. Number returned is limited by the
``limit`` parameter, which defaults to 100 (see :ref:`pagination`).
"""
view_name = 'ivorn'
view_name = 'list/ivorn'

def get_query(self):
return db_session.query(Voevent.ivorn)
Expand All @@ -204,7 +176,7 @@ def process_query(self, query):


@add_to_apiv1
class IvornReferenceCount(ListQueryView):
class ListIvornReferenceCount(ListQueryView):
"""
Result (list of 2-element lists):
``[[ivorn, n_refs], ...]``
Expand All @@ -214,7 +186,7 @@ class IvornReferenceCount(ListQueryView):
- IVORN of packet
- Number of references to other packets, in this packet.
"""
view_name = 'ivorn_ref_count'
view_name = 'list/ivorn_nrefs'

def get_query(self):
return query.ivorn_cites_to_others_count_q(db_session)
Expand All @@ -224,7 +196,7 @@ def process_query(self, query):


@add_to_apiv1
class IvornCitedCount(ListQueryView):
class ListIvornCitedCount(ListQueryView):
"""
Result (list of 2-element lists):
``[[ivorn, n_cited], ...]``
Expand All @@ -234,7 +206,7 @@ class IvornCitedCount(ListQueryView):
- IVORN of packet
- Number of times this packet is cited by others
"""
view_name = 'ivorn_cited_count'
view_name = 'list/ivorn_ncites'

def get_query(self):
return query.ivorn_cited_from_others_count_q(db_session)
Expand All @@ -244,12 +216,40 @@ def process_query(self, query):


@add_to_apiv1
class RoleCount(QueryView):
class MapAuthoredMonthCount(QueryView):
"""
Result:
Dict: Mapping month -> packet counts per-month.
Here, 'month' refers to the month of the 'authoring' DateTime,
i.e. the ``Who.Date`` element of the VOEvent. NB, may be None.
"""
view_name = 'map/authored_month_count'

def get_query(self):
return query.authored_month_counts_q(db_session)

def process_query(self, q):
raw_results = q.all()
converted_results = []
for r in raw_results:
if r.month_id:
newrow = (r.month_id.date().isoformat()[:-3], r.month_count)
else:
newrow = r
converted_results.append(newrow)
return dict(converted_results)


@add_to_apiv1
class MapRoleCount(QueryView):
"""
Result:
Dict: Mapping role -> packet counts per-role.
"""
view_name = 'role_count'
view_name = 'map/role_count'

def get_query(self):
return query.role_counts_q(db_session)
Expand All @@ -259,12 +259,12 @@ def process_query(self, q):


@add_to_apiv1
class StreamCount(QueryView):
class MapStreamCount(QueryView):
"""
Result:
Dict: Mapping stream -> packet counts per-stream.
"""
view_name = 'stream_count'
view_name = 'map/stream_count'

def get_query(self):
return query.stream_counts_q(db_session)
Expand All @@ -274,12 +274,12 @@ def process_query(self, q):


@add_to_apiv1
class StreamRoleCount(QueryView):
class MapStreamRoleCount(QueryView):
"""
Result:
Nested dict: Mapping stream -> role -> packet counts per-stream-and-role.
"""
view_name = 'stream_role_count'
view_name = 'map/stream_role_count'

def get_query(self):
return query.stream_counts_role_breakdown_q(db_session)
Expand All @@ -288,9 +288,9 @@ def process_query(self, q):
return convenience.to_nested_dict(q.all())


@apiv1.route('/synopsis/')
@apiv1.route('/synopsis/<path:url_encoded_ivorn>')
def synopsis_view(url_encoded_ivorn=None):
@apiv1.route('/packet/synopsis/')
@apiv1.route('/packet/synopsis/<path:url_encoded_ivorn>')
def packet_synopsis(url_encoded_ivorn=None):
"""
Result:
Nested dict providing key details, e.g.::
Expand Down Expand Up @@ -359,9 +359,9 @@ def synopsis_view(url_encoded_ivorn=None):
return jsonify(make_response_dict(result))


@apiv1.route('/xml/')
@apiv1.route('/xml/<path:url_encoded_ivorn>')
def xml_view(url_encoded_ivorn=None):
@apiv1.route('/packet/xml/')
@apiv1.route('/packet/xml/<path:url_encoded_ivorn>')
def packet_xml(url_encoded_ivorn=None):
"""
Returns the XML packet contents stored for a given IVORN.
Expand Down

0 comments on commit 25507f3

Please sign in to comment.