diff --git a/geomagio/TimeseriesUtility.py b/geomagio/TimeseriesUtility.py index dab3e80105d2ab7ce3a96f9d0fa7a055c273d6f7..8b0d3ee49d1fbcf8fbdd5f6da1fd5fe08a661056 100644 --- a/geomagio/TimeseriesUtility.py +++ b/geomagio/TimeseriesUtility.py @@ -82,6 +82,7 @@ def get_delta_from_interval(data_interval): delta = None return delta + def get_interval_from_delta(delta): """Convert delta to an interval name @@ -95,15 +96,15 @@ def get_interval_from_delta(delta): interval : str interval length {day, hour, minute, second, tenhertz} """ - if delta == "0.1": + if delta == 0.1: data_interval = "tenhertz" elif delta == 1: data_interval = "second" - elif delta == "60": + elif delta == 60: data_interval = "minute" - elif delta == "3600": + elif delta == 3600: data_interval = "hour" - elif delta == "86400": + elif delta == 86400: data_interval = "day" else: data_interval = delta diff --git a/geomagio/webservice/data.py b/geomagio/webservice/data.py index 66f336880bc8461a123cf61958e694f58e8fe81b..5811db60be7b7c6d78a59d68733dc5ec250cc733 100644 --- a/geomagio/webservice/data.py +++ b/geomagio/webservice/data.py @@ -11,10 +11,10 @@ from ..imfjson import IMFJSONWriter from ..TimeseriesUtility import get_interval_from_delta -DEFAULT_DATA_TYPE = 'variation' -DEFAULT_ELEMENTS = ('X', 'Y', 'Z', 'F') -DEFAULT_OUTPUT_FORMAT = 'iaga2002' -DEFAULT_SAMPLING_PERIOD = '60' +DEFAULT_DATA_TYPE = "variation" +DEFAULT_ELEMENTS = ("X", "Y", "Z", "F") +DEFAULT_OUTPUT_FORMAT = "iaga2002" +DEFAULT_SAMPLING_PERIOD = "60" ERROR_CODE_MESSAGES = { 204: "No Data", 400: "Bad Request", @@ -22,20 +22,41 @@ ERROR_CODE_MESSAGES = { 409: "Conflict", 500: "Internal Server Error", 501: "Not Implemented", - 503: "Service Unavailable" + 503: "Service Unavailable", } VALID_DATA_TYPES = ["variation", "adjusted", "quasi-definitive", "definitive"] VALID_INTERVALS = ["tenhertz", "second", "minute", "hour", "day"] -VALID_OBSERVATORIES = ["BRT", "BRW", "DED", "DHT", "CMO", "CMT", "SIT", "SHU", "NEW", -"BDT", "BOU", "TST", "USGS", "FDT", "FRD", "FRN", "TUC", "BSL", "HON", "SJG", -"GUA", "SJT"] +VALID_OBSERVATORIES = [ + "BRT", + "BRW", + "DED", + "DHT", + "CMO", + "CMT", + "SIT", + "SHU", + "NEW", + "BDT", + "BOU", + "TST", + "USGS", + "FDT", + "FRD", + "FRN", + "TUC", + "BSL", + "HON", + "SJG", + "GUA", + "SJT", +] VALID_OUTPUT_FORMATS = ["iaga2002", "json"] VALID_SAMPLING_PERIODS = ["0.1", "1", "60", "3600", "86400"] blueprint = Blueprint("data", __name__) input_factory = None -VERSION = 'version' +VERSION = "version" def init_app(app: Flask): @@ -72,6 +93,7 @@ def get_data(): class WebServiceException(Exception): """Base class for exceptions thrown by web services.""" + pass @@ -97,6 +119,7 @@ class WebServiceQuery(object): output format. default 'iaga2002'. """ + def __init__( self, observatory_id=None, @@ -105,7 +128,7 @@ class WebServiceQuery(object): elements=("X", "Y", "Z", "F"), sampling_period=60, data_type="variation", - output_format="iaga2002" + output_format="iaga2002", ): self.observatory_id = observatory_id self.starttime = starttime @@ -118,15 +141,10 @@ class WebServiceQuery(object): def format_error(status_code, exception): """Assign error_body value based on error format.""" - - if request.args.get("output_format") == 'json': - return Response( - json_error(status_code, exception, request.url), - mimetype="application/json") + if request.args.get("format") == "json": + return Response(json_error(status_code, exception), mimetype="application/json") else: - return Response( - iaga2002_error(status_code, exception, request.query_string), - mimetype="text/plain") + return Response(iaga2002_error(status_code, exception), mimetype="text/plain",) def format_timeseries(timeseries, query): @@ -148,11 +166,12 @@ def format_timeseries(timeseries, query): if query.output_format == "json": return Response( IMFJSONWriter.format(timeseries, query.elements), - mimetype="application/json") + mimetype="application/json", + ) else: return Response( - IAGA2002Writer.format(timeseries, query.elements), - mimetype="text/plain") + IAGA2002Writer.format(timeseries, query.elements), mimetype="text/plain" + ) def get_input_factory(): @@ -163,15 +182,12 @@ def get_input_factory(): input_factory Edge or miniseed factory object """ - data_type = os.getenv('DATA_TYPE', 'edge') - host = os.getenv('DATA_HOST', 'cwbpub.cr.usgs.gov') - port = os.getenv('DATA_PORT', 2060) - - if data_type == 'edge': - input_factory = EdgeFactory( - host=host, - port=port, - type=data_type) + data_type = os.getenv("DATA_TYPE", "edge") + host = os.getenv("DATA_HOST", "cwbpub.cr.usgs.gov") + port = os.getenv("DATA_PORT", 2060) + + if data_type == "edge": + input_factory = EdgeFactory(host=host, port=port, type=data_type) return input_factory else: return None @@ -196,7 +212,8 @@ def get_timeseries(query): query.observatory_id, query.elements, query.data_type, - query.sampling_period) + query.sampling_period, + ) return timeseries @@ -245,11 +262,10 @@ def json_error(code, message): "generated": date, "url": request.url, "title": status_message, - "error": message - } + "error": message, + }, } - return dumps(error_dict, - sort_keys=True).encode('utf8') + return dumps(error_dict, sort_keys=True).encode("utf8") def parse_query(query): @@ -272,40 +288,52 @@ def parse_query(query): """ # Get values observatory_id = query.get("observatory") - elements = query.get("channels", DEFAULT_ELEMENTS) + start_time = query.get("starttime") + end_time = query.get("endtime") + elements = query.getlist("channels") sampling_period = query.get("sampling_period", DEFAULT_SAMPLING_PERIOD) data_type = query.get("type", DEFAULT_DATA_TYPE) output_format = query.get("format", DEFAULT_OUTPUT_FORMAT) # Format values and get time values output_format.lower() observatory_id.upper() - - try: - start_time = UTCDateTime(query.get('starttime')) - except: - start_time = query.get('starttime') + elements = [e.split(",") for e in elements] + elements = sum(elements, []) + if not elements: + elements = DEFAULT_ELEMENTS + if observatory_id not in VALID_OBSERVATORIES: + raise WebServiceException( + f"""Bad observatory id "{observatory_id}". Valid values are: {', '.join(VALID_OBSERVATORIES)}.""" + ) if not start_time: now = datetime.now() - start_time = UTCDateTime( - year=now.year, - month=now.month, - day=now.day, - hour=0) - try: - end_time = UTCDateTime(query.get("endtime")) - except: - end_time = query.get("endtime") + start_time = UTCDateTime(year=now.year, month=now.month, day=now.day, hour=0) + else: + try: + start_time = UTCDateTime(start_time) + except Exception: + raise WebServiceException( + 'Bad starttime value "%s".' + " Valid values are ISO-8601 timestamps." % start_time + ) if not end_time: end_time = start_time + (24 * 60 * 60 - 1) end_time = UTCDateTime(end_time) - + else: + try: + end_time = UTCDateTime(end_time) + except Exception: + raise WebServiceException( + 'Bad end_time value "%s".' + " Valid values are ISO-8601 timestamps." % end_time + ) # Create WebServiceQuery object and set properties params = WebServiceQuery() params.observatory_id = observatory_id params.starttime = start_time params.endtime = end_time params.elements = elements - params.sampling_period = get_interval_from_delta(sampling_period) + params.sampling_period = get_interval_from_delta(float(sampling_period)) params.data_type = data_type params.output_format = output_format return params @@ -324,33 +352,21 @@ def validate_query(query): WebServiceException if any parameters are not supported. """ - if type(query.endtime) == str: - raise WebServiceException( - 'Bad end_time value "%s".' - ' Valid values are ISO-8601 timestamps.' % query.endtime) - if type(query.starttime) == str: - raise WebServiceException( - 'Bad end_time value "%s".' - ' Valid values are ISO-8601 timestamps.' % query.starttime) if len(query.elements) > 4 and query.output_format == "iaga2002": raise WebServiceException( "No more than four elements allowed for iaga2002 format." ) - if query.observatory_id not in VALID_OBSERVATORIES: - raise WebServiceException( - f"""Bad observatory id "{query.observatory_id}". Valid values are: {', '.join(VALID_OBSERVATORIES)}.""" - ) if query.starttime > query.endtime: raise WebServiceException("Starttime must be before endtime.") if query.data_type not in VALID_DATA_TYPES: raise WebServiceException( - f"""Bad data type value "{query.data_type}". Valid values are: {', '.join(VALID_DATA_TYPES)}.""" - ) + f"""Bad data type value "{query.data_type}". Valid values are: {', '.join(VALID_DATA_TYPES)}.""" + ) if query.sampling_period not in VALID_INTERVALS: raise WebServiceException( f"""Bad sampling_period value {query.sampling_period}. Valid values are: {', '.join(VALID_SAMPLING_PERIODS)}.""" - ) + ) if query.output_format not in VALID_OUTPUT_FORMATS: raise WebServiceException( - f"""Bad format value "{query.output_format}". Valid values are: {', '.join(VALID_OUTPUT_FORMATS)}.""" - ) \ No newline at end of file + f"""Bad format value "{query.output_format}". Valid values are: {', '.join(VALID_OUTPUT_FORMATS)}.""" + ) diff --git a/geomagio/webservice/static/usage.css b/geomagio/webservice/static/usage.css index 0bb4b75dd87e9893ca504baaaf51b552e18d3794..05ab695099fdc6b6df062740f9c3acb9d66b13df 100644 --- a/geomagio/webservice/static/usage.css +++ b/geomagio/webservice/static/usage.css @@ -1,7 +1,3 @@ -p { - font-weight: bold; -} - .space-between { margin: 1em 0 0; } diff --git a/geomagio/webservice/templates/data/usage.html b/geomagio/webservice/templates/data/usage.html index 476a041ee981b927523b1365547feab0b20027fb..fc189df5bc92bc22c649074e7425f9a1a194b321 100644 --- a/geomagio/webservice/templates/data/usage.html +++ b/geomagio/webservice/templates/data/usage.html @@ -3,55 +3,83 @@ <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='usage.css') }}"/> {% endblock %} {% block content %} - <div class="views-field views-field-field-intro"> <div class="field-content lead pane-border"><p><strong>Example Requests</strong><br /></p><dl><dt>BOU observatory data for current UTC day in IAGA2002 format</dt> -<dd><a href="http://geomag.usgs.gov/ws/edge/?id=BOU">http://geomag.usgs.gov/ws/edge/?id=BOU</a></dd> -<dt>BOU observatory data for current UTC day in JSON format</dt> -<dd><a href="http://geomag.usgs.gov/ws/edge/?id=BOU&format=json">http://geomag.usgs.gov/ws/edge/?id=BOU&format=json</a></dd> -<dt>BOU electric field data for current UTC day in IAGA2002 format</dt> -<dd><a href="http://geomag.usgs.gov/ws/edge/?id=BOU&elements=E-N,E-E">http://geomag.usgs.gov/ws/edge/?id=BOU&elements=E-N,E-E</a></dd> -</dl><p><a href="/natural-hazards/geomagnetism/science/more-examples">See more examples</a></p> -</div> </div> - <div class="views-field views-field-field-science-tab-intro"> <div class="field-content"></div> </div> - <div class="views-field views-field-field-science-object-body"> <div class="field-content"><div class="tex2jax"><p><strong>Request Limits</strong></p> -<p>To ensure availability for users, the web service restricts the amount of data that can be retrieved in one request. The amount of data requested is computed as follows, where an interval is the number of seconds between start time and end time:</p> -<p>samples = count(elements) * interval / sampling_period</p> -<p><strong>Limits by the output format</strong></p> -<p><strong>json</strong></p> -<ul><li>172800 samples = 4 elements * 12 hours * 3600 samples/hour.</li> -</ul><p><strong>iaga2002</strong></p> -<ul><li>345600 samples = 4 elements * 24 hours * 3600 samples/hour.</li> -<li>NOTE: while the json format supports fewer total samples per request, users may request fewer elements to retrieve longer intervals.</li> -</ul><p><strong>Parameters</strong></p> -<p><strong>id</strong></p> -<ul><li>Observatory code. Required.</li> -<li>Valid values: BDT, BOU, TST, BRW, BRT, BSL, CMO, CMT, DED, DHT, FRD, FRN, GUA, HON, NEW, SHU, SIT, SJG, TUC, USGS, BLC, BRD, CBB, EUA, FCC, IQA, MEA, OTT, RES, SNK, STJ, VIC, YKC, HAD, HER, KAK</li> -</ul><p><strong>starttime</strong></p> -<ul><li>Time of first requested data.</li> -<li>Default: start of current UTC day</li> -<li>Format: ISO8601 (YYYY-MM-DDTHH:MM:SSZ)</li> -<li>Example: 2018-08-06T22:10:14Z</li> -</ul><p><strong>endtime</strong></p> -<ul><li>Time of last requested data.</li> -<li>Default: starttime + 24 hours</li> -<li>Format: ISO8601 (YYYY-MM-DDTHH:MM:SSZ)</li> -<li>Example: 2018-08-06T22:10:14Z</li> -</ul><p><strong>elements</strong></p> -<ul><li>Comma separated list of requested elements.</li> -<li>Default: X,Y,Z,F</li> -<li>Valid values: D, DIST, DST, E, E-E, E-N, F, G, H, SQ, SV, UK1, UK2, UK3, UK4, X, Y, Z</li> -</ul><p><strong>sampling_period</strong></p> -<ul><li>Interval in seconds between values.</li> -<li>Default: 60</li> -<li>Valid values: 1, 60, 3600</li> -</ul><p><strong>type</strong></p> -<ul><li>Type of data.</li> -<li>Default: variation Valid values: variation, adjusted, quasi-definitive,definitive</li> -<li>NOTE: the USGS web service also supports specific EDGE location codes. For example: R0 is "internet variation",R1 is "satellite variation".</li> -</ul><p><strong>format</strong></p> -<ul><li>Output format.</li> -<li>Default: iaga2002</li> -<li>Valid values: iaga2002, json.</li> -</ul></div></div> </div> +<div> + <div> + <p><strong>Example Requests</strong><br /></p> + <dl> + <dt>BOU observatory data for current UTC day in IAGA2002 format</dt> + <dd><a href="http://geomag.usgs.gov/ws/edge/?id=BOU">http://geomag.usgs.gov/ws/edge/?id=BOU</a> + </dd> + <dt>BOU observatory data for current UTC day in JSON format + </dt> + <dd><a href="http://geomag.usgs.gov/ws/edge/?id=BOU&format=json">http://geomag.usgs.gov/ws/edge/?id=BOU&format=json</a> + </dd> + <dt>BOU electric field data for current UTC day in IAGA2002 format + </dt> + <dd><a href="http://geomag.usgs.gov/ws/edge/?id=BOU&elements=E-N,E-E">http://geomag.usgs.gov/ws/edge/?id=BOU&elements=E-N,E-E</a> + </dd> + </dl> + <p><a href="/natural-hazards/geomagnetism/science/more-examples">See more examples</a> + </p> +</div> +<div> + <p><strong>Request Limits</strong></p> + <p>To ensure availability for users, the web service restricts the amount of data that can be retrieved in one request. The amount of data requested is computed as follows, where an interval is the number of seconds between start time and end time:</p> + <p>samples = count(elements) * interval / sampling_period</p> + <p><strong>Limits by the output format</strong></p> + <p><strong>json</strong></p> + <ul> + <li>172800 samples = 4 elements * 12 hours * 3600 samples/hour.</li> + </ul> + <p><strong>iaga2002</strong></p> + <ul> + <li>345600 samples = 4 elements * 24 hours * 3600 samples/hour.</li> + <li>NOTE: while the json format supports fewer total samples per request, users may request fewer elements to retrieve longer intervals.</li> + </ul> + <p><strong>Parameters</strong></p> + <p><strong>id</strong></p> + <ul> + <li>Observatory code. Required.</li> + <li>Valid values: BDT, BOU, TST, BRW, BRT, BSL, CMO, CMT, DED, DHT, FRD, FRN, GUA, HON, NEW, SHU, SIT, SJG, TUC, USGS, BLC, BRD, CBB, EUA, FCC, IQA, MEA, OTT, RES, SNK, STJ, VIC, YKC, HAD, HER, KAK</li> + </ul> + <p><strong>starttime</strong></p> + <ul> + <li>Time of first requested data.</li> + <li>Default: start of current UTC day</li> + <li>Format: ISO8601 (YYYY-MM-DDTHH:MM:SSZ)</li> + <li>Example: 2018-08-06T22:10:14Z</li> + </ul> + <p><strong>endtime</strong></p> + <ul> + <li>Time of last requested data.</li> + <li>Default: starttime + 24 hours</li> + <li>Format: ISO8601 (YYYY-MM-DDTHH:MM:SSZ)</li> + <li>Example: 2018-08-06T22:10:14Z</li> + </ul><p><strong>elements</strong></p> + <ul> + <li>Comma separated list of requested elements.</li> + <li>Default: X,Y,Z,F</li> + <li>Valid values: D, DIST, DST, E, E-E, E-N, F, G, H, SQ, SV, UK1, UK2, UK3, UK4, X, Y, Z</li> + </ul> + <p><strong>sampling_period</strong></p> + <ul> + <li>Interval in seconds between values.</li> + <li>Default: 60</li> + <li>Valid values: 1, 60, 3600</li> + </ul> + <p><strong>type</strong></p> + <ul> + <li>Type of data.</li> + <li>Default: variation Valid values: variation, adjusted, quasi-definitive,definitive</li> + <li>NOTE: the USGS web service also supports specific EDGE location codes. For example: R0 is "internet variation",R1 is "satellite variation".</li> + </ul> + <p><strong>format</strong></p> + <ul> + <li>Output format.</li> + <li>Default: iaga2002</li> + <li>Valid values: iaga2002, json.</li> + </ul> +</div></div> </body> </html> {% endblock %} \ No newline at end of file