Skip to content

Commit 3b2dfa2

Browse files
committed
Port and improve work done in ckan#259
1 parent 1205d3a commit 3b2dfa2

2 files changed

Lines changed: 72 additions & 18 deletions

File tree

ckanext/spatial/harvesters/csw.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,12 +160,25 @@ def fetch_stage(self,harvest_object):
160160
harvest_object)
161161
return False
162162

163+
# load config
164+
self._set_source_config(harvest_object.source.config)
165+
# get output_schema from config
166+
output_schema = self.source_config.get('output_schema', self.output_schema())
167+
163168
identifier = harvest_object.guid
164169
try:
165170
record = self.csw.getrecordbyid([identifier], outputschema=self.output_schema())
166171
except Exception as e:
167-
self._save_object_error('Error getting the CSW record with GUID %s' % identifier, harvest_object)
168-
return False
172+
try:
173+
log.warn('Unable to fetch GUID {} with output schema: {}'.format(identifier, output_schema))
174+
if output_schema == self.output_schema():
175+
raise e
176+
log.info('Fetching GUID {} with output schema: {}'.format(identifier, self.output_schema()))
177+
# retry with default output schema
178+
record = self.csw.getrecordbyid([identifier], outputschema=self.output_schema())
179+
except Exception as e:
180+
self._save_object_error('Error getting the CSW record with GUID {}'.format(identifier), harvest_object)
181+
return False
169182

170183
if record is None:
171184
self._save_object_error('Empty record for GUID %s' % identifier,

ckanext/spatial/lib/csw_client.py

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ def _xmd(self, obj):
3535
pass
3636
elif isinstance(val, six.string_types):
3737
md[attr] = val
38+
elif isinstance(val, bytes):
39+
md[attr] = val
3840
elif isinstance(val, int):
3941
md[attr] = val
4042
elif isinstance(val, list):
@@ -57,7 +59,6 @@ def getcapabilities(self, debug=False, **kw):
5759
caps = self._xmd(ows)
5860
if not debug:
5961
if "request" in caps: del caps["request"]
60-
if "response" in caps: del caps["response"]
6162
if "owscommon" in caps: del caps["owscommon"]
6263
return caps
6364

@@ -70,13 +71,41 @@ class CswService(OwsService):
7071
def __init__(self, endpoint=None):
7172
super(CswService, self).__init__(endpoint)
7273
self.sortby = SortBy([SortProperty('dc:identifier')])
74+
# check capabilities
75+
log.warn("IN SETUP: %s", endpoint)
76+
_cap = self.getcapabilities(endpoint)['response']
77+
self.capabilities = etree.ElementTree(etree.fromstring(_cap))
78+
self.output_schemas = {
79+
'GetRecords': self._get_output_schemas('GetRecords'),
80+
'GetRecordById': self._get_output_schemas('GetRecordById'),
81+
}
82+
log.warn("OUTPUTSCHEMAS: %s", self.output_schemas)
83+
84+
def _get_output_schemas(self, operation):
85+
_cap_ns = self.capabilities.getroot().nsmap
86+
_ows_ns = _cap_ns.get('ows')
87+
if not _ows_ns:
88+
raise CswError('Bad getcapabilities response: OWS namespace not found ' + str(_cap_ns))
89+
_op = self.capabilities.find("//{{{}}}Operation[@name='{}']".format(_ows_ns, operation))
90+
_schemas = _op.find("{{{}}}Parameter[@name='outputSchema']".format(_ows_ns))
91+
_values = map(lambda v: v.text, _schemas.findall("{{{}}}Value".format(_ows_ns)))
92+
output_schemas = {}
93+
for key, value in _schemas.nsmap.items():
94+
if value in _values:
95+
output_schemas.update({key : value})
96+
return output_schemas
7397

7498
def getrecords(self, qtype=None, keywords=[],
7599
typenames="csw:Record", esn="brief",
76100
skip=0, count=10, outputschema="gmd", **kw):
77-
from owslib.csw import namespaces
78101
constraints = []
79102
csw = self._ows(**kw)
103+
log.warn("OUTPUT_SCHEMA: %s", outputschema)
104+
105+
# check target csw server capabilities for requested output schema
106+
output_schemas = self.output_schemas['GetRecords']
107+
if not output_schemas.get(outputschema):
108+
raise CswError('Output schema \'{}\' not supported by target server: '.format(output_schemas))
80109

81110
if qtype is not None:
82111
constraints.append(PropertyIsEqualTo("dc:type", qtype))
@@ -87,24 +116,29 @@ def getrecords(self, qtype=None, keywords=[],
87116
"esn": esn,
88117
"startposition": skip,
89118
"maxrecords": count,
90-
"outputschema": namespaces[outputschema],
119+
"outputschema": output_schemas[outputschema],
91120
"sortby": self.sortby
92-
}
121+
}
93122
log.info('Making CSW request: getrecords2 %r', kwa)
94123
csw.getrecords2(**kwa)
95124
if csw.exceptionreport:
96125
err = 'Error getting records: %r' % \
97126
csw.exceptionreport.exceptions
98-
#log.error(err)
127+
log.error(err)
99128
raise CswError(err)
100129
return [self._xmd(r) for r in list(csw.records.values())]
101130

102131
def getidentifiers(self, qtype=None, typenames="csw:Record", esn="brief",
103132
keywords=[], limit=None, page=10, outputschema="gmd",
104133
startposition=0, cql=None, **kw):
105-
from owslib.csw import namespaces
106134
constraints = []
107135
csw = self._ows(**kw)
136+
log.warn("OUTPUT_SCHEMA: %s", outputschema)
137+
138+
# fetch target csw server capabilities for requested output schema
139+
output_schemas = self.output_schemas['GetRecords']
140+
if not output_schemas.get(outputschema):
141+
raise CswError('Output schema \'{}\' not supported by target server: '.format(output_schemas))
108142

109143
if qtype is not None:
110144
constraints.append(PropertyIsEqualTo("dc:type", qtype))
@@ -115,20 +149,20 @@ def getidentifiers(self, qtype=None, typenames="csw:Record", esn="brief",
115149
"esn": esn,
116150
"startposition": startposition,
117151
"maxrecords": page,
118-
"outputschema": namespaces[outputschema],
152+
"outputschema": output_schemas[outputschema],
119153
"cql": cql,
120154
"sortby": self.sortby
121155
}
122156
i = 0
123157
matches = 0
124158
while True:
125-
log.info('Making CSW request: getrecords2 %r', kwa)
159+
log.warn('Making CSW request: getrecords2 %r', kwa)
126160

127161
csw.getrecords2(**kwa)
128162
if csw.exceptionreport:
129163
err = 'Error getting identifiers: %r' % \
130164
csw.exceptionreport.exceptions
131-
#log.error(err)
165+
log.error(err)
132166
raise CswError(err)
133167

134168
if matches == 0:
@@ -154,11 +188,15 @@ def getidentifiers(self, qtype=None, typenames="csw:Record", esn="brief",
154188
kwa["startposition"] = startposition
155189

156190
def getrecordbyid(self, ids=[], esn="full", outputschema="gmd", **kw):
157-
from owslib.csw import namespaces
158191
csw = self._ows(**kw)
192+
# fetch target csw server capabilities for requested output schema
193+
output_schemas=output_schemas = self.output_schemas['GetRecordById']
194+
if not output_schemas.get(outputschema):
195+
raise CswError('Output schema \'{}\' not supported by target server: '.format(output_schemas))
196+
159197
kwa = {
160198
"esn": esn,
161-
"outputschema": namespaces[outputschema],
199+
"outputschema": output_schemas[outputschema],
162200
}
163201
# Ordinary Python version's don't support the metadata argument
164202
log.info('Making CSW request: getrecordbyid %r %r', ids, kwa)
@@ -168,14 +206,17 @@ def getrecordbyid(self, ids=[], esn="full", outputschema="gmd", **kw):
168206
csw.exceptionreport.exceptions
169207
#log.error(err)
170208
raise CswError(err)
171-
if not csw.records:
209+
elif csw.records:
210+
record = self._xmd(list(csw.records.values())[0])
211+
elif csw.response:
212+
record = self._xmd(etree.fromstring(csw.response))
213+
else:
172214
return
173-
record = self._xmd(list(csw.records.values())[0])
174215

175216
## strip off the enclosing results container, we only want the metadata
176-
#md = csw._exml.find("/gmd:MD_Metadata")#, namespaces=namespaces)
177-
# Ordinary Python version's don't support the metadata argument
178-
md = csw._exml.find("/{http://www.isotc211.org/2005/gmd}MD_Metadata")
217+
# '/{schema}*' expression should be safe enough and is able to match the
218+
# desired schema followed by both MD_Metadata or MI_Metadata (iso19115[-2])
219+
md = csw._exml.find("/{{{schema}}}*".format(schema=output_schemas[outputschema]))
179220
mdtree = etree.ElementTree(md)
180221
try:
181222
record["xml"] = etree.tostring(mdtree, pretty_print=True, encoding=str)

0 commit comments

Comments
 (0)