Skip to content
Snippets Groups Projects
data.py 8.34 KiB
Newer Older
  • Learn to ignore specific revisions
  • Travis Rivers's avatar
    Travis Rivers committed
    from datetime import datetime
    
    Travis Rivers's avatar
    Travis Rivers committed
    import os
    
    Travis Rivers's avatar
    Travis Rivers committed
    from flask import Blueprint, Flask, jsonify, render_template, request, Response
    from obspy import UTCDateTime
    
    Travis Rivers's avatar
    Travis Rivers committed
    from collections import OrderedDict
    from json import dumps
    
    
    Travis Rivers's avatar
    Travis Rivers committed
    from geomagio.edge import EdgeFactory
    from geomagio.iaga2002 import IAGA2002Writer
    from geomagio.imfjson import IMFJSONWriter
    
    
    Travis Rivers's avatar
    Travis Rivers committed
    ERROR_CODE_MESSAGES = {
        204: "No Data",
        400: "Bad Request",
        404: "Not Found",
        409: "Conflict",
        500: "Internal Server Error",
        501: "Not Implemented",
    
    Travis Rivers's avatar
    Travis Rivers committed
    }
    VALID_DATA_TYPES = ["variation", "adjusted", "quasi-definitive", "definitive"]
    VALID_OUTPUT_FORMATS = ["iaga2002", "json"]
    VALID_SAMPLING_PERIODS = ["1", "60"]
    
    blueprint = Blueprint("data", __name__)
    factory = EdgeFactory(
    
        host=os.getenv('host', 'cwbpub.cr.usgs.gov'),
        port=os.getenv('port', 2060),
        write_port=os.getenv('write_port', 7981)
       )
    
    Travis Rivers's avatar
    Travis Rivers committed
    
    def init_app(app: Flask):
        global blueprint
    
    Travis Rivers's avatar
    Travis Rivers committed
        global factory
    
    Travis Rivers's avatar
    Travis Rivers committed
    
        app.register_blueprint(blueprint)
    
    
    Travis Rivers's avatar
    Travis Rivers committed
    class WebServiceQuery(object):
        """Query parameters for a web service request.
        Parameters
        ----------
        observatory_id : str
            observatory
        starttime : obspy.core.UTCDateTime
            time of first requested sample
        endtime : obspy.core.UTCDateTime
            time of last requested sample
        elements : array_like
            list of requested elements
        sampling_period : int
            period between samples in seconds
            default 60.
        data_type : {'variation', 'adjusted', 'quasi-definitive', 'definitive'}
            data type
            default 'variation'.
        output_format : {'iaga2002', 'json'}
            output format.
            default 'iaga2002'.
        """
        def __init__(
            self,
            observatory_id=None,
            starttime=None,
            endtime=None,
    
    Travis Rivers's avatar
    Travis Rivers committed
            sampling_period=60,
            data_type="variation",
            output_format="iaga2002",
        ):
            self.observatory_id = observatory_id
            self.starttime = starttime
            self.endtime = endtime
            self.elements = elements
            self.sampling_period = sampling_period
            self.data_type = data_type
            self.output_format = output_format
    
        def _verify_parameters(self):
            """Verify that parameters are valid.
            Raises
            ------
            WebServiceException
                if any parameters are not supported.
            """
            if len(self.elements) > 4 and self.output_format == "iaga2002":
                raise WebServiceException(
                    "No more than four elements allowed for iaga2002 format."
                )
            if self.starttime > self.endtime:
                raise WebServiceException("Starttime must be before endtime.")
            if self.data_type not in VALID_DATA_TYPES:
                raise WebServiceException(
                    'Bad type value "%s".'
                    " Valid values are: %s" % (self.data_type, VALID_DATA_TYPES)
                )
            if self.sampling_period not in VALID_SAMPLING_PERIODS:
                raise WebServiceException(
                    'Bad sampling_period value "%s".'
                    " Valid values are: %s" % (self.sampling_period, VALID_SAMPLING_PERIODS)
                )
            if self.output_format not in VALID_OUTPUT_FORMATS:
                raise WebServiceException(
                    'Bad format value "%s".'
                    " Valid values are: %s" % (self.output_format, VALID_OUTPUT_FORMATS)
                )
    
    @blueprint.route("/data", methods=["GET"])
    
    Travis Rivers's avatar
    Travis Rivers committed
    def get_data():
        query_params = request.args
    
        url = request.url
    
        if not query_params:
    
    Travis Rivers's avatar
    Travis Rivers committed
            return render_template("usage.html")
    
        parsed_query = parse_query(query_params)
    
    Travis Rivers's avatar
    Travis Rivers committed
    
    
    Travis Rivers's avatar
    Travis Rivers committed
        try:
            parsed_query._verify_parameters()
        except Exception as e:
            message = str(e)
            error_body = error(400, message, parsed_query, url)
            return error_body
    
    Travis Rivers's avatar
    Travis Rivers committed
    
    
        try:
            timeseries = get_timeseries(parsed_query)
        except Exception as e:
            message = str(e)
            error_body = error(500, message, parsed_query, url)
            return error_body
    
    Travis Rivers's avatar
    Travis Rivers committed
    
    
    Travis Rivers's avatar
    Travis Rivers committed
        return format_timeseries(timeseries, parsed_query)
    
    Travis Rivers's avatar
    Travis Rivers committed
    
    
    Travis Rivers's avatar
    Travis Rivers committed
    def error(code, message, query, url):
        error_body = http_error(code, message, query, url)
        status = str(code) + ' ' + ERROR_CODE_MESSAGES[code]
    
    Travis Rivers's avatar
    Travis Rivers committed
    
    
    Travis Rivers's avatar
    Travis Rivers committed
        Response(error_body, mimetype="text/plain")
    
        return error_body
    
    def http_error(code, message, query, url):
        if query.output_format == 'json':
            http_error_body = json_error(code, message, url)
            return http_error_body
        else:
    
            http_error_body = iaga2002_error(code, message, url)
    
    Travis Rivers's avatar
    Travis Rivers committed
            return http_error_body
    
    def json_error(code, message, url):
        error_dict = OrderedDict()
        error_dict['type'] = "Error"
        error_dict['metadata'] = OrderedDict()
    
        error_dict['metadata']['status'] = code
    
    Travis Rivers's avatar
    Travis Rivers committed
        date = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%fZ")
        error_dict['metadata']['generated'] = date
        error_dict['metadata']['url'] = url
        status_message = ERROR_CODE_MESSAGES[code]
        error_dict['metadata']['title'] = status_message
        error_dict['metadata']['error'] = message
        error_body = dumps(error_dict,
        ensure_ascii=True).encode('utf8')
    
        return error_body
    
    def iaga2002_error(code, message, url):
        status_message = ERROR_CODE_MESSAGES[code]
    
        error_body = 'Error ' + str(code) + ': ' \
        + status_message + '\n\n' + message + '\n\n'\
        + 'Usage details are available from '\
        + 'http://geomag.usgs.gov/ws/edge/ \n\n'\
        + 'Request:\n' + url + '\n\n' + 'Request Submitted:\n'\
        + datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ") + '\n'
    
        error_body = Response(error_body, mimetype="text/plain")
    
    Travis Rivers's avatar
    Travis Rivers committed
        return error_body
    
    Travis Rivers's avatar
    Travis Rivers committed
    
    def parse_query(query):
        """Parse request arguments into a set of parameters
    
        Parameters
        ----------
        query: Immutable Dict
            request.args object
    
        Returns
        -------
    
    Travis Rivers's avatar
    Travis Rivers committed
        WebServiceQuery
            parsed query object
    
    Travis Rivers's avatar
    Travis Rivers committed
    
        Raises
        ------
        WebServiceException
            if any parameters are not supported.
        """
    
        # Create web service query object
        params = WebServiceQuery()
    
    Travis Rivers's avatar
    Travis Rivers committed
    
    
    Travis Rivers's avatar
    Travis Rivers committed
        if not query.get("endtime"):
    
    Travis Rivers's avatar
    Travis Rivers committed
            now = datetime.now()
    
    Travis Rivers's avatar
    Travis Rivers committed
            today = UTCDateTime(now.year, now.month, now.day, 0)
    
    Travis Rivers's avatar
    Travis Rivers committed
            end_time = today
        else:
    
    Travis Rivers's avatar
    Travis Rivers committed
            end_time = UTCDateTime(query.get("endtime"))
    
    Travis Rivers's avatar
    Travis Rivers committed
    
    
    Travis Rivers's avatar
    Travis Rivers committed
        if not query.get("starttime"):
            start_time = UTCDateTime(query.get("endtime")) - (24 * 60 * 60 - 1)
    
    Travis Rivers's avatar
    Travis Rivers committed
        else:
    
    Travis Rivers's avatar
    Travis Rivers committed
            start_time = UTCDateTime(query.get("starttime"))
    
    Travis Rivers's avatar
    Travis Rivers committed
    
    
    Travis Rivers's avatar
    Travis Rivers committed
            sampling_period = query.get("sampling_period")
    
            params.sampling_period =  sampling_period
    
    Travis Rivers's avatar
    Travis Rivers committed
    
    
    Travis Rivers's avatar
    Travis Rivers committed
            format = query.get("format")
    
    Travis Rivers's avatar
    Travis Rivers committed
    
    
    Travis Rivers's avatar
    Travis Rivers committed
        observatory = query.get("observatory")
    
    Travis Rivers's avatar
    Travis Rivers committed
    
    
    Travis Rivers's avatar
    Travis Rivers committed
            channels = query.get("channels").split(",")
    
    Travis Rivers's avatar
    Travis Rivers committed
    
    
        if query.get("type"):
            type = query.get("type")
            params.data_type = type
    
    Travis Rivers's avatar
    Travis Rivers committed
    
    
    Travis Rivers's avatar
    Travis Rivers committed
        params.observatory_id = observatory
        params.starttime = start_time
        params.endtime = end_time
    
    Travis Rivers's avatar
    Travis Rivers committed
    
        return params
    
    def get_timeseries(query):
        """
        Parameters
        ----------
    
    Travis Rivers's avatar
    Travis Rivers committed
         WebServiceQuery
            parsed query object
    
    Travis Rivers's avatar
    Travis Rivers committed
    
        Returns
        -------
        obspy.core.Stream
            timeseries object with requested data
        """
    
    Travis Rivers's avatar
    Travis Rivers committed
        if query.sampling_period == "1":
            query.sampling_period = "second"
    
        if query.sampling_period == "60":
            query.sampling_period = "minute"
    
    Travis Rivers's avatar
    Travis Rivers committed
    
        timeseries = factory.get_timeseries(
    
    Travis Rivers's avatar
    Travis Rivers committed
            query.starttime,
            query.endtime,
            query.observatory_id,
            query.elements,
            query.data_type,
            query.sampling_period)
    
    Travis Rivers's avatar
    Travis Rivers committed
    
        return timeseries
    
    def format_timeseries(timeseries, query):
        """Formats timeseries into JSON or IAGA data
    
        Parameters
        ----------
        obspy.core.Stream
            timeseries object with requested data
    
    
    Travis Rivers's avatar
    Travis Rivers committed
        WebServiceQuery
            parsed query object
    
    Travis Rivers's avatar
    Travis Rivers committed
    
        Returns
        -------
        unicode
            IAGA2002 or JSON formatted string.
        """
    
    Travis Rivers's avatar
    Travis Rivers committed
        if query.output_format == "json":
            json_output = IMFJSONWriter.format(timeseries, query.elements)
            json_output = Response(json_output, mimetype="application/json")
    
    Travis Rivers's avatar
    Travis Rivers committed
    
            return json_output
    
        else:
    
    Travis Rivers's avatar
    Travis Rivers committed
            iaga_output = IAGA2002Writer.format(timeseries, query.elements)
            iaga_output = Response(iaga_output, mimetype="text/plain")
    
    Travis Rivers's avatar
    Travis Rivers committed
    
            return iaga_output
    
    class WebServiceException(Exception):
        """Base class for exceptions thrown by web services."""
        pass