Skip to content

Commit

Permalink
Use URLS as defined in GetCaps for CSW operations. #154
Browse files Browse the repository at this point in the history
Changes the OperationsMetadata objects in ows.py to allow for multiple
urls of the same type (ie. POST/XML and POST/SOAP).
  • Loading branch information
kwilcox committed May 13, 2014
1 parent 626a7d1 commit a14c402
Show file tree
Hide file tree
Showing 27 changed files with 212 additions and 118 deletions.
2 changes: 1 addition & 1 deletion CREDITS.txt
Expand Up @@ -9,4 +9,4 @@
* Brad Hards <bradh@frogmouth.net>
* Christian Ledermann <christian.ledermann@gmail.com>
* Sean Cowan <scowan@asascience.com>
* Kyle Wilcox <kwilcox@asascience.com>
* Kyle Wilcox <wilcox.kyle@gmail.com>
18 changes: 11 additions & 7 deletions owslib/coverage/wcs100.py
Expand Up @@ -111,7 +111,10 @@ def getCoverage(self, identifier=None, bbox=None, time=None, format = None, crs
if log.isEnabledFor(logging.DEBUG):
log.debug('WCS 1.0.0 DEBUG: Parameters passed to GetCoverage: identifier=%s, bbox=%s, time=%s, format=%s, crs=%s, width=%s, height=%s, resx=%s, resy=%s, resz=%s, parameter=%s, method=%s, other_arguments=%s'%(identifier, bbox, time, format, crs, width, height, resx, resy, resz, parameter, method, str(kwargs)))

base_url = self.getOperationByName('GetCoverage').methods[method]['url']
try:
base_url = next((m.get('url') for m in self.getOperationByName('GetCoverage').methods if m.get('type').lower() == method.lower()))
except StopIteration:
base_url = self.url

if log.isEnabledFor(logging.DEBUG):
log.debug('WCS 1.0.0 DEBUG: base url of server: %s'%base_url)
Expand Down Expand Up @@ -164,7 +167,8 @@ def getOperationByName(self, name):
if item.name == name:
return item
raise KeyError, "No operation named %s" % name



class OperationMetadata(object):
"""Abstraction for WCS metadata.
Implements IMetadata.
Expand All @@ -174,15 +178,15 @@ def __init__(self, elem):
self.name = elem.tag.split('}')[1]

#self.formatOptions = [f.text for f in elem.findall('{http://www.opengis.net/wcs/1.1/ows}Parameter/{http://www.opengis.net/wcs/1.1/ows}AllowedValues/{http://www.opengis.net/wcs/1.1/ows}Value')]
methods = []
self.methods = []
for resource in elem.findall(ns('DCPType/')+ns('HTTP/')+ns('Get/')+ns('OnlineResource')):
url = resource.attrib['{http://www.w3.org/1999/xlink}href']
methods.append(('Get', {'url': url}))
self.methods.append({'type': 'Get', 'url': url})
for resource in elem.findall(ns('DCPType/')+ns('HTTP/')+ns('Post/')+ns('OnlineResource')):
url = resource.attrib['{http://www.w3.org/1999/xlink}href']
methods.append(('Post', {'url': url}))
self.methods = dict(methods)
self.methods.append({'type': 'Post', 'url': url})


class ServiceIdentification(object):
""" Abstraction for ServiceIdentification metadata """
def __init__(self,elem):
Expand Down
6 changes: 4 additions & 2 deletions owslib/coverage/wcs110.py
Expand Up @@ -138,10 +138,12 @@ def getCoverage(self, identifier=None, bbox=None, time=None, format = None, stor
if log.isEnabledFor(logging.DEBUG):
log.debug('WCS 1.1.0 DEBUG: Parameters passed to GetCoverage: identifier=%s, bbox=%s, time=%s, format=%s, rangesubset=%s, gridbaseCRS=%s, gridtype=%s, gridCS=%s, gridorigin=%s, gridoffsets=%s, method=%s, other_arguments=%s'%(identifier, bbox, time, format, rangesubset, gridbaseCRS, gridtype, gridCS, gridorigin, gridoffsets, method, str(kwargs)))


if method == 'Get':
method='{http://www.opengis.net/wcs/1.1/ows}Get'
base_url = self.getOperationByName('GetCoverage').methods[method]['url']
try:
base_url = next((m.get('url') for m in self.getOperationByName('GetCoverage').methods if m.get('type').lower() == method.lower()))
except StopIteration:
base_url = self.url


#process kwargs
Expand Down
16 changes: 15 additions & 1 deletion owslib/csw.py
Expand Up @@ -586,6 +586,20 @@ def _invoke(self):
if isinstance(self.request, basestring): # GET KVP
self.response = urlopen(self.request, timeout=self.timeout).read()
else:
xml_post_url = self.url
# Get correct POST URL based on Operation list.
for op in self.operations:
post_verbs = filter(lambda x: x.get('type').lower() == 'post', op.methods)
if len(post_verbs) > 1:
# Filter by constraints. We must match a PostEncoding of "XML"
try:
xml_post_url = next(x for x in filter(list, ([pv.get('url') for const in pv.get('constraints') if const.name.lower() == "postencoding" and 'xml' in map(lambda x: x.lower(), const.values)] for pv in post_verbs)))[0]
except StopIteration:
# Well, just use the first one.
xml_post_url = post_verbs[0].get('url')
elif len(post_verbs) == 1:
xml_post_url = post_verbs[0].get('url')

self.request = cleanup_namespaces(self.request)
# Add any namespaces used in the "typeNames" attribute of the
# csw:Query element to the query's xml namespaces.
Expand All @@ -599,7 +613,7 @@ def _invoke(self):

self.request = util.xml2string(etree.tostring(self.request))

self.response = util.http_post(self.url, self.request, self.lang, self.timeout)
self.response = util.http_post(xml_post_url, self.request, self.lang, self.timeout)

# parse result see if it's XML
self._exml = etree.parse(StringIO.StringIO(self.response))
Expand Down
2 changes: 1 addition & 1 deletion owslib/feature/__init__.py
Expand Up @@ -105,7 +105,7 @@ def getGETGetFeatureRequest(self, typename=None, filter=None, bbox=None, feature
3) featureid (direct access to known features)
"""

base_url = self.getOperationByName('GetFeature').methods[method]['url']
base_url = next((m.get('url') for m in self.getOperationByName('GetFeature').methods if m.get('type').lower() == method.lower()))
base_url = base_url if base_url.endswith("?") else base_url+"?"

request = {'service': 'WFS', 'version': self.version, 'request': 'GetFeature'}
Expand Down
16 changes: 9 additions & 7 deletions owslib/feature/wfs100.py
Expand Up @@ -10,7 +10,7 @@
from cStringIO import StringIO
from urllib import urlencode
from urllib2 import urlopen
from owslib.util import openURL, testXMLValue, extract_xml_list, ServiceException
from owslib.util import openURL, testXMLValue, extract_xml_list, ServiceException, xmltag_split
from owslib.etree import etree
from owslib.fgdc import Metadata
from owslib.iso import MD_Metadata
Expand Down Expand Up @@ -162,9 +162,11 @@ def getfeature(self, typename=None, filter=None, bbox=None, featureid=None,
2) typename and filter (more expressive)
3) featureid (direct access to known features)
"""
base_url = self.getOperationByName('{http://www.opengis.net/wfs}GetFeature').methods[method]['url']
try:
base_url = next((m.get('url') for m in self.getOperationByName('GetFeature').methods if m.get('type').lower() == method.lower()))
except StopIteration:
base_url = self.url
request = {'service': 'WFS', 'version': self.version, 'request': 'GetFeature'}


# check featureid
if featureid:
Expand Down Expand Up @@ -318,21 +320,21 @@ def __init__(self, elem, parent, parse_remote_metadata=False, timeout=30):

self.metadataUrls.append(metadataUrl)


class OperationMetadata:
"""Abstraction for WFS metadata.
Implements IMetadata.
"""
def __init__(self, elem):
"""."""
self.name = elem.tag
self.name = xmltag_split(elem.tag)
# formatOptions
self.formatOptions = [f.tag for f in elem.findall(nspath('ResultFormat/*'))]
methods = []
self.methods = []
for verb in elem.findall(nspath('DCPType/HTTP/*')):
url = verb.attrib['onlineResource']
methods.append((verb.tag, {'url': url}))
self.methods = dict(methods)
self.methods.append({'type' : xmltag_split(verb.tag), 'url': url})


class WFSCapabilitiesReader(object):
Expand Down
5 changes: 4 additions & 1 deletion owslib/feature/wfs110.py
Expand Up @@ -148,7 +148,10 @@ def getfeature(self, typename=None, filter=None, bbox=None, featureid=None,
2) typename and filter (more expressive)
3) featureid (direct access to known features)
"""
base_url = self.getOperationByName('GetFeature').methods[method]['url']
try:
base_url = next((m.get('url') for m in self.getOperationByName('GetFeature').methods if m.get('type').lower() == method.lower()))
except StopIteration:
base_url = self.url
request = {'service': 'WFS', 'version': self.version, 'request': 'GetFeature'}

if not isinstance(typename, list):
Expand Down
19 changes: 14 additions & 5 deletions owslib/feature/wfs200.py
Expand Up @@ -218,8 +218,11 @@ def getfeature(self, typename=None, filter=None, bbox=None, featureid=None,


def getpropertyvalue(self, query=None, storedquery_id=None, valuereference=None, typename=None, method=nspath('Get'),**kwargs):
''' the WFS GetPropertyValue method'''
base_url = self.getOperationByName('GetPropertyValue').methods[method]['url']
''' the WFS GetPropertyValue method'''
try:
base_url = next((m.get('url') for m in self.getOperationByName('GetPropertyValue').methods if m.get('type').lower() == method.lower()))
except StopIteration:
base_url = self.url
request = {'service': 'WFS', 'version': self.version, 'request': 'GetPropertyValue'}
if query:
request['query'] = str(query)
Expand All @@ -245,12 +248,15 @@ def _getStoredQueries(self, timeout=30):
method=nspath('Get')

#first make the ListStoredQueries response and save the results in a dictionary if form {storedqueryid:(title, returnfeaturetype)}
base_url = self.getOperationByName('ListStoredQueries').methods[method]['url']
try:
base_url = next((m.get('url') for m in self.getOperationByName('ListStoredQueries').methods if m.get('type').lower() == method.lower()))
except StopIteration:
base_url = self.url

request = {'service': 'WFS', 'version': self.version, 'request': 'ListStoredQueries'}
encoded_request = urlencode(request)
u = urlopen(base_url + encoded_request, timeout=timeout)
tree=etree.fromstring(u.read())
base_url = self.getOperationByName('ListStoredQueries').methods[method]['url']
tempdict={}
for sqelem in tree[:]:
title=rft=id=None
Expand All @@ -263,7 +269,10 @@ def _getStoredQueries(self, timeout=30):
tempdict[id]=(title,rft) #store in temporary dictionary

#then make the DescribeStoredQueries request and get the rest of the information about the stored queries
base_url = self.getOperationByName('DescribeStoredQueries').methods[method]['url']
try:
base_url = next((m.get('url') for m in self.getOperationByName('DescribeStoredQueries').methods if m.get('type').lower() == method.lower()))
except StopIteration:
base_url = self.url
request = {'service': 'WFS', 'version': self.version, 'request': 'DescribeStoredQueries'}
encoded_request = urlencode(request)
u = urlopen(base_url + encoded_request, timeout=timeout)
Expand Down
28 changes: 21 additions & 7 deletions owslib/ows.py
Expand Up @@ -145,20 +145,34 @@ def __init__(self, infoset,namespace=DEFAULT_OWS_NAMESPACE):
val = self._root.find(util.nspath('ServiceContact/ContactInfo/ContactInstructions', namespace))
self.instructions = util.testXMLValue(val)


class Constraint(object):
def __init__(self, elem, namespace=DEFAULT_OWS_NAMESPACE):
self.name = elem.attrib.get('name')
self.values = [i.text for i in elem.findall(util.nspath('Value', namespace))]
self.values += [i.text for i in elem.findall(util.nspath('AllowedValues/Value', namespace))]

def __repr__(self):
if self.values:
return "Constraint: %s - %s" % (self.name, self.values)
else:
return "Constraint: %s" % self.name


class OperationsMetadata(object):
"""Initialize an OWS OperationMetadata construct"""
def __init__(self,elem,namespace=DEFAULT_OWS_NAMESPACE):
def __init__(self, elem, namespace=DEFAULT_OWS_NAMESPACE):
self.name = elem.attrib['name']
self.formatOptions = ['text/xml']
methods = []
parameters = []
constraints = []
self.methods = []
self.constraints = []

for verb in elem.findall(util.nspath('DCP/HTTP/*', namespace)):
url = util.testXMLAttribute(verb, util.nspath('href', XLINK_NAMESPACE))
if url is not None:
methods.append((util.xmltag_split(verb.tag), {'url': url}))
self.methods = dict(methods)
verb_constraints = [Constraint(conts, namespace) for conts in verb.findall(util.nspath('Constraint', namespace))]
self.methods.append({'constraints' : verb_constraints, 'type' : util.xmltag_split(verb.tag), 'url': url})

for parameter in elem.findall(util.nspath('Parameter', namespace)):
if namespace == OWS_NAMESPACE_1_1_0:
Expand All @@ -168,8 +182,8 @@ def __init__(self,elem,namespace=DEFAULT_OWS_NAMESPACE):
self.parameters = dict(parameters)

for constraint in elem.findall(util.nspath('Constraint', namespace)):
constraints.append((constraint.attrib['name'], {'values': [i.text for i in constraint.findall(util.nspath('Value', namespace))]}))
self.constraints = dict(constraints)
self.constraints.append(Constraint(constraint, namespace))


class BoundingBox(object):
"""Initialize an OWS BoundingBox construct"""
Expand Down
17 changes: 15 additions & 2 deletions owslib/swe/observation/sos100.py
Expand Up @@ -59,6 +59,13 @@ def __init__(self, url, version='1.0.0', xml=None, username=None, password=None)
# build metadata objects
self._build_metadata()

def getOperationByName(self, name):
"""Return a named content item."""
for item in self.operations:
if item.name == name:
return item
raise KeyError("No operation named %s" % name)

def _build_metadata(self):
"""
Set up capabilities metadata objects
Expand Down Expand Up @@ -96,7 +103,10 @@ def describe_sensor(self, outputFormat=None,
method='Get',
**kwargs):

base_url = self.get_operation_by_name('DescribeSensor').methods[method]['url']
try:
base_url = next((m.get('url') for m in self.getOperationByName('DescribeSensor').methods if m.get('type').lower() == method.lower()))
except StopIteration:
base_url = self.url
request = {'service': 'SOS', 'version': self.version, 'request': 'DescribeSensor'}

# Required Fields
Expand Down Expand Up @@ -137,8 +147,11 @@ def get_observation(self, responseFormat=None,
**kwargs : extra arguments
anything else e.g. vendor specific parameters
"""
try:
base_url = next((m.get('url') for m in self.getOperationByName('GetObservation').methods if m.get('type').lower() == method.lower()))
except StopIteration:
base_url = self.url

base_url = self.get_operation_by_name('GetObservation').methods[method]['url']
request = {'service': 'SOS', 'version': self.version, 'request': 'GetObservation'}

# Required Fields
Expand Down
11 changes: 9 additions & 2 deletions owslib/swe/observation/sos200.py
Expand Up @@ -62,6 +62,13 @@ def __init__(self, url, version='2.0.0', xml=None, username=None, password=None)
# build metadata objects
self._build_metadata()

def getOperationByName(self, name):
"""Return a named content item."""
for item in self.operations:
if item.name == name:
return item
raise KeyError("No operation named %s" % name)

def _build_metadata(self):
"""
Set up capabilities metadata objects
Expand Down Expand Up @@ -100,8 +107,8 @@ def describe_sensor(self, outputFormat=None,
**kwargs):

try:
base_url = self.get_operation_by_name('DescribeSensor').methods[method]['url']
except:
base_url = next((m.get('url') for m in self.getOperationByName('DescribeSensor').methods if m.get('type').lower() == method.lower()))
except StopIteration:
base_url = self.url
request = {'service': 'SOS', 'version': self.version, 'request': 'DescribeSensor'}

Expand Down
15 changes: 9 additions & 6 deletions owslib/wms.py
Expand Up @@ -20,7 +20,7 @@
from urllib import urlencode
import warnings
from etree import etree
from .util import openURL, testXMLValue, extract_xml_list
from .util import openURL, testXMLValue, extract_xml_list, xmltag_split
from fgdc import Metadata
from iso import MD_Metadata

Expand Down Expand Up @@ -202,7 +202,10 @@ def getmap(self, layers=None, styles=None, srs=None, bbox=None,
>>> out.close()
"""
base_url = self.getOperationByName('GetMap').methods[method]['url']
try:
base_url = next((m.get('url') for m in self.getOperationByName('GetMap').methods if m.get('type').lower() == method.lower()))
except StopIteration:
base_url = self.url
request = {'version': self.version, 'request': 'GetMap'}

# check layers and styles
Expand Down Expand Up @@ -510,14 +513,14 @@ class OperationMetadata:
"""
def __init__(self, elem):
"""."""
self.name = elem.tag
self.name = xmltag_split(elem.tag)
# formatOptions
self.formatOptions = [f.text for f in elem.findall('Format')]
methods = []
self.methods = []
for verb in elem.findall('DCPType/HTTP/*'):
url = verb.find('OnlineResource').attrib['{http://www.w3.org/1999/xlink}href']
methods.append((verb.tag, {'url': url}))
self.methods = dict(methods)
self.methods.append({'type' : xmltag_split(verb.tag), 'url': url})


class ContactMetadata:
"""Abstraction for contact details advertised in GetCapabilities.
Expand Down

0 comments on commit a14c402

Please sign in to comment.