abkfenris/gage-web

View on GitHub
app/api_0_1/gages.py

Summary

Maintainability
A
0 mins
Test Coverage
"""
gages api imports api, app, db, auth, and models

Endpoints:
----------

- **/api/1.0/gages/** - **GET** List all gages
- **/api/1.0/gages/<id>** - **GET** Detailed information about gage number *id*
- **/api/1.0/gages/<id>/sample** - **POST** new sample data for gage *id*

"""

from flask import jsonify, request, url_for, current_app
from itsdangerous import JSONWebSignatureSerializer, BadSignature

from ..models import Gage
from .blueprint import api
from .errors import unauthorized


@api.route('/gages/')
def get_gages():
    """
    List all gages

    Example response: ::

        { "count": 5,
          "gages": [
            { "id": 2,
              "location": "Wild River near RT 2 in Gilead Maine",
              "name": "Wild River at Gilead",
              "url": "http://riverflo.ws/api/1.0/gages/2"
            },
            { "id": 3,
              "location": "Bear River near RT 2 in Newry Maine",
              "name": "Bear River at Newry",
              "url": "http://riverflo.ws/api/1.0/gages/3"
            }
          ],
          "next": "http://riverflo.ws/api/1.0/gages/?page=2",
          "prev": null
        }
    """
    page = request.args.get('page', 1, type=int)
    pagination = Gage.query.paginate(page,
                                     per_page=current_app.config['API_GAGES_PER_PAGE'],  # noqa
                                     error_out=False)
    gages = pagination.items
    prev = None
    if pagination.has_prev:
        prev = url_for('.get_gages', page=page-1, _external=True)
    next_p = None
    if pagination.has_next:
        next_p = url_for('.get_gages', page=page+1, _external=True)
    return jsonify({
        'gages': [gage.to_json() for gage in gages],
        'prev': prev,
        'next': next_p,
        'count': pagination.total
    })


@api.route('/gages/<int:gid>')
def get_gage(gid):
    """
    Detailed information about gage *id*

    Parameters:
        id (int): Primary id key of a gage

    Example response: ::

        { "html": "http://riverflo.ws/gage/androscoggin-rumford/",
          "id": 4,
          "location": null,
          "name": "Androscoggin River at Rumford",
          "regions": [
            { "html": "http://riverflo.ws/region/maine/",
              "id": 4,
              "name": "Maine",
              "url": "http://riverflo.ws/api/1.0/regions/4"
            }
          ],
          "sensors": [
            { "id": 10,
              "recent_sample": {
                "datetime": "Mon, 03 Nov 2014 18:15:00 GMT",
                "id": 5801,
                "url": "http://riverflo.ws/api/1.0/samples/5801",
                "value": 4.32
              },
              "type": "usgs-height",
              "url": "http://riverflo.ws/api/1.0/sensors/10"
            },
            { "id": 11,
              "recent_sample": {
                "datetime": "Mon, 03 Nov 2014 18:15:00 GMT",
                "id": 5866,
                "url": "http://riverflo.ws/api/1.0/samples/5866",
                "value": 3230.0
              },
              "type": "usgs-discharge",
              "url": "http://riverflo.ws/api/1.0/sensors/11"
            }
          ],
          "url": "http://riverflo.ws/api/1.0/gages/4"
        }
    """
    gage = Gage.query.get_or_404(gid)
    return jsonify(gage.to_long_json())


@api.route('/gages/<int:gid>/sample', methods=['POST'])
def gage_new_samples(gid):
    """
    Submit new samples to gage *id*

    Parameters:
        id (int): Primary id key number of a gage

    Samples are formatted in body of request as a JSON Web Signature using the
    ``Gage.key``

    Example sample submitter: ::

        from itsdangerous import JSONWebSignatureSerializer
        import requests

        payload = {'samples':[
            {'type':'level',
             'value':16.7,
             'datetime': str(datetime.datetime.now())
             },
            {'type':'amps',
             'value':367.3,
             'datetime': str(datetime.datetime.now())
             },
            {'type':'voltage',
             'value':14.3,
             'datetime': str(datetime.datetime.now())
             },
            {'type':'discharge',
             'value':480,
             'datetime': str(datetime.datetime.now())
            }
            ],
            'gage':{
            'id':5
            }}

        s = JSONWebSignatureSerializer('<gage.key>')

        def submit(payload):
            data = s.dumps(payload)
            url = "http://riverflo.ws/api/1.0/gages/<gage.id>/sample"
            r = requests.post(url, data=data)
            if r.status_code is 200:
                return True
            else:
                return False

    If the key matches the stored key for the gage id in the url, \
    the server will iterate over the samples and add them to the database \
    creating new sensors for the gage if a new sample type is found. \
    Then the server will return JSON with a status code of 200.

    Example response: ::

        { 'gage': {u'id': 5,
            'location': 'Androscoggin River downstream of I-95 in Auburn ME',
            'name': 'Androscoggin River at Auburn',
            'url': 'http://riverflo.ws/api/1.0/gages/5'},
         'result': 'created',
         'samples': [{'datetime': 'Tue, 04 Nov 2014 20:43:39 GMT',
           'id': 10781,
           'url': 'http://riverflo.ws/api/1.0/samples/10781',
           'value': 16.7},
          {'datetime': 'Tue, 04 Nov 2014 20:43:39 GMT',
           'id': 10782,
           'url': 'http://riverflo.ws/api/1.0/samples/10782',
           'value': 367.3},
          {'datetime': 'Tue, 04 Nov 2014 20:43:39 GMT',
           'id': 10783,
           'url': 'http://riverflo.ws/api/1.0/samples/10783',
           'value': 14.3},
          {'datetime': 'Tue, 04 Nov 2014 20:43:39 GMT',
           'id': 10784,
           'url': 'http://riverflo.ws/api/1.0/samples/10784',
           'value': 480.0}]}

    If the signature does not match, the server will return JSON \
    with a status code of 401 - Unauthorized: ::

        {'error': 'unauthorized', 'message': 'bad signature'}

    """
    gage = Gage.query.get_or_404(gid)
    s = JSONWebSignatureSerializer(gage.key)
    try:
        req_json = s.loads(request.data)
    except BadSignature:  # If the signature doesn't match
        return unauthorized('bad signature')
    except TypeError:  # Return unauthorized if no key defined for gage
        return unauthorized('bad signature')
    else:
        samples = req_json['samples']
        output = []
        print(samples)
        for sample in samples:
            result = gage.new_sample(stype=sample['type'].lower(),
                                     value=sample['value'],
                                     sdatetime=sample['datetime'])
            result_json = result.to_sensor_json()
            print(result_json)
            result_json['sender_id'] = sample['sender_id']
            print(result_json)
            output.append(result_json)
        return jsonify({
            'gage': gage.to_json(),
            'samples': output,
            'result': 'created'
        })