Commit 49287c0c authored by Javier Quinteros's avatar Javier Quinteros

Include a central TdmswsAPI and two other classes for the Station-WS and the Dataselect.

application.wadl still does not work. The dot is still not accepted.
parent 745c06ef
<application xmlns="http://wadl.dev.java.net/2009/02"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<resources base="/fdsnws/station/1">
<resource path="query">
<method name="GET">
<request>
<param name="starttime" style="query" type="xsd:dateTime"/>
<param name="endtime" style="query" type="xsd:dateTime"/>
<param name="startbefore" style="query" type="xsd:dateTime"/>
<param name="startafter" style="query" type="xsd:dateTime"/>
<param name="endbefore" style="query" type="xsd:dateTime"/>
<param name="endafter" style="query" type="xsd:dateTime"/>
<param name="network" style="query" type="xsd:string"/>
<param name="station" style="query" type="xsd:string"/>
<param name="location" style="query" type="xsd:string"/>
<param name="channel" style="query" type="xsd:string"/>
<param name="minlatitude" style="query" type="xsd:float"/>
<param name="maxlatitude" style="query" type="xsd:float"/>
<param name="minlongitude" style="query" type="xsd:float"/>
<param name="maxlongitude" style="query" type="xsd:float"/>
<param name="latitude" style="query" type="xsd:float"/>
<param name="longitude" style="query" type="xsd:float"/>
<param name="minradius" style="query" type="xsd:float"/>
<param name="maxradius" style="query" type="xsd:float"/>
<param name="level" style="query" type="xsd:string" default="station">
<option value="network"/>
<option value="station"/>
<option value="channel"/>
<option value="response"/>
</param>
<param name="includerestricted" style="query" type="xsd:boolean" default="false"/>
<param name="nodata" style="query" type="xsd:int" default="204">
<option value="204"/>
<option value="404"/>
</param>
<param name="format" style="query" type="xsd:string" default="xml">
<option value="xml"/>
<option value="text"/>
<!-- additional, non standard options -->
<option value="fdsnxml"/>
<option value="stationxml"/>
<option value="sc3ml"/>
</param>
<!-- additional, non standard parameters -->
<param name="formatted" style="query" type="xsd:boolean" default="false">
<doc>
Controls formatted (pretty print) output.
</doc>
</param>
<!-- not implemented -->
<!--param name="updateafter" style="query" type="xsd:dateTime"/-->
</request>
<response status="200">
<representation mediaType="application/xml"/>
<representation mediaType="text/plain"/>
</response>
<response status="204 400 401 403 404 413 414 500 503">
<representation mediaType="text/plain"/>
</response>
</method>
<!-- <method name="POST">
<response status="200">
<representation mediaType="application/xml"/>
<representation mediaType="text/plain"/>
</response>
<response status="204 400 401 403 404 413 414 500 503">
<representation mediaType="text/plain"/>
</response>
</method> -->
</resource>
<resource path="version">
<method name="GET">
<response>
<representation mediaType="text/plain"/>
</response>
</method>
</resource>
<resource path="application.wadl">
<method name="GET">
<response>
<representation mediaType="application/xml"/>
</response>
</method>
</resource>
</resources>
</application>
......@@ -30,6 +30,7 @@ import cherrypy
import argparse
from cherrypy.process import plugins
import os
import json
import logging
import logging.config
from datetime import datetime
......@@ -162,12 +163,11 @@ Service version:
return template.format(code, text)
@cherrypy.expose
class TdmswsAPI(object):
"""Object dispatching methods related to access to streams."""
"""Main class including the dispatcher."""
def __init__(self, experiment, directory='.', loglevel='WARNING'):
"""Constructor of the TdmswsAPI class."""
"""Constructor of the TdmswsAPI object."""
# Save parameters
self.__experiment = experiment
self.__directory = directory
......@@ -185,6 +185,10 @@ class TdmswsAPI(object):
if self.__log == '':
self.__log = '--'
self.dataselect = DataselectAPI(experiment, directory)
self.station = StationAPI(experiment, directory)
self.log = logging.getLogger('TdmswsAPI')
@cherrypy.expose
def index(self):
cherrypy.response.headers['Content-Type'] = 'text/html'
......@@ -195,20 +199,318 @@ class TdmswsAPI(object):
texthelp = fin.read()
except FileNotFoundError:
texthelp = """<html>
<head>tdsmws</head>
<head>tdmsws</head>
<body>
Default help for the tdmsws service (GEOFON).
</body>
</html>"""
return texthelp.encode('utf-8')
@cherrypy.expose
@cherrypy.popargs('wsversion')
class StationAPI(object):
"""Object dispatching methods related to access to streams."""
def __init__(self, experiment, directory='.', loglevel='WARNING'):
"""Constructor of the StationAPI class."""
# Save parameters
self.__experiment = experiment
self.__directory = directory
self.__log = logging.getLogger('StationAPI')
self.__log.setLevel(loglevel)
# Get extra fields from the cfg file
cfgfile = configparser.RawConfigParser()
cfgfile.read(os.path.join(directory, 'tdsmws.cfg'))
self.__net = cfgfile.get('NSLC', 'network', fallback='XX')
self.__cha = cfgfile.get('NSLC', 'channel', fallback='FH1')
# Special case for the empty location. Change '' for '--'
self.__loc = cfgfile.get('NSLC', 'location', fallback='--')
if self.__loc == '':
self.__loc = '--'
@cherrypy.expose
def index(self, wsversion='1'):
if wsversion != '1':
# Send Error 400
message = 'Only Station-WS version 1 is supported'
self.__log.error(message)
cherrypy.response.headers['Content-Type'] = 'text/plain'
raise cherrypy.HTTPError(400, errormessage(400, message))
cherrypy.response.headers['Content-Type'] = 'text/html'
# TODO Create an HTML page with a minimum documentation for a user
try:
with open('help.html') as fin:
texthelp = fin.read()
except FileNotFoundError:
texthelp = """<html>
<head>tdsmws - Station</head>
<body>
Default help for the Station web service (GEOFON).
</body>
</html>"""
return texthelp.encode('utf-8')
@cherrypy.expose
def applicationwadl(self, wsversion='1'):
if wsversion != '1':
# Send Error 400
message = 'Only Station-WS version 1 is supported'
self.__log.error(message)
cherrypy.response.headers['Content-Type'] = 'text/plain'
raise cherrypy.HTTPError(400, errormessage(400, message))
project_dir = os.path.dirname(__file__)
try:
with open(os.path.join(project_dir, 'data/station.wadl')) as fin:
text = fin.read()
cherrypy.response.headers['Content-Type'] = 'application/xml'
return text.encode('utf-8')
except FileNotFoundError:
pass
# Send Error 400
message = 'application.wadl not found!'
self.__log.error(message)
cherrypy.response.headers['Content-Type'] = 'text/plain'
raise cherrypy.HTTPError(400, errormessage(400, message))
@cherrypy.expose
def version(self, wsversion='1'):
"""Return the version of this implementation.
:returns: Version of the system
:rtype: string
"""
if wsversion != '1':
# Send Error 400
message = 'Only Station-WS version 1 is supported'
self.__log.error(message)
cherrypy.response.headers['Content-Type'] = 'text/plain'
raise cherrypy.HTTPError(400, errormessage(400, message))
stationwsversion = '1.1.0'
cherrypy.response.headers['Content-Type'] = 'text/plain'
return stationwsversion.encode('utf-8')
@cherrypy.expose
def query(self, network='*', net='*', station='*', sta='*', location='*', loc='*', channel='*', cha='*',
starttime=None, start=None, endtime=None, end=None, minlatitude=-90.0, minlat=-90.0, maxlatitude=90.0,
maxlat=90.0, minlongitude=-180.0, minlon=-180.0, maxlongitude=180.0, maxlon=180.0, format='xml',
level='station', wsversion='1', **kwargs):
"""Get data in miniSEED format.
:param network: Usually the network code configured in the cfg file. It is included just to satisfy the standard
:type network: str
:param net: Alias of network
:type net: str
:param station: Comma-separated integers identifying of streams to retrieve
:type station: str
:param sta: Alias of station
:type sta: str
:param location: Usually the location code configured in the cfg file. Included just to satisfy the standard
:type location: str
:param loc: Alias of location
:type loc: str
:param channel: Usually the channel code configured in the cfg file. It is included just to satisfy the standard
:type channel: str
:param cha: Alias of channel
:type cha: str
:param starttime: Start time of the time window to access
:type starttime: str
:param start: Alias of starttime
:type start: str
:param endtime: End time of the time window to access
:type endtime: str
:param end: Alias of endtime
:type end: str
:param format: Specify format of result, either xml or text. If it's not specified the service must
return StationXML.
:type format: str
:returns: miniSEED data
:rtype: bytearray
:raises: cherrypy.HTTPError
"""
"""Constructor of the StationAPI class."""
# Check parameters
# WS version
if wsversion != '1':
# Send Error 400
message = 'Only Station-WS version 1 is supported'
self.__log.error(message)
cherrypy.response.headers['Content-Type'] = 'text/plain'
raise cherrypy.HTTPError(400, errormessage(400, message))
# Format
# Only text format is currently implemented
if format != 'text':
# Send Error 400
message = 'Only format=text is currently supported'
self.__log.error(message)
cherrypy.response.headers['Content-Type'] = 'text/plain'
raise cherrypy.HTTPError(400, errormessage(400, message))
# Network
if network not in ('*', self.__net) or net not in ('*', self.__net):
cherrypy.response.status = 204
return
# Location
if location not in ('*', self.__loc) or loc not in ('*', self.__loc):
cherrypy.response.status = 204
return
# Channel
if channel not in ('*', self.__cha) or cha not in ('*', self.__cha):
cherrypy.response.status = 204
return
# Station and sta
# Discard the most comprehensive case of '*' and keep the most restricted one
auxsta = station if sta == '*' else sta
# Starttime and start
# Discard the most comprehensive case of None and keep the most restricted one
starttime = starttime if start is None else start
# Endtime and end
# Discard the most comprehensive case of None and keep the most restricted one
endtime = endtime if end is None else end
# Station(s)
try:
liststa = [] if auxsta == '*' else [int(x) for x in auxsta.split(',')]
except Exception:
# Send Error 400
message = 'Wrong formatted list of stations (%s).' % sta
self.__log.error(message)
cherrypy.response.headers['Content-Type'] = 'text/plain'
raise cherrypy.HTTPError(400, errormessage(400, message))
if starttime is not None:
try:
startdt = str2date(starttime)
except Exception:
# Send Error 400
message = 'Error converting the "starttime" parameter (%s).' % starttime
self.__log.error(message)
cherrypy.response.headers['Content-Type'] = 'text/plain'
raise cherrypy.HTTPError(400, errormessage(400, message))
else:
startdt = None
if endtime is not None:
try:
enddt = str2date(endtime)
except Exception:
# Send Error 400
message = 'Error converting the "endtime" parameter (%s).' % endtime
self.__log.error(message)
cherrypy.response.headers['Content-Type'] = 'text/plain'
raise cherrypy.HTTPError(400, errormessage(400, message))
else:
enddt = None
cherrypy.response.headers['Content-Type'] = 'text/plain'
return self.__generatemetadata(liststa, startdt, enddt)
def __generatemetadata(self, streams, starttime=None, endtime=None):
"""Generator to extract metadata based on the selection
:param streams: List of streams (integers) to be extracted
:type streams: list
:param starttime: Start time of the time window to access
:type starttime: datetime
:param endtime: End time of the time window to access
:type endtime: datetime
:returns: miniSEED data
:rtype: bytearray
"""
if not len(streams):
t = TDMS(self.__experiment, directory=self.__directory, starttime=starttime, endtime=endtime, iterate='M')
with t:
for data in t:
yield json.dumps(data, default=datetime.isoformat)
return
for stream in streams:
t = TDMS(self.__experiment, directory=self.__directory, starttime=starttime, endtime=endtime,
chstart=stream, chstop=stream, iterate='M')
with t:
for data in t:
yield json.dumps(data, default=datetime.isoformat)
@cherrypy.expose
@cherrypy.popargs('wsversion')
class DataselectAPI(object):
"""Object dispatching methods related to access to streams."""
def __init__(self, experiment, directory='.', loglevel='WARNING'):
"""Constructor of the DataselectAPI class."""
# Save parameters
self.__experiment = experiment
self.__directory = directory
self.__log = logging.getLogger('DataselectAPI')
self.__log.setLevel(loglevel)
# Get extra fields from the cfg file
cfgfile = configparser.RawConfigParser()
cfgfile.read(os.path.join(directory, 'tdsmws.cfg'))
self.__net = cfgfile.get('NSLC', 'network', fallback='XX')
self.__cha = cfgfile.get('NSLC', 'channel', fallback='FH1')
# Special case for the empty location. Change '' for '--'
self.__loc = cfgfile.get('NSLC', 'location', fallback='--')
if self.__loc == '':
self.__loc = '--'
@cherrypy.expose
def index(self, wsversion='1'):
if wsversion != '1':
# Send Error 400
message = 'Only Station-WS version 1 is supported'
self.__log.error(message)
cherrypy.response.headers['Content-Type'] = 'text/plain'
raise cherrypy.HTTPError(400, errormessage(400, message))
cherrypy.response.headers['Content-Type'] = 'text/html'
# TODO Create an HTML page with a minimum documentation for a user
try:
with open('help.html') as fin:
texthelp = fin.read()
except FileNotFoundError:
texthelp = """<html>
<head>tdsmws - Dataselect</head>
<body>
Default help for the tdsmws service (GEOFON).
Default help for the Dataselect service (GEOFON).
</body>
</html>"""
return texthelp.encode('utf-8')
@cherrypy.expose
def applicationwadl(self):
def applicationwadl(self, wsversion='1'):
if wsversion != '1':
# Send Error 400
message = 'Only Station-WS version 1 is supported'
self.__log.error(message)
cherrypy.response.headers['Content-Type'] = 'text/plain'
raise cherrypy.HTTPError(400, errormessage(400, message))
project_dir = os.path.dirname(__file__)
try:
with open(os.path.join(project_dir, 'data/application.wadl')) as fin:
with open(os.path.join(project_dir, 'data/dataselect.wadl')) as fin:
text = fin.read()
cherrypy.response.headers['Content-Type'] = 'application/xml'
return text.encode('utf-8')
......@@ -222,19 +524,26 @@ class TdmswsAPI(object):
raise cherrypy.HTTPError(400, errormessage(400, message))
@cherrypy.expose
def version(self):
def version(self, wsversion='1'):
"""Return the version of this implementation.
:returns: Version of the system
:rtype: string
"""
version = '1.1.0'
if wsversion != '1':
# Send Error 400
message = 'Only Station-WS version 1 is supported'
self.__log.error(message)
cherrypy.response.headers['Content-Type'] = 'text/plain'
raise cherrypy.HTTPError(400, errormessage(400, message))
dataselectversion = '1.1.0'
cherrypy.response.headers['Content-Type'] = 'text/plain'
return version.encode('utf-8')
return dataselectversion.encode('utf-8')
@cherrypy.expose
def query(self, network='*', net='*', station='*', sta='*', location='*', loc='*', channel='*', cha='*',
starttime=None, start=None, endtime=None, end=None, **kwargs):
starttime=None, start=None, endtime=None, end=None, wsversion='1', **kwargs):
"""Get data in miniSEED format.
:param network: Usually the network code configured in the cfg file. It is included just to satisfy the standard
......@@ -261,12 +570,21 @@ class TdmswsAPI(object):
:type endtime: str
:param end: Alias of endtime
:type end: str
:param wsversion: Major version of the Dataselect web service
:type wsversion: str
:returns: miniSEED data
:rtype: bytearray
:raises: cherrypy.HTTPError
"""
# Check parameters
if wsversion != '1':
# Send Error 400
message = 'Only Station-WS version 1 is supported'
self.__log.error(message)
cherrypy.response.headers['Content-Type'] = 'text/plain'
raise cherrypy.HTTPError(400, errormessage(400, message))
# Network
if network not in ('*', self.__net):
cherrypy.response.headers['Content-Type'] = 'text/plain'
......@@ -346,9 +664,9 @@ class TdmswsAPI(object):
enddt = None
cherrypy.response.headers['Content-Type'] = 'application/vnd.fdsn.mseed'
return self.generatemseed(liststa, startdt, enddt)
return self.__generatemseed(liststa, startdt, enddt)
def generatemseed(self, streams, starttime=None, endtime=None):
def __generatemseed(self, streams, starttime=None, endtime=None):
"""Generator to extract miniSEED data based on the selection
:param streams: List of streams (integers) to be extracted
......@@ -420,7 +738,7 @@ def main():
}
# Update the global CherryPy configuration
cherrypy.config.update(server_config)
cherrypy.tree.mount(TdmswsAPI(experiment), '/fdsnws/dataselect/1')
cherrypy.tree.mount(TdmswsAPI(experiment), '/fdsnws')
plugins.Daemonizer(cherrypy.engine).subscribe()
if hasattr(cherrypy.engine, 'signal_handler'):
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment