def _write_response(self): """ Generate response """ # set HTTP response headers and XML declaration xmldecl = "" appinfo = "" LOGGER.debug("Writing response.") if hasattr(self, "soap") and self.soap: self._gen_soap_wrapper() if isinstance(self.kvp, dict) and "outputformat" in self.kvp and self.kvp["outputformat"] == "application/json": self.contenttype = self.kvp["outputformat"] from pycsw.core.formats import fmt_json response = fmt_json.exml2json(self.response, self.context.namespaces, self.pretty_print) else: # it's XML if "outputformat" in self.kvp: self.contenttype = self.kvp["outputformat"] else: self.contenttype = self.mimetype response = etree.tostring(self.response, pretty_print=self.pretty_print, encoding="unicode") xmldecl = '<?xml version="1.0" encoding="%s" standalone="no"?>' "\n" % self.encoding appinfo = "<!-- pycsw %s -->\n" % self.context.version s = (u"%s%s%s" % (xmldecl, appinfo, response)).encode(self.encoding) LOGGER.debug("Response code: %s", self.context.response_codes[self.status]) LOGGER.debug("Response:\n%s", s) return [self.context.response_codes[self.status], s]
def _write_response(self): ''' Generate response ''' # set HTTP response headers and XML declaration xmldecl='' appinfo='' LOGGER.debug('Writing response.') if hasattr(self, 'soap') and self.soap: self._gen_soap_wrapper() if (isinstance(self.kvp, dict) and 'outputformat' in self.kvp and self.kvp['outputformat'] == 'application/json'): self.contenttype = self.kvp['outputformat'] from pycsw.core.formats import fmt_json response = fmt_json.exml2json(self.response, self.context.namespaces, self.pretty_print) else: # it's XML if 'outputformat' in self.kvp: self.contenttype = self.kvp['outputformat'] else: self.contenttype = self.mimetype response = etree.tostring(self.response, pretty_print=self.pretty_print, encoding='unicode') xmldecl = '<?xml version="1.0" encoding="%s" standalone="no"?>\n' \ % self.encoding appinfo = '<!-- pycsw %s -->\n' % self.context.version s = (u'%s%s%s' % (xmldecl, appinfo, response)).encode(self.encoding) LOGGER.debug('Response code: %s', self.context.response_codes[self.status]) LOGGER.debug('Response:\n%s', s) return [self.context.response_codes[self.status], s]
def gen_sitemap(context, database, table, url, output_file): """generate an XML sitemap from all records in repository""" # get configuration and init repo connection repos = repository.Repository(database, context, table=table) # write out sitemap document urlset = etree.Element(util.nspath_eval("sitemap:urlset", context.namespaces), nsmap=context.namespaces) schema_loc = util.nspath_eval("xsi:schemaLocation", context.namespaces) urlset.attrib[schema_loc] = ( "%s http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd" % context.namespaces["sitemap"] ) # get all records count, records = repos.query(constraint={}, maxrecords=99999999) LOGGER.info("Found %s records", count) for rec in records: url = etree.SubElement(urlset, util.nspath_eval("sitemap:url", context.namespaces)) uri = "%s?service=CSW&version=2.0.2&request=GetRepositoryItem&id=%s" % ( url, getattr(rec, context.md_core_model["mappings"]["pycsw:Identifier"]), ) etree.SubElement(url, util.nspath_eval("sitemap:loc", context.namespaces)).text = uri # write to file LOGGER.info("Writing to %s", output_file) with open(output_file, "w") as ofile: ofile.write(etree.tostring(urlset, pretty_print=1, encoding="utf8", xml_declaration=1))
def cql2fes1(cql, namespaces): """transforms Common Query Language (CQL) query into OGC fes1 syntax""" filters = [] tmp_list = [] logical_op = None LOGGER.debug('CQL: %s', cql) if ' or ' in cql: logical_op = etree.Element(util.nspath_eval('ogc:Or', namespaces)) tmp_list = cql.split(' or ') elif ' OR ' in cql: logical_op = etree.Element(util.nspath_eval('ogc:Or', namespaces)) tmp_list = cql.split(' OR ') elif ' and ' in cql: logical_op = etree.Element(util.nspath_eval('ogc:And', namespaces)) tmp_list = cql.split(' and ') elif ' AND ' in cql: logical_op = etree.Element(util.nspath_eval('ogc:And', namespaces)) tmp_list = cql.split(' AND ') if tmp_list: LOGGER.debug('Logical operator found (AND/OR)') else: tmp_list.append(cql) for t in tmp_list: filters.append(_parse_condition(t)) root = etree.Element(util.nspath_eval('ogc:Filter', namespaces)) if logical_op is not None: root.append(logical_op) for flt in filters: condition = etree.Element(util.nspath_eval(flt[0], namespaces)) etree.SubElement( condition, util.nspath_eval('ogc:PropertyName', namespaces)).text = flt[1] etree.SubElement( condition, util.nspath_eval('ogc:Literal', namespaces)).text = flt[2] if logical_op is not None: logical_op.append(condition) else: root.append(condition) LOGGER.debug('Resulting OGC Filter: %s', etree.tostring(root, pretty_print=1)) return root
def caps2iso(record, caps, context): """Creates ISO metadata from Capabilities XML""" from pycsw.plugins.profiles.apiso.apiso import APISO apiso_obj = APISO(context.model, context.namespaces, context) apiso_obj.ogc_schemas_base = 'http://schemas.opengis.net' apiso_obj.url = context.url queryables = dict(apiso_obj.repository['queryables']['SupportedISOQueryables'].items() + apiso_obj.repository['queryables']['SupportedISOQueryables'].items()) iso_xml = apiso_obj.write_record(record, 'full', 'http://www.isotc211.org/2005/gmd', queryables, caps) return etree.tostring(iso_xml)
def _write_response(self): """ Generate response """ # set HTTP response headers and XML declaration xmldecl = '' appinfo = '' LOGGER.info('Writing response.') if hasattr(self, 'soap') and self.soap: self._gen_soap_wrapper() if etree.__version__ >= '3.5.0': # remove superfluous namespaces etree.cleanup_namespaces(self.response, keep_ns_prefixes=self.context.keep_ns_prefixes) response = etree.tostring(self.response, pretty_print=self.pretty_print, encoding='unicode') if (isinstance(self.kvp, dict) and 'outputformat' in self.kvp and (self.kvp['outputformat'] == 'application/xml' or self.kvp['outputformat'] == 'application/XML')): self.contenttype = self.kvp['outputformat'] xmldecl = ('<?xml version="1.0" encoding="%s" standalone="no"?>' '\n' % self.encoding) appinfo = '<!-- pycsw %s -->\n' % self.context.version else: # it's json self.contenttype = "application/json" from pycsw.core.formats import fmt_json response = fmt_json.xml2json(response, self.context.namespaces, self.pretty_print) if isinstance(self.contenttype, bytes): self.contenttype = self.contenttype.decode() LOGGER.debug(response) #response = '{"csw30:SummaryRecord": {"dc:identifier123": "urn:uuid:a06af396-3105-442d-8b40-22b57a90d2f2","dc:title": "Lorem ipsum dolor sit amet","dc:type": "http://purl.org/dc/dcmitype/Image","dc:format": "image/jpeg", "": {"": ""}}}' s = (u'%s%s%s' % (xmldecl, appinfo, response)).encode(self.encoding) LOGGER.debug('Response code: %s', self.context.response_codes[self.status]) LOGGER.debug('Response:\n%s', s) return [self.context.response_codes[self.status], s]
def _write_response(self): """ Generate response """ # set HTTP response headers and XML declaration xmldecl = '' appinfo = '' LOGGER.info('Writing response.') if hasattr(self, 'soap') and self.soap: self._gen_soap_wrapper() if etree.__version__ >= '3.5.0': # remove superfluous namespaces etree.cleanup_namespaces(self.response, keep_ns_prefixes=self.context.keep_ns_prefixes) response = etree.tostring(self.response, pretty_print=self.pretty_print, encoding='unicode') if (isinstance(self.kvp, dict) and 'outputformat' in self.kvp and self.kvp['outputformat'] == 'application/json'): self.contenttype = self.kvp['outputformat'] from pycsw.core.formats import fmt_json response = fmt_json.xml2json(response, self.context.namespaces, self.pretty_print) else: # it's XML if 'outputformat' in self.kvp: self.contenttype = self.kvp['outputformat'] else: self.contenttype = self.mimetype xmldecl = ('<?xml version="1.0" encoding="%s" standalone="no"?>' '\n' % self.encoding) appinfo = '<!-- pycsw %s -->\n' % self.context.version if isinstance(self.contenttype, bytes): self.contenttype = self.contenttype.decode() s = (u'%s%s%s' % (xmldecl, appinfo, response)).encode(self.encoding) LOGGER.debug('Response code: %s', self.context.response_codes[self.status]) LOGGER.debug('Response:\n%s', s) return [self.context.response_codes[self.status], s]
def update_xpath(nsmap, xml, recprop): """Update XML document XPath values""" if isinstance(xml, unicode): # not lxml serialized yet xml = etree.fromstring(xml, PARSER) recprop = eval(recprop) nsmap = eval(nsmap) try: nodes = xml.xpath(recprop['rp']['xpath'], namespaces=nsmap) if len(nodes) > 0: # matches for node1 in nodes: if node1.text != recprop['value']: # values differ, update node1.text = recprop['value'] except Exception as err: raise RuntimeError('ERROR: %s' % str(err)) return etree.tostring(xml)
def update_xpath(nsmap, xml, recprop): """Update XML document XPath values""" if isinstance(xml, bytes) or isinstance(xml, str): # serialize to lxml xml = etree.fromstring(xml, PARSER) recprop = eval(recprop) nsmap = eval(nsmap) try: nodes = xml.xpath(recprop['rp']['xpath'], namespaces=nsmap) if len(nodes) > 0: # matches for node1 in nodes: if node1.text != recprop['value']: # values differ, update node1.text = recprop['value'] except Exception as err: LOGGER.warning('update_xpath error', exc_info=True) raise RuntimeError('ERROR: %s' % str(err)) from err return etree.tostring(xml)
def gen_sitemap(context, database, table, url, output_file): """generate an XML sitemap from all records in repository""" # get configuration and init repo connection repos = repository.Repository(database, context, table=table) # write out sitemap document urlset = etree.Element(util.nspath_eval('sitemap:urlset', context.namespaces), nsmap=context.namespaces) schema_loc = util.nspath_eval('xsi:schemaLocation', context.namespaces) urlset.attrib[schema_loc] = \ '%s http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd' % \ context.namespaces['sitemap'] # get all records count, records = repos.query(constraint={}, maxrecords=99999999) LOGGER.info('Found %s records', count) for rec in records: url_ = etree.SubElement( urlset, util.nspath_eval('sitemap:url', context.namespaces)) uri = '%s?service=CSW&version=2.0.2&request=GetRepositoryItem&id=%s' % \ (url, getattr(rec, context.md_core_model['mappings']['pycsw:Identifier'])) etree.SubElement(url_, util.nspath_eval('sitemap:loc', context.namespaces)).text = uri # write to file LOGGER.info('Writing to %s', output_file) with open(output_file, 'wb') as ofile: ofile.write( etree.tostring(urlset, pretty_print=1, encoding='utf8', xml_declaration=1))
def _write_response(self): ''' Generate response ''' # set HTTP response headers and XML declaration xmldecl = '' appinfo = '' LOGGER.debug('Writing response.') if hasattr(self, 'soap') and self.soap: self._gen_soap_wrapper() if (isinstance(self.kvp, dict) and 'outputformat' in self.kvp and self.kvp['outputformat'] == 'application/json'): self.contenttype = self.kvp['outputformat'] from pycsw.core.formats import fmt_json response = fmt_json.exml2json(self.response, self.context.namespaces, self.pretty_print) else: # it's XML if 'outputformat' in self.kvp: self.contenttype = self.kvp['outputformat'] else: self.contenttype = self.mimetype response = etree.tostring(self.response, pretty_print=self.pretty_print, encoding='unicode') xmldecl = '<?xml version="1.0" encoding="%s" standalone="no"?>\n' \ % self.encoding appinfo = '<!-- pycsw %s -->\n' % self.context.version s = (u'%s%s%s' % (xmldecl, appinfo, response)).encode(self.encoding) LOGGER.debug('Response code: %s', self.context.response_codes[self.status]) LOGGER.debug('Response:\n%s', s) return [self.context.response_codes[self.status], s]
def kvp2filterxml(kvp, context): ''' transform kvp to filter XML string ''' bbox_element = None time_element = None anytext_elements = [] # Count parameters par_count = 0 for p in ['q','bbox','time']: if p in kvp and kvp[p] != '': par_count += 1 # Create root element for FilterXML root = etree.Element(util.nspath_eval('ogc:Filter', context.namespaces)) # bbox to FilterXML if 'bbox' in kvp and kvp['bbox'] != '': LOGGER.debug('Detected bbox parameter') bbox_list = [x.strip() for x in kvp['bbox'].split(',')] bbox_element = etree.Element(util.nspath_eval('ogc:BBOX', context.namespaces)) el = etree.Element(util.nspath_eval('ogc:PropertyName', context.namespaces)) el.text = 'ows:BoundingBox' bbox_element.append(el) env = etree.Element(util.nspath_eval('gml:Envelope', context.namespaces)) el = etree.Element(util.nspath_eval('gml:lowerCorner', context.namespaces)) if len(bbox_list) == 5: # add srsName LOGGER.debug('Found CRS') env.attrib['srsName'] = bbox_list[4] else: LOGGER.debug('Assuming 4326') env.attrib['srsName'] = 'urn:ogc:def:crs:OGC:1.3:CRS84' if not util.validate_4326(bbox_list): msg = '4326 coordinates out of range: %s' % bbox_list LOGGER.debug(msg) raise RuntimeError(msg) try: el.text = "%s %s" % (bbox_list[0], bbox_list[1]) except Exception as err: errortext = 'Exception: OpenSearch bbox not valid.\nError: %s.' % str(err) LOGGER.debug(errortext) env.append(el) el = etree.Element(util.nspath_eval('gml:upperCorner', context.namespaces)) try: el.text = "%s %s" % (bbox_list[2], bbox_list[3]) except Exception as err: errortext = 'Exception: OpenSearch bbox not valid.\nError: %s.' % str(err) LOGGER.debug(errortext) env.append(el) bbox_element.append(env) # q to FilterXML if 'q' in kvp and kvp['q'] != '': LOGGER.debug('Detected q parameter') qvals = kvp['q'].split() LOGGER.debug(qvals) if len(qvals) > 1: par_count += 1 for qval in qvals: LOGGER.debug('processing q token') anytext_element = etree.Element(util.nspath_eval('ogc:PropertyIsEqualTo', context.namespaces)) el = etree.Element(util.nspath_eval('ogc:PropertyName', context.namespaces)) el.text = 'csw:AnyText' anytext_element.append(el) el = etree.Element(util.nspath_eval('ogc:Literal', context.namespaces)) if six.PY2: el.text = qval.decode('utf8') else: el.text = qval anytext_element.append(el) anytext_elements.append(anytext_element) # time to FilterXML if 'time' in kvp and kvp['time'] != '': LOGGER.debug('Detected time parameter %s', kvp['time']) time_list = kvp['time'].split("/") if (len(time_list) == 2): LOGGER.debug('TIMELIST: %s', time_list) # This is a normal request if '' not in time_list: LOGGER.debug('Both dates present') # Both dates are present time_element = etree.Element(util.nspath_eval('ogc:PropertyIsBetween', context.namespaces)) el = etree.Element(util.nspath_eval('ogc:PropertyName', context.namespaces)) el.text = 'dc:date' time_element.append(el) el = etree.Element(util.nspath_eval('ogc:LowerBoundary', context.namespaces)) el2 = etree.Element(util.nspath_eval('ogc:Literal', context.namespaces)) el2.text = time_list[0] el.append(el2) time_element.append(el) el = etree.Element(util.nspath_eval('ogc:UpperBoundary', context.namespaces)) el2 = etree.Element(util.nspath_eval('ogc:Literal', context.namespaces)) el2.text = time_list[1] el.append(el2) time_element.append(el) else: if time_list == ['', '']: par_count -= 1 # One of two is empty elif time_list[1] is '': time_element = etree.Element(util.nspath_eval('ogc:PropertyIsGreaterThanOrEqualTo', context.namespaces)) el = etree.Element(util.nspath_eval('ogc:PropertyName', context.namespaces)) el.text = 'dc:date' time_element.append(el) el = etree.Element(util.nspath_eval('ogc:Literal', context.namespaces)) el.text = time_list[0] time_element.append(el) else: time_element = etree.Element(util.nspath_eval('ogc:PropertyIsLessThanOrEqualTo', context.namespaces)) el = etree.Element(util.nspath_eval('ogc:PropertyName', context.namespaces)) el.text = 'dc:date' time_element.append(el) el = etree.Element(util.nspath_eval('ogc:Literal', context.namespaces)) el.text = time_list[1] time_element.append(el) elif ((len(time_list) == 1) and ('' not in time_list)): # This is an equal request time_element = etree.Element(util.nspath_eval('ogc:PropertyIsEqualTo', context.namespaces)) el = etree.Element(util.nspath_eval('ogc:PropertyName', context.namespaces)) el.text = 'dc:date' time_element.append(el) el = etree.Element(util.nspath_eval('ogc:Literal', context.namespaces)) el.text = time_list[0] time_element.append(el) else: # Error errortext = 'Exception: OpenSearch time not valid: %s.' % str(kvp['time']) LOGGER.debug(errortext) if par_count == 0: return '' elif par_count == 1: LOGGER.debug('Single predicate filter') # Only one OpenSearch parameter exists if 'bbox' in kvp and kvp['bbox'] != '': LOGGER.debug('Adding bbox') root.append(bbox_element) elif time_element is not None: LOGGER.debug('Adding time') root.append(time_element) elif anytext_elements: LOGGER.debug('Adding anytext') root.extend(anytext_elements) elif (par_count > 1): LOGGER.debug('ogc:And query (%d predicates)', par_count) # Since more than 1 parameter, append the AND logical operator logical_and = etree.Element(util.nspath_eval('ogc:And', context.namespaces)) if bbox_element is not None: logical_and.append(bbox_element) if time_element is not None: logical_and.append(time_element) if anytext_elements is not None: logical_and.extend(anytext_elements) root.append(logical_and) # Render etree to string XML LOGGER.debug(etree.tostring(root, encoding='unicode')) return etree.tostring(root, encoding='unicode')
def kvp2filterxml(kvp, context, profiles): ''' transform kvp to filter XML string ''' bbox_element = None time_element = None anytext_elements = [] query_temporal_by_iso = False if profiles is not None and 'plugins' in profiles and 'APISO' in profiles[ 'plugins']: query_temporal_by_iso = True # Count parameters par_count = 0 for p in ['q', 'bbox', 'time']: if p in kvp and kvp[p] != '': par_count += 1 # Create root element for FilterXML root = etree.Element(util.nspath_eval('ogc:Filter', context.namespaces)) # bbox to FilterXML if 'bbox' in kvp and kvp['bbox'] != '': LOGGER.debug('Detected bbox parameter') bbox_list = [x.strip() for x in kvp['bbox'].split(',')] bbox_element = etree.Element( util.nspath_eval('ogc:BBOX', context.namespaces)) el = etree.Element( util.nspath_eval('ogc:PropertyName', context.namespaces)) el.text = 'ows:BoundingBox' bbox_element.append(el) env = etree.Element( util.nspath_eval('gml:Envelope', context.namespaces)) el = etree.Element( util.nspath_eval('gml:lowerCorner', context.namespaces)) if len(bbox_list) == 5: # add srsName LOGGER.debug('Found CRS') env.attrib['srsName'] = bbox_list[4] else: LOGGER.debug('Assuming 4326') env.attrib['srsName'] = 'urn:ogc:def:crs:OGC:1.3:CRS84' if not validate_4326(bbox_list): msg = '4326 coordinates out of range: %s' % bbox_list LOGGER.error(msg) raise RuntimeError(msg) try: el.text = "%s %s" % (bbox_list[0], bbox_list[1]) except Exception as err: errortext = 'Exception: OpenSearch bbox not valid.\nError: %s.' % str( err) LOGGER.exception(errortext) env.append(el) el = etree.Element( util.nspath_eval('gml:upperCorner', context.namespaces)) try: el.text = "%s %s" % (bbox_list[2], bbox_list[3]) except Exception as err: errortext = 'Exception: OpenSearch bbox not valid.\nError: %s.' % str( err) LOGGER.exception(errortext) env.append(el) bbox_element.append(env) # q to FilterXML if 'q' in kvp and kvp['q'] != '': LOGGER.debug('Detected q parameter') qvals = kvp['q'].split() LOGGER.debug(qvals) if len(qvals) > 1: par_count += 1 for qval in qvals: LOGGER.debug('processing q token') anytext_element = etree.Element( util.nspath_eval('ogc:PropertyIsEqualTo', context.namespaces)) el = etree.Element( util.nspath_eval('ogc:PropertyName', context.namespaces)) el.text = 'csw:AnyText' anytext_element.append(el) el = etree.Element( util.nspath_eval('ogc:Literal', context.namespaces)) el.text = qval anytext_element.append(el) anytext_elements.append(anytext_element) if ('start' in kvp or 'stop' in kvp) and 'time' not in kvp: LOGGER.debug('Detected start/stop in KVP') kvp['time'] = '' if 'start' in kvp and kvp['start'] != '': kvp['time'] = kvp['start'] + '/' if 'stop' in kvp and kvp['stop'] != '': if len(kvp['time']) > 0: kvp['time'] += kvp['stop'] else: kvp['time'] = '/' + kvp['stop'] LOGGER.debug('new KVP time: {}'.format(kvp['time'])) # time to FilterXML if 'time' in kvp and kvp['time'] != '': LOGGER.debug('Detected time parameter %s', kvp['time']) time_list = kvp['time'].split("/") LOGGER.debug('TIMELIST: %s', time_list) if len(time_list) == 2: if '' not in time_list: # both dates present LOGGER.debug('Both dates present') if query_temporal_by_iso: LOGGER.debug('Querying by ISO data extent') time_element = etree.Element( util.nspath_eval('ogc:And', context.namespaces)) begin_element = etree.Element( util.nspath_eval('ogc:PropertyIsGreaterThanOrEqualTo', context.namespaces)) etree.SubElement( begin_element, util.nspath_eval('ogc:PropertyName', context.namespaces )).text = 'apiso:TempExtent_begin' etree.SubElement( begin_element, util.nspath_eval( 'ogc:Literal', context.namespaces)).text = time_list[0] end_element = etree.Element( util.nspath_eval('ogc:PropertyIsLessThanOrEqualTo', context.namespaces)) etree.SubElement( end_element, util.nspath_eval( 'ogc:PropertyName', context.namespaces)).text = 'apiso:TempExtent_end' etree.SubElement( end_element, util.nspath_eval( 'ogc:Literal', context.namespaces)).text = time_list[1] time_element.append(begin_element) time_element.append(end_element) else: LOGGER.debug('Querying by DC date') time_element = etree.Element( util.nspath_eval('ogc:PropertyIsBetween', context.namespaces)) el = etree.Element( util.nspath_eval('ogc:PropertyName', context.namespaces)) el.text = 'dc:date' time_element.append(el) el = etree.Element( util.nspath_eval('ogc:LowerBoundary', context.namespaces)) el2 = etree.Element( util.nspath_eval('ogc:Literal', context.namespaces)) el2.text = time_list[0] el.append(el2) time_element.append(el) el = etree.Element( util.nspath_eval('ogc:UpperBoundary', context.namespaces)) el2 = etree.Element( util.nspath_eval('ogc:Literal', context.namespaces)) el2.text = time_list[1] el.append(el2) time_element.append(el) else: # one is empty LOGGER.debug('Querying by open-ended date') if time_list == ['', '']: par_count -= 1 # One of two is empty elif time_list[1] == '': # start datetime but no end datetime time_element = etree.Element( util.nspath_eval('ogc:PropertyIsGreaterThanOrEqualTo', context.namespaces)) el = etree.Element( util.nspath_eval('ogc:PropertyName', context.namespaces)) if query_temporal_by_iso: el.text = 'apiso:TempExtent_begin' else: el.text = 'dc:date' time_element.append(el) el = etree.Element( util.nspath_eval('ogc:Literal', context.namespaces)) el.text = time_list[0] time_element.append(el) else: # end datetime but no start datetime time_element = etree.Element( util.nspath_eval('ogc:PropertyIsLessThanOrEqualTo', context.namespaces)) el = etree.Element( util.nspath_eval('ogc:PropertyName', context.namespaces)) if query_temporal_by_iso: el.text = 'apiso:TempExtent_end' else: el.text = 'dc:date' time_element.append(el) el = etree.Element( util.nspath_eval('ogc:Literal', context.namespaces)) el.text = time_list[1] time_element.append(el) elif ((len(time_list) == 1) and ('' not in time_list)): LOGGER.debug('Querying time instant via dc:date') # This is an equal request time_element = etree.Element( util.nspath_eval('ogc:PropertyIsEqualTo', context.namespaces)) el = etree.Element( util.nspath_eval('ogc:PropertyName', context.namespaces)) el.text = 'dc:date' time_element.append(el) el = etree.Element( util.nspath_eval('ogc:Literal', context.namespaces)) el.text = time_list[0] time_element.append(el) else: # Error errortext = 'Exception: OpenSearch time not valid: %s.' % str( kvp['time']) LOGGER.error(errortext) if par_count == 0: return '' elif par_count == 1: LOGGER.debug('Single predicate filter') # Only one OpenSearch parameter exists if 'bbox' in kvp and kvp['bbox'] != '': LOGGER.debug('Adding bbox') root.append(bbox_element) elif time_element is not None: LOGGER.debug('Adding time') root.append(time_element) elif anytext_elements: LOGGER.debug('Adding anytext') root.extend(anytext_elements) elif (par_count > 1): LOGGER.debug('ogc:And query (%d predicates)', par_count) # Since more than 1 parameter, append the AND logical operator logical_and = etree.Element( util.nspath_eval('ogc:And', context.namespaces)) if bbox_element is not None: logical_and.append(bbox_element) if time_element is not None: logical_and.append(time_element) if anytext_elements is not None: logical_and.extend(anytext_elements) root.append(logical_and) # Render etree to string XML LOGGER.debug(etree.tostring(root, encoding='unicode')) return etree.tostring(root, encoding='unicode')
def kvp2filterxml(kvp, context): """ transform kvp to filter XML string """ bbox_element = None time_element = None anytext_elements = [] # Count parameters par_count = 0 for p in ["q", "bbox", "time"]: if p in kvp and kvp[p] != "": par_count += 1 # Create root element for FilterXML root = etree.Element(util.nspath_eval("ogc:Filter", context.namespaces)) # bbox to FilterXML if "bbox" in kvp and kvp["bbox"] != "": LOGGER.debug("Detected bbox parameter") bbox_list = [x.strip() for x in kvp["bbox"].split(",")] bbox_element = etree.Element(util.nspath_eval("ogc:BBOX", context.namespaces)) el = etree.Element(util.nspath_eval("ogc:PropertyName", context.namespaces)) el.text = "ows:BoundingBox" bbox_element.append(el) env = etree.Element(util.nspath_eval("gml:Envelope", context.namespaces)) el = etree.Element(util.nspath_eval("gml:lowerCorner", context.namespaces)) if len(bbox_list) == 5: # add srsName LOGGER.debug("Found CRS") env.attrib["srsName"] = bbox_list[4] else: LOGGER.debug("Assuming 4326") env.attrib["srsName"] = "urn:ogc:def:crs:OGC:1.3:CRS84" if not util.validate_4326(bbox_list): msg = "4326 coordinates out of range: %s" % bbox_list LOGGER.debug(msg) raise RuntimeError(msg) try: el.text = "%s %s" % (bbox_list[0], bbox_list[1]) except Exception as err: errortext = "Exception: OpenSearch bbox not valid.\nError: %s." % str(err) LOGGER.debug(errortext) env.append(el) el = etree.Element(util.nspath_eval("gml:upperCorner", context.namespaces)) try: el.text = "%s %s" % (bbox_list[2], bbox_list[3]) except Exception as err: errortext = "Exception: OpenSearch bbox not valid.\nError: %s." % str(err) LOGGER.debug(errortext) env.append(el) bbox_element.append(env) # q to FilterXML if "q" in kvp and kvp["q"] != "": LOGGER.debug("Detected q parameter") qvals = kvp["q"].split() LOGGER.debug(qvals) if len(qvals) > 1: par_count += 1 for qval in qvals: LOGGER.debug("processing q token") anytext_element = etree.Element(util.nspath_eval("ogc:PropertyIsEqualTo", context.namespaces)) el = etree.Element(util.nspath_eval("ogc:PropertyName", context.namespaces)) el.text = "csw:AnyText" anytext_element.append(el) el = etree.Element(util.nspath_eval("ogc:Literal", context.namespaces)) if six.PY2: el.text = qval.decode("utf8") else: el.text = qval anytext_element.append(el) anytext_elements.append(anytext_element) # time to FilterXML if "time" in kvp and kvp["time"] != "": LOGGER.debug("Detected time parameter %s", kvp["time"]) time_list = kvp["time"].split("/") if len(time_list) == 2: LOGGER.debug("TIMELIST: %s", time_list) # This is a normal request if "" not in time_list: LOGGER.debug("Both dates present") # Both dates are present time_element = etree.Element(util.nspath_eval("ogc:PropertyIsBetween", context.namespaces)) el = etree.Element(util.nspath_eval("ogc:PropertyName", context.namespaces)) el.text = "dc:date" time_element.append(el) el = etree.Element(util.nspath_eval("ogc:LowerBoundary", context.namespaces)) el2 = etree.Element(util.nspath_eval("ogc:Literal", context.namespaces)) el2.text = time_list[0] el.append(el2) time_element.append(el) el = etree.Element(util.nspath_eval("ogc:UpperBoundary", context.namespaces)) el2 = etree.Element(util.nspath_eval("ogc:Literal", context.namespaces)) el2.text = time_list[1] el.append(el2) time_element.append(el) else: if time_list == ["", ""]: par_count -= 1 # One of two is empty elif time_list[1] is "": time_element = etree.Element( util.nspath_eval("ogc:PropertyIsGreaterThanOrEqualTo", context.namespaces) ) el = etree.Element(util.nspath_eval("ogc:PropertyName", context.namespaces)) el.text = "dc:date" time_element.append(el) el = etree.Element(util.nspath_eval("ogc:Literal", context.namespaces)) el.text = time_list[0] time_element.append(el) else: time_element = etree.Element( util.nspath_eval("ogc:PropertyIsLessThanOrEqualTo", context.namespaces) ) el = etree.Element(util.nspath_eval("ogc:PropertyName", context.namespaces)) el.text = "dc:date" time_element.append(el) el = etree.Element(util.nspath_eval("ogc:Literal", context.namespaces)) el.text = time_list[1] time_element.append(el) elif (len(time_list) == 1) and ("" not in time_list): # This is an equal request time_element = etree.Element(util.nspath_eval("ogc:PropertyIsEqualTo", context.namespaces)) el = etree.Element(util.nspath_eval("ogc:PropertyName", context.namespaces)) el.text = "dc:date" time_element.append(el) el = etree.Element(util.nspath_eval("ogc:Literal", context.namespaces)) el.text = time_list[0] time_element.append(el) else: # Error errortext = "Exception: OpenSearch time not valid: %s." % str(kvp["time"]) LOGGER.debug(errortext) if par_count == 0: return "" elif par_count == 1: LOGGER.debug("Single predicate filter") # Only one OpenSearch parameter exists if "bbox" in kvp and kvp["bbox"] != "": LOGGER.debug("Adding bbox") root.append(bbox_element) elif time_element: LOGGER.debug("Adding time") root.append(time_element) elif anytext_elements: LOGGER.debug("Adding anytext") root.extend(anytext_elements) elif par_count > 1: LOGGER.debug("ogc:And query (%d predicates)", par_count) # Since more than 1 parameter, append the AND logical operator logical_and = etree.Element(util.nspath_eval("ogc:And", context.namespaces)) if bbox_element is not None: logical_and.append(bbox_element) if time_element is not None: logical_and.append(time_element) if anytext_elements is not None: logical_and.extend(anytext_elements) root.append(logical_and) # Render etree to string XML LOGGER.debug(etree.tostring(root, encoding="unicode")) return etree.tostring(root, encoding="unicode")
def _write_response(self): """ Generate response """ # set HTTP response headers and XML declaration xmldecl = '' appinfo = '' LOGGER.info('Writing response.') #import webbrowser # neuer code für unsere API html anzeigen zu können TAN #if self.response == 'a': #print('ja') #new = 2 # open in a new tab, if possible #url = "'/usr/lib/python3.5/site-packages/pycsw/test.html'" #webbrowser.open(url,new=new) #open('/usr/lib/python3.5/site-packages/pycsw/test.html') #print('nein') if hasattr(self, 'soap') and self.soap: self._gen_soap_wrapper() if etree.__version__ >= '3.5.0': # remove superfluous namespaces etree.cleanup_namespaces(self.response, keep_ns_prefixes=self.context.keep_ns_prefixes) response = etree.tostring(self.response, pretty_print=self.pretty_print, encoding='unicode') # Funktion wird aufgerufen um xml in json umzuwandeln, wenn es in der url angegeben ist if (isinstance(self.kvp, dict) and 'outputformat' in self.kvp and self.kvp['outputformat'] == 'application/json'): self.contenttype = self.kvp['outputformat'] from pycsw.core.formats import fmt_json response = fmt_json.xml2json(response, self.context.namespaces, self.pretty_print) # new requests for the similarities should be always in json format (@author: Anika Graupner) elif (isinstance(self.kvp, dict) and 'request' in self.kvp and self.kvp['request'] == 'GetSimilarRecords'): if (isinstance(self.kvp, dict) and 'outputformat' in self.kvp and self.kvp['outputformat'] == 'application/xml'): if 'outputformat' in self.kvp: self.contenttype = self.kvp['outputformat'] else: self.contenttype = self.mimetype xmldecl = ('<?xml version="1.0" encoding="%s" standalone="no"?>' '\n' % self.encoding) appinfo = '<!-- pycsw %s -->\n' % self.context.version else: self.contenttype = self.kvp['request'] from pycsw.core.formats import fmt_json response = fmt_json.xml2json(response, self.context.namespaces, self.pretty_print) elif (isinstance(self.kvp, dict) and 'request' in self.kvp and self.kvp['request'] == 'GetSimilarityBBox'): if (isinstance(self.kvp, dict) and 'outputformat' in self.kvp and self.kvp['outputformat'] == 'application/xml'): if 'outputformat' in self.kvp: self.contenttype = self.kvp['outputformat'] else: self.contenttype = self.mimetype xmldecl = ('<?xml version="1.0" encoding="%s" standalone="no"?>' '\n' % self.encoding) appinfo = '<!-- pycsw %s -->\n' % self.context.version else: self.contenttype = self.kvp['request'] from pycsw.core.formats import fmt_json response = fmt_json.xml2json(response, self.context.namespaces, self.pretty_print) else: # it's XML if 'outputformat' in self.kvp: self.contenttype = self.kvp['outputformat'] else: self.contenttype = self.mimetype xmldecl = ('<?xml version="1.0" encoding="%s" standalone="no"?>' '\n' % self.encoding) appinfo = '<!-- pycsw %s -->\n' % self.context.version if isinstance(self.contenttype, bytes): self.contenttype = self.contenttype.decode() s = (u'%s%s%s' % (xmldecl, appinfo, response)).encode(self.encoding) LOGGER.debug('Response code: %s', self.context.response_codes[self.status]) LOGGER.debug('Response:\n%s', s) return [self.context.response_codes[self.status], s]
def _parse_sos(context, repos, record, identifier, version): from owslib.sos import SensorObservationService bboxs = [] recobjs = [] serviceobj = repos.dataset() if version == '1.0.0': schema = 'http://www.opengis.net/sos/1.0' else: schema = 'http://www.opengis.net/sos/2.0' md = SensorObservationService(record, version=version) # generate record of service instance _set(context, serviceobj, 'pycsw:Identifier', identifier) _set(context, serviceobj, 'pycsw:Typename', 'csw:Record') _set(context, serviceobj, 'pycsw:Schema', schema) _set(context, serviceobj, 'pycsw:MdSource', record) _set(context, serviceobj, 'pycsw:InsertDate', util.get_today_and_now()) _set(context, serviceobj, 'pycsw:AnyText', util.get_anytext(etree.tostring(md._capabilities))) _set(context, serviceobj, 'pycsw:Type', 'service') _set(context, serviceobj, 'pycsw:Title', md.identification.title) _set(context, serviceobj, 'pycsw:Abstract', md.identification.abstract) _set(context, serviceobj, 'pycsw:Keywords', ','.join(md.identification.keywords)) _set(context, serviceobj, 'pycsw:Creator', md.provider.contact.name) _set(context, serviceobj, 'pycsw:Publisher', md.provider.name) _set(context, serviceobj, 'pycsw:Contributor', md.provider.contact.name) _set(context, serviceobj, 'pycsw:OrganizationName', md.provider.contact.name) _set(context, serviceobj, 'pycsw:AccessConstraints', md.identification.accessconstraints) _set(context, serviceobj, 'pycsw:OtherConstraints', md.identification.fees) _set(context, serviceobj, 'pycsw:Source', record) _set(context, serviceobj, 'pycsw:Format', md.identification.type) _set(context, serviceobj, 'pycsw:CRS', 'urn:ogc:def:crs:EPSG:6.11:4326') _set(context, serviceobj, 'pycsw:DistanceUOM', 'degrees') _set(context, serviceobj, 'pycsw:ServiceType', 'OGC:SOS') _set(context, serviceobj, 'pycsw:ServiceTypeVersion', md.identification.version) _set(context, serviceobj, 'pycsw:Operation', ','.join([d.name for d in md.operations])) _set(context, serviceobj, 'pycsw:OperatesOn', ','.join(list(md.contents))) _set(context, serviceobj, 'pycsw:CouplingType', 'tight') links = [ '%s,OGC-SOS Sensor Observation Service,OGC:SOS,%s' % (identifier, md.url), ] _set(context, serviceobj, 'pycsw:Links', '^'.join(links)) # generate record foreach offering LOGGER.debug('Harvesting %d SOS ObservationOffering\'s ', len(md.contents)) for offering in md.contents: recobj = repos.dataset() identifier2 = '%s-%s' % (identifier, md.contents[offering].id) _set(context, recobj, 'pycsw:Identifier', identifier2) _set(context, recobj, 'pycsw:Typename', 'csw:Record') _set(context, recobj, 'pycsw:Schema', schema) _set(context, recobj, 'pycsw:MdSource', record) _set(context, recobj, 'pycsw:InsertDate', util.get_today_and_now()) _set(context, recobj, 'pycsw:Type', 'dataset') _set(context, recobj, 'pycsw:ParentIdentifier', identifier) _set(context, recobj, 'pycsw:Title', md.contents[offering].description) _set(context, recobj, 'pycsw:Abstract', md.contents[offering].description) _set(context, recobj, 'pycsw:TempExtent_begin', util.datetime2iso8601(md.contents[offering].begin_position)) _set(context, recobj, 'pycsw:TempExtent_end', util.datetime2iso8601(md.contents[offering].end_position)) #For observed_properties that have mmi url or urn, we simply want the observation name. observed_properties = [] for obs in md.contents[offering].observed_properties: #Observation is stored as urn representation: urn:ogc:def:phenomenon:mmisw.org:cf:sea_water_salinity if obs.lower().startswith(('urn:', 'x-urn')): observed_properties.append(obs.rsplit(':', 1)[-1]) #Observation is stored as uri representation: http://mmisw.org/ont/cf/parameter/sea_floor_depth_below_sea_surface elif obs.lower().startswith(('http://', 'https://')): observed_properties.append(obs.rsplit('/', 1)[-1]) else: observed_properties.append(obs) #Build anytext from description and the observed_properties. anytext = [] anytext.append(md.contents[offering].description) anytext.extend(observed_properties) _set(context, recobj, 'pycsw:AnyText', util.get_anytext(anytext)) _set(context, recobj, 'pycsw:Keywords', ','.join(observed_properties)) bbox = md.contents[offering].bbox if bbox is not None: tmp = '%s,%s,%s,%s' % (bbox[0], bbox[1], bbox[2], bbox[3]) wkt_polygon = util.bbox2wktpolygon(tmp) _set(context, recobj, 'pycsw:BoundingBox', wkt_polygon) _set(context, recobj, 'pycsw:CRS', md.contents[offering].bbox_srs.id) _set(context, recobj, 'pycsw:DistanceUOM', 'degrees') bboxs.append(wkt_polygon) _set(context, recobj, 'pycsw:XML', caps2iso(recobj, md, context)) recobjs.append(recobj) # Derive a bbox based on aggregated featuretype bbox values bbox_agg = util.bbox_from_polygons(bboxs) if bbox_agg is not None: _set(context, serviceobj, 'pycsw:BoundingBox', bbox_agg) _set(context, serviceobj, 'pycsw:XML', caps2iso(serviceobj, md, context)) recobjs.insert(0, serviceobj) return recobjs
def _parse_wcs(context, repos, record, identifier): from owslib.wcs import WebCoverageService bboxs = [] recobjs = [] serviceobj = repos.dataset() md = WebCoverageService(record, '1.0.0') # generate record of service instance _set(context, serviceobj, 'pycsw:Identifier', identifier) _set(context, serviceobj, 'pycsw:Typename', 'csw:Record') _set(context, serviceobj, 'pycsw:Schema', 'http://www.opengis.net/wcs') _set(context, serviceobj, 'pycsw:MdSource', record) _set(context, serviceobj, 'pycsw:InsertDate', util.get_today_and_now()) _set(context, serviceobj, 'pycsw:AnyText', util.get_anytext(etree.tostring(md._capabilities))) _set(context, serviceobj, 'pycsw:Type', 'service') _set(context, serviceobj, 'pycsw:Title', md.identification.title) _set(context, serviceobj, 'pycsw:Abstract', md.identification.abstract) _set(context, serviceobj, 'pycsw:Keywords', ','.join(md.identification.keywords)) _set(context, serviceobj, 'pycsw:Creator', md.provider.contact.name) _set(context, serviceobj, 'pycsw:Publisher', md.provider.name) _set(context, serviceobj, 'pycsw:Contributor', md.provider.contact.name) _set(context, serviceobj, 'pycsw:OrganizationName', md.provider.contact.name) _set(context, serviceobj, 'pycsw:AccessConstraints', md.identification.accessConstraints) _set(context, serviceobj, 'pycsw:OtherConstraints', md.identification.fees) _set(context, serviceobj, 'pycsw:Source', record) _set(context, serviceobj, 'pycsw:Format', md.identification.type) _set(context, serviceobj, 'pycsw:CRS', 'urn:ogc:def:crs:EPSG:6.11:4326') _set(context, serviceobj, 'pycsw:DistanceUOM', 'degrees') _set(context, serviceobj, 'pycsw:ServiceType', 'OGC:WCS') _set(context, serviceobj, 'pycsw:ServiceTypeVersion', md.identification.version) _set(context, serviceobj, 'pycsw:Operation', ','.join([d.name for d in md.operations])) _set(context, serviceobj, 'pycsw:OperatesOn', ','.join(list(md.contents))) _set(context, serviceobj, 'pycsw:CouplingType', 'tight') links = [ '%s,OGC-WCS Web Coverage Service,OGC:WCS,%s' % (identifier, md.url) ] _set(context, serviceobj, 'pycsw:Links', '^'.join(links)) # generate record foreach coverage LOGGER.debug('Harvesting %d WCS coverages ' % len(md.contents)) for coverage in md.contents: recobj = repos.dataset() identifier2 = '%s-%s' % (identifier, md.contents[coverage].id) _set(context, recobj, 'pycsw:Identifier', identifier2) _set(context, recobj, 'pycsw:Typename', 'csw:Record') _set(context, recobj, 'pycsw:Schema', 'http://www.opengis.net/wcs') _set(context, recobj, 'pycsw:MdSource', record) _set(context, recobj, 'pycsw:InsertDate', util.get_today_and_now()) _set(context, recobj, 'pycsw:Type', 'dataset') _set(context, recobj, 'pycsw:ParentIdentifier', identifier) _set(context, recobj, 'pycsw:Title', md.contents[coverage].title) _set(context, recobj, 'pycsw:Abstract', md.contents[coverage].abstract) _set(context, recobj, 'pycsw:Keywords', ','.join(md.contents[coverage].keywords)) _set(context, recobj, 'pycsw:AnyText', util.get_anytext([md.contents[coverage].title, md.contents[coverage].abstract, ','.join(md.contents[coverage].keywords)])) bbox = md.contents[coverage].boundingBoxWGS84 if bbox is not None: tmp = '%s,%s,%s,%s' % (bbox[0], bbox[1], bbox[2], bbox[3]) wkt_polygon = util.bbox2wktpolygon(tmp) _set(context, recobj, 'pycsw:BoundingBox', wkt_polygon) _set(context, recobj, 'pycsw:CRS', 'urn:ogc:def:crs:EPSG:6.11:4326') _set(context, recobj, 'pycsw:DistanceUOM', 'degrees') bboxs.append(wkt_polygon) links = [ '%s,OGC-Web Coverage Service,OGC:WCS,%s' % (md.contents[coverage].id, md.url) ] _set(context, recobj, 'pycsw:Links', '^'.join(links)) _set(context, recobj, 'pycsw:XML', caps2iso(recobj, md, context)) recobjs.append(recobj) # Derive a bbox based on aggregated coverage bbox values bbox_agg = util.bbox_from_polygons(bboxs) if bbox_agg is not None: _set(context, serviceobj, 'pycsw:BoundingBox', bbox_agg) _set(context, serviceobj, 'pycsw:XML', caps2iso(serviceobj, md, context)) recobjs.insert(0, serviceobj) return recobjs
def kvp2filterxml(kvp, context, profiles, fes_version='1.0'): ''' transform kvp to filter XML string ''' bbox_element = None time_element = None anytext_elements = [] query_temporal_by_iso = False eo_parentidentifier_element = None eo_bands_element = None eo_cloudcover_element = None eo_instrument_element = None eo_orbitdirection_element = None eo_orbitnumber_element = None eo_platform_element = None eo_processinglevel_element = None eo_producttype_element = None eo_sensortype_element = None eo_snowcover_element = None if profiles is not None and 'plugins' in profiles and 'APISO' in profiles['plugins']: query_temporal_by_iso = True # Count parameters par_count = 0 for p in ['q','bbox','time']: if p in kvp and kvp[p] != '': par_count += 1 # Create root element for FilterXML root = etree.Element(util.nspath_eval('ogc:Filter', context.namespaces)) # bbox to FilterXML if 'bbox' in kvp and kvp['bbox'] != '': LOGGER.debug('Detected bbox parameter') bbox_list = [x.strip() for x in kvp['bbox'].split(',')] bbox_element = etree.Element(util.nspath_eval('ogc:BBOX', context.namespaces)) el = etree.Element(util.nspath_eval('ogc:PropertyName', context.namespaces)) el.text = 'ows:BoundingBox' bbox_element.append(el) env = etree.Element(util.nspath_eval('gml:Envelope', context.namespaces)) el = etree.Element(util.nspath_eval('gml:lowerCorner', context.namespaces)) if len(bbox_list) == 5: # add srsName LOGGER.debug('Found CRS') env.attrib['srsName'] = bbox_list[4] else: LOGGER.debug('Assuming 4326') env.attrib['srsName'] = 'urn:ogc:def:crs:OGC:1.3:CRS84' if not validate_4326(bbox_list): msg = '4326 coordinates out of range: %s' % bbox_list LOGGER.error(msg) raise RuntimeError(msg) try: el.text = "%s %s" % (bbox_list[0], bbox_list[1]) except Exception as err: errortext = 'Exception: OpenSearch bbox not valid.\nError: %s.' % str(err) LOGGER.exception(errortext) env.append(el) el = etree.Element(util.nspath_eval('gml:upperCorner', context.namespaces)) try: el.text = "%s %s" % (bbox_list[2], bbox_list[3]) except Exception as err: errortext = 'Exception: OpenSearch bbox not valid.\nError: %s.' % str(err) LOGGER.exception(errortext) env.append(el) bbox_element.append(env) # q to FilterXML if 'q' in kvp and kvp['q'] != '': LOGGER.debug('Detected q parameter') qvals = kvp['q'].split() LOGGER.debug(qvals) if len(qvals) > 1: par_count += 1 for qval in qvals: LOGGER.debug('processing q token') anytext_element = etree.Element(util.nspath_eval('ogc:PropertyIsEqualTo', context.namespaces)) el = etree.Element(util.nspath_eval('ogc:PropertyName', context.namespaces)) el.text = 'csw:AnyText' anytext_element.append(el) el = etree.Element(util.nspath_eval('ogc:Literal', context.namespaces)) el.text = qval anytext_element.append(el) anytext_elements.append(anytext_element) if ('start' in kvp or 'stop' in kvp) and 'time' not in kvp: LOGGER.debug('Detected start/stop in KVP') kvp['time'] = '' if 'start' in kvp and kvp['start'] != '': kvp['time'] = kvp['start'] + '/' if 'stop' in kvp and kvp['stop'] != '': if len(kvp['time']) > 0: kvp['time'] += kvp['stop'] else: kvp['time'] = '/' + kvp['stop'] LOGGER.debug('new KVP time: {}'.format(kvp['time'])) # time to FilterXML if 'time' in kvp and kvp['time'] != '': LOGGER.debug('Detected time parameter %s', kvp['time']) time_list = kvp['time'].split("/") LOGGER.debug('TIMELIST: %s', time_list) if len(time_list) == 2: if '' not in time_list: # both dates present LOGGER.debug('Both dates present') if query_temporal_by_iso: LOGGER.debug('Querying by ISO data extent') time_element = etree.Element(util.nspath_eval('ogc:And', context.namespaces)) begin_element = etree.Element(util.nspath_eval('ogc:PropertyIsGreaterThanOrEqualTo', context.namespaces)) etree.SubElement(begin_element, util.nspath_eval('ogc:PropertyName', context.namespaces)).text = 'apiso:TempExtent_begin' etree.SubElement(begin_element, util.nspath_eval('ogc:Literal', context.namespaces)).text = time_list[0] end_element = etree.Element(util.nspath_eval('ogc:PropertyIsLessThanOrEqualTo', context.namespaces)) etree.SubElement(end_element, util.nspath_eval('ogc:PropertyName', context.namespaces)).text = 'apiso:TempExtent_end' etree.SubElement(end_element, util.nspath_eval('ogc:Literal', context.namespaces)).text = time_list[1] time_element.append(begin_element) time_element.append(end_element) else: LOGGER.debug('Querying by DC date') time_element = etree.Element(util.nspath_eval('ogc:PropertyIsBetween', context.namespaces)) el = etree.Element(util.nspath_eval('ogc:PropertyName', context.namespaces)) el.text = 'dc:date' time_element.append(el) el = etree.Element(util.nspath_eval('ogc:LowerBoundary', context.namespaces)) el2 = etree.Element(util.nspath_eval('ogc:Literal', context.namespaces)) el2.text = time_list[0] el.append(el2) time_element.append(el) el = etree.Element(util.nspath_eval('ogc:UpperBoundary', context.namespaces)) el2 = etree.Element(util.nspath_eval('ogc:Literal', context.namespaces)) el2.text = time_list[1] el.append(el2) time_element.append(el) else: # one is empty LOGGER.debug('Querying by open-ended date') if time_list == ['', '']: par_count -= 1 # One of two is empty elif time_list[1] == '': # start datetime but no end datetime time_element = etree.Element(util.nspath_eval('ogc:PropertyIsGreaterThanOrEqualTo', context.namespaces)) el = etree.Element(util.nspath_eval('ogc:PropertyName', context.namespaces)) if query_temporal_by_iso: el.text = 'apiso:TempExtent_begin' else: el.text = 'dc:date' time_element.append(el) el = etree.Element(util.nspath_eval('ogc:Literal', context.namespaces)) el.text = time_list[0] time_element.append(el) else: # end datetime but no start datetime time_element = etree.Element(util.nspath_eval('ogc:PropertyIsLessThanOrEqualTo', context.namespaces)) el = etree.Element(util.nspath_eval('ogc:PropertyName', context.namespaces)) if query_temporal_by_iso: el.text = 'apiso:TempExtent_end' else: el.text = 'dc:date' time_element.append(el) el = etree.Element(util.nspath_eval('ogc:Literal', context.namespaces)) el.text = time_list[1] time_element.append(el) elif ((len(time_list) == 1) and ('' not in time_list)): LOGGER.debug('Querying time instant via dc:date') # This is an equal request time_element = etree.Element(util.nspath_eval('ogc:PropertyIsEqualTo', context.namespaces)) el = etree.Element(util.nspath_eval('ogc:PropertyName', context.namespaces)) el.text = 'dc:date' time_element.append(el) el = etree.Element(util.nspath_eval('ogc:Literal', context.namespaces)) el.text = time_list[0] time_element.append(el) else: # Error errortext = 'Exception: OpenSearch time not valid: %s.' % str(kvp['time']) LOGGER.error(errortext) LOGGER.debug('Processing EO queryables') if not util.is_none_or_empty(kvp.get('eo:parentidentifier')): par_count += 1 eo_parentidentifier_element = etree.Element(util.nspath_eval('ogc:PropertyIsEqualTo', context.namespaces)) etree.SubElement(eo_parentidentifier_element, util.nspath_eval('ogc:PropertyName', context.namespaces)).text = 'apiso:ParentIdentifier' etree.SubElement(eo_parentidentifier_element, util.nspath_eval( 'ogc:Literal', context.namespaces)).text = kvp['eo:parentidentifier'] if not util.is_none_or_empty(kvp.get('eo:producttype')): par_count += 1 eo_producttype_element = etree.Element(util.nspath_eval('ogc:PropertyIsLike', context.namespaces), matchCase='false', wildCard='*', singleChar='?', escapeChar='\\') etree.SubElement(eo_producttype_element, util.nspath_eval('ogc:PropertyName', context.namespaces)).text = 'apiso:Subject' etree.SubElement(eo_producttype_element, util.nspath_eval( 'ogc:Literal', context.namespaces)).text = '*eo:productType:%s*' % kvp['eo:producttype'] if not util.is_none_or_empty(kvp.get('eo:platform')): par_count += 1 eo_platform_element = etree.Element(util.nspath_eval('ogc:PropertyIsEqualTo', context.namespaces)) etree.SubElement(eo_platform_element, util.nspath_eval('ogc:PropertyName', context.namespaces)).text = 'apiso:Platform' etree.SubElement(eo_platform_element, util.nspath_eval( 'ogc:Literal', context.namespaces)).text = kvp['eo:platform'] if not util.is_none_or_empty(kvp.get('eo:processinglevel')): par_count += 1 eo_processinglevel_element = etree.Element(util.nspath_eval('ogc:PropertyIsLike', context.namespaces), matchCase='false', wildCard='*', singleChar='?', escapeChar='\\') etree.SubElement(eo_processinglevel_element, util.nspath_eval('ogc:PropertyName', context.namespaces)).text = 'apiso:Subject' etree.SubElement(eo_processinglevel_element, util.nspath_eval( 'ogc:Literal', context.namespaces)).text = '*eo:processingLevel:%s*' % kvp['eo:processinglevel'] if not util.is_none_or_empty(kvp.get('eo:instrument')): par_count += 1 eo_instrument_element = etree.Element(util.nspath_eval('ogc:PropertyIsEqualTo', context.namespaces)) etree.SubElement(eo_instrument_element, util.nspath_eval('ogc:PropertyName', context.namespaces)).text = 'apiso:Instrument' etree.SubElement(eo_instrument_element, util.nspath_eval( 'ogc:Literal', context.namespaces)).text = kvp['eo:instrument'] if not util.is_none_or_empty(kvp.get('eo:sensortype')): par_count += 1 eo_sensortype_element = etree.Element(util.nspath_eval('ogc:PropertyIsEqualTo', context.namespaces)) etree.SubElement(eo_sensortype_element, util.nspath_eval('ogc:PropertyName', context.namespaces)).text = 'apiso:SensorType' etree.SubElement(eo_sensortype_element, util.nspath_eval( 'ogc:Literal', context.namespaces)).text = kvp['eo:sensortype'] if not util.is_none_or_empty(kvp.get('eo:cloudcover')): par_count += 1 eo_cloudcover_element = evaluate_literal(context, 'apiso:CloudCover', kvp['eo:cloudcover']) if not util.is_none_or_empty(kvp.get('eo:snowcover')): par_count += 1 eo_snowcover_element = etree.Element(util.nspath_eval('ogc:PropertyIsLike', context.namespaces), matchCase='false', wildCard='*', singleChar='?', escapeChar='\\') etree.SubElement(eo_snowcover_element, util.nspath_eval('ogc:PropertyName', context.namespaces)).text = 'apiso:Subject' etree.SubElement(eo_snowcover_element, util.nspath_eval( 'ogc:Literal', context.namespaces)).text = '*eo:snowCover:%s*' % kvp['eo:snowcover'] if not util.is_none_or_empty(kvp.get('eo:spectralrange')): par_count += 1 eo_bands_element = etree.Element(util.nspath_eval('ogc:PropertyIsLike', context.namespaces), matchCase='false', wildCard='*', singleChar='?', escapeChar='\\') etree.SubElement(eo_bands_element, util.nspath_eval('ogc:PropertyName', context.namespaces)).text = 'apiso:Bands' etree.SubElement(eo_bands_element, util.nspath_eval( 'ogc:Literal', context.namespaces)).text = '*%s*' % kvp['eo:spectralrange'] if not util.is_none_or_empty(kvp.get('eo:orbitnumber')): par_count += 1 eo_orbitnumber_element = etree.Element(util.nspath_eval('ogc:PropertyIsLike', context.namespaces), matchCase='false', wildCard='*', singleChar='?', escapeChar='\\') etree.SubElement(eo_orbitnumber_element, util.nspath_eval('ogc:PropertyName', context.namespaces)).text = 'apiso:Subject' etree.SubElement(eo_orbitnumber_element, util.nspath_eval( 'ogc:Literal', context.namespaces)).text = '*eo:orbitNumber:%s*' % kvp['eo:orbitnumber'] if not util.is_none_or_empty(kvp.get('eo:orbitdirection')): par_count += 1 eo_orbitdirection_element = etree.Element(util.nspath_eval('ogc:PropertyIsLike', context.namespaces), matchCase='false', wildCard='*', singleChar='?', escapeChar='\\') etree.SubElement(eo_orbitdirection_element, util.nspath_eval('ogc:PropertyName', context.namespaces)).text = 'apiso:Subject' etree.SubElement(eo_orbitdirection_element, util.nspath_eval( 'ogc:Literal', context.namespaces)).text = '*eo:orbitDirection:%s*' % kvp['eo:orbitdirection'] LOGGER.info('Query parameter count: %s', par_count) if par_count == 0: return '' elif par_count == 1: LOGGER.debug('Single predicate filter') # Only one OpenSearch parameter exists if 'bbox' in kvp and kvp['bbox'] != '': LOGGER.debug('Adding bbox') root.append(bbox_element) elif time_element is not None: LOGGER.debug('Adding time') root.append(time_element) elif anytext_elements: LOGGER.debug('Adding anytext') root.extend(anytext_elements) elif par_count > 1: LOGGER.debug('ogc:And query (%d predicates)', par_count) # Since more than 1 parameter, append the AND logical operator logical_and = etree.Element(util.nspath_eval('ogc:And', context.namespaces)) if bbox_element is not None: logical_and.append(bbox_element) if time_element is not None: logical_and.append(time_element) if anytext_elements is not None: logical_and.extend(anytext_elements) root.append(logical_and) if par_count == 1: node_to_append = root elif par_count > 1: node_to_append = logical_and LOGGER.debug('Adding EO queryables') for eo_element in [eo_producttype_element, eo_platform_element, eo_instrument_element, eo_sensortype_element, eo_cloudcover_element, eo_snowcover_element, eo_bands_element, eo_orbitnumber_element, eo_orbitdirection_element, eo_processinglevel_element, eo_parentidentifier_element]: if eo_element is not None: node_to_append.append(eo_element) # Render etree to string XML LOGGER.debug(etree.tostring(root, encoding='unicode')) filterstring = etree.tostring(root, encoding='unicode') if fes_version == '2.0': filterstring = filterstring.replace('PropertyName', 'ValueReference')\ .replace('xmlns:ogc="http://www.opengis.net/ogc"', 'xmlns:fes20="http://www.opengis.net/fes/2.0"')\ .replace('ogc:', 'fes20:') return filterstring
def response(self, response, kvp, repository, server_url): """process OAI-PMH request""" mode = kvp.pop('mode', None) if 'config' in kvp: config_val = kvp.pop('config') url = '%smode=oaipmh' % util.bind_url(server_url) node = etree.Element(util.nspath_eval('oai:OAI-PMH', self.namespaces), nsmap=self.namespaces) node.set( util.nspath_eval('xsi:schemaLocation', self.namespaces), '%s http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd' % self.namespaces['oai']) LOGGER.info(etree.tostring(node)) etree.SubElement(node, util.nspath_eval( 'oai:responseDate', self.namespaces)).text = util.get_today_and_now() etree.SubElement(node, util.nspath_eval('oai:request', self.namespaces), attrib=kvp).text = url if 'verb' not in kvp: etree.SubElement( node, util.nspath_eval('oai:error', self.namespaces), code='badArgument').text = 'Missing \'verb\' parameter' return node if kvp['verb'] not in self.request_model.keys(): etree.SubElement( node, util.nspath_eval('oai:error', self.namespaces), code='badArgument').text = 'Unknown verb \'%s\'' % kvp['verb'] return node if util.xmltag_split(response.tag) == 'ExceptionReport': etree.SubElement(node, util.nspath_eval('oai:error', self.namespaces), code='badArgument').text = response.xpath( '//ows:ExceptionText|//ows20:ExceptionText', namespaces=self.context.namespaces)[0].text return node verb = kvp.pop('verb') if verb in ['GetRecord', 'ListIdentifiers', 'ListRecords']: if 'metadataprefix' not in kvp: etree.SubElement(node, util.nspath_eval('oai:error', self.namespaces), code='badArgument' ).text = 'Missing metadataPrefix parameter' return node elif kvp['metadataprefix'] not in self.metadata_formats.keys(): etree.SubElement(node, util.nspath_eval('oai:error', self.namespaces), code='badArgument' ).text = 'Invalid metadataPrefix parameter' return node for key, value in kvp.iteritems(): if key != 'mode' and key not in self.request_model[verb]: etree.SubElement( node, util.nspath_eval('oai:error', self.namespaces), code='badArgument').text = 'Illegal parameter \'%s\'' % key return node verbnode = etree.SubElement( node, util.nspath_eval('oai:%s' % verb, self.namespaces)) if verb == 'Identify': etree.SubElement( verbnode, util.nspath_eval('oai:repositoryName', self.namespaces)).text = self.config.get( 'metadata:main', 'identification_title') etree.SubElement(verbnode, util.nspath_eval('oai:baseURL', self.namespaces)).text = url etree.SubElement( verbnode, util.nspath_eval('oai:protocolVersion', self.namespaces)).text = '2.0' etree.SubElement( verbnode, util.nspath_eval('oai:adminEmail', self.namespaces)).text = self.config.get( 'metadata:main', 'contact_email') etree.SubElement( verbnode, util.nspath_eval( 'oai:earliestDatestamp', self.namespaces)).text = repository.query_insert('min') etree.SubElement( verbnode, util.nspath_eval('oai:deletedRecord', self.namespaces)).text = 'no' etree.SubElement( verbnode, util.nspath_eval( 'oai:granularity', self.namespaces)).text = 'YYYY-MM-DDThh:mm:ssZ' elif verb == 'ListSets': for key, value in self.metadata_sets.iteritems(): setnode = etree.SubElement( verbnode, util.nspath_eval('oai:set', self.namespaces)) etree.SubElement( setnode, util.nspath_eval('oai:setSpec', self.namespaces)).text = key etree.SubElement( setnode, util.nspath_eval('oai:setName', self.namespaces)).text = value[0] elif verb == 'ListMetadataFormats': for key, value in self.metadata_formats.iteritems(): mdfnode = etree.SubElement( verbnode, util.nspath_eval('oai:metadataFormat', self.namespaces)) etree.SubElement( mdfnode, util.nspath_eval('oai:metadataPrefix', self.namespaces)).text = key etree.SubElement( mdfnode, util.nspath_eval('oai:schema', self.namespaces)).text = value['schema'] etree.SubElement( mdfnode, util.nspath_eval( 'oai:metadataNamespace', self.namespaces)).text = value['namespace'] elif verb in ['GetRecord', 'ListIdentifiers', 'ListRecords']: if verb == 'GetRecord': # GetRecordById records = response.getchildren() else: # GetRecords records = response.getchildren()[1].getchildren() for child in records: recnode = etree.SubElement( verbnode, util.nspath_eval('oai:record', self.namespaces)) header = etree.SubElement( recnode, util.nspath_eval('oai:header', self.namespaces)) self._transform_element(header, response, 'oai:identifier') self._transform_element(header, response, 'oai:dateStamp') self._transform_element(header, response, 'oai:setSpec') if verb in ['GetRecord', 'ListRecords']: metadata = etree.SubElement( recnode, util.nspath_eval('oai:metadata', self.namespaces)) if 'metadataprefix' in kvp and kvp[ 'metadataprefix'] == 'oai_dc': child.tag = util.nspath_eval('oai_dc:dc', self.namespaces) metadata.append(child) if verb != 'GetRecord': complete_list_size = response.xpath( '//@numberOfRecordsMatched')[0] next_record = response.xpath('//@nextRecord')[0] cursor = str(int(complete_list_size) - int(next_record) - 1) resumption_token = etree.SubElement( verbnode, util.nspath_eval('oai:resumptionToken', self.namespaces), completeListSize=complete_list_size, cursor=cursor).text = next_record return node
def response(self, response, kvp, repository, server_url): """process OAI-PMH request""" mode = kvp.pop('mode', None) if 'config' in kvp: config_val = kvp.pop('config') url = '%smode=oaipmh' % util.bind_url(server_url) node = etree.Element(util.nspath_eval('oai:OAI-PMH', self.namespaces), nsmap=self.namespaces) node.set(util.nspath_eval('xsi:schemaLocation', self.namespaces), '%s http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd' % self.namespaces['oai']) LOGGER.info(etree.tostring(node)) etree.SubElement(node, util.nspath_eval('oai:responseDate', self.namespaces)).text = util.get_today_and_now() etree.SubElement(node, util.nspath_eval('oai:request', self.namespaces), attrib=kvp).text = url if 'verb' not in kvp: etree.SubElement(node, util.nspath_eval('oai:error', self.namespaces), code='badArgument').text = 'Missing \'verb\' parameter' return node if kvp['verb'] not in self.request_model.keys(): etree.SubElement(node, util.nspath_eval('oai:error', self.namespaces), code='badArgument').text = 'Unknown verb \'%s\'' % kvp['verb'] return node if util.xmltag_split(response.tag) == 'ExceptionReport': etree.SubElement(node, util.nspath_eval('oai:error', self.namespaces), code='badArgument').text = response.xpath('//ows:ExceptionText|//ows20:ExceptionText', namespaces=self.context.namespaces)[0].text return node verb = kvp.pop('verb') if verb in ['GetRecord', 'ListIdentifiers', 'ListRecords']: if 'metadataprefix' not in kvp: etree.SubElement(node, util.nspath_eval('oai:error', self.namespaces), code='badArgument').text = 'Missing metadataPrefix parameter' return node elif kvp['metadataprefix'] not in self.metadata_formats.keys(): etree.SubElement(node, util.nspath_eval('oai:error', self.namespaces), code='badArgument').text = 'Invalid metadataPrefix parameter' return node for key, value in kvp.items(): if key != 'mode' and key not in self.request_model[verb]: etree.SubElement(node, util.nspath_eval('oai:error', self.namespaces), code='badArgument').text = 'Illegal parameter \'%s\'' % key return node verbnode = etree.SubElement(node, util.nspath_eval('oai:%s' % verb, self.namespaces)) if verb == 'Identify': etree.SubElement(verbnode, util.nspath_eval('oai:repositoryName', self.namespaces)).text = self.config.get('metadata:main', 'identification_title') etree.SubElement(verbnode, util.nspath_eval('oai:baseURL', self.namespaces)).text = url etree.SubElement(verbnode, util.nspath_eval('oai:protocolVersion', self.namespaces)).text = '2.0' etree.SubElement(verbnode, util.nspath_eval('oai:adminEmail', self.namespaces)).text = self.config.get('metadata:main', 'contact_email') etree.SubElement(verbnode, util.nspath_eval('oai:earliestDatestamp', self.namespaces)).text = repository.query_insert('min') etree.SubElement(verbnode, util.nspath_eval('oai:deletedRecord', self.namespaces)).text = 'no' etree.SubElement(verbnode, util.nspath_eval('oai:granularity', self.namespaces)).text = 'YYYY-MM-DDThh:mm:ssZ' elif verb == 'ListSets': for key, value in self.metadata_sets.items(): setnode = etree.SubElement(verbnode, util.nspath_eval('oai:set', self.namespaces)) etree.SubElement(setnode, util.nspath_eval('oai:setSpec', self.namespaces)).text = key etree.SubElement(setnode, util.nspath_eval('oai:setName', self.namespaces)).text = value[0] elif verb == 'ListMetadataFormats': for key, value in self.metadata_formats.items(): mdfnode = etree.SubElement(verbnode, util.nspath_eval('oai:metadataFormat', self.namespaces)) etree.SubElement(mdfnode, util.nspath_eval('oai:metadataPrefix', self.namespaces)).text = key etree.SubElement(mdfnode, util.nspath_eval('oai:schema', self.namespaces)).text = value['schema'] etree.SubElement(mdfnode, util.nspath_eval('oai:metadataNamespace', self.namespaces)).text = value['namespace'] elif verb in ['GetRecord', 'ListIdentifiers', 'ListRecords']: if verb == 'GetRecord': # GetRecordById records = response.getchildren() else: # GetRecords records = response.getchildren()[1].getchildren() for child in records: recnode = etree.SubElement(verbnode, util.nspath_eval('oai:record', self.namespaces)) header = etree.SubElement(recnode, util.nspath_eval('oai:header', self.namespaces)) self._transform_element(header, response, 'oai:identifier') self._transform_element(header, response, 'oai:dateStamp') self._transform_element(header, response, 'oai:setSpec') if verb in ['GetRecord', 'ListRecords']: metadata = etree.SubElement(recnode, util.nspath_eval('oai:metadata', self.namespaces)) if 'metadataprefix' in kvp and kvp['metadataprefix'] == 'oai_dc': child.tag = util.nspath_eval('oai_dc:dc', self.namespaces) metadata.append(child) if verb != 'GetRecord': complete_list_size = response.xpath('//@numberOfRecordsMatched')[0] next_record = response.xpath('//@nextRecord')[0] cursor = str(int(complete_list_size) - int(next_record) - 1) resumption_token = etree.SubElement(verbnode, util.nspath_eval('oai:resumptionToken', self.namespaces), completeListSize=complete_list_size, cursor=cursor).text = next_record return node