def _get_envelope(self): """Parse gml:Envelope""" tmp = self._exml.find( util.nspath_eval('gml:Envelope/gml:lowerCorner', self.nsmap)) if tmp is None: raise RuntimeError('Invalid gml:Envelope geometry.\ Missing gml:lowerCorner') else: lower_left = tmp.text tmp = self._exml.find( util.nspath_eval('gml:Envelope/gml:upperCorner', self.nsmap)) if tmp is None: raise RuntimeError('Invalid gml:Envelope geometry.\ Missing gml:upperCorner') else: upper_right = tmp.text llmin = lower_left.split() urmax = upper_right.split() if len(llmin) < 2 or len(urmax) < 2: raise RuntimeError('Invalid gml:Envelope geometry. \ gml:lowerCorner and gml:upperCorner must hold at least x and y') if self.crs.axisorder == 'yx': self.wkt = util.bbox2wktpolygon( '%s,%s,%s,%s' % (llmin[1], llmin[0], urmax[1], urmax[0])) else: self.wkt = util.bbox2wktpolygon( '%s,%s,%s,%s' % (llmin[0], llmin[1], urmax[0], urmax[1]))
def write_keywords(keywords, nsmap): """generate gmd:MD_Keywords construct""" md_keywords = etree.Element(util.nspath_eval('gmd:MD_Keywords', nsmap)) for kw in keywords.split(','): keyword = etree.SubElement(md_keywords, util.nspath_eval('gmd:keyword', nsmap)) etree.SubElement(keyword, util.nspath_eval('gco:CharacterString', nsmap)).text = kw return md_keywords
def write_extent(bbox, nsmap): ''' Generate BBOX extent ''' if bbox is not None: try: bbox2 = util.wkt2geom(bbox) except: return None bounding_box = etree.Element( util.nspath_eval('gm03:GM03_2_1Core.Core.EX_GeographicBoundingBox', NAMESPACES)) etree.SubElement(bounding_box, util.nspath_eval('gm03:northBoundLatitude', nsmap)).text = str(bbox2[3]) etree.SubElement(bounding_box, util.nspath_eval('gm03:southBoundLatitude', nsmap)).text = str(bbox2[1]) etree.SubElement(bounding_box, util.nspath_eval('gm03:eastBoundLongitude', nsmap)).text = str(bbox2[0]) etree.SubElement(bounding_box, util.nspath_eval('gm03:westBoundLongitude', nsmap)).text = str(bbox2[2]) return bounding_box return None
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 _get_envelope(self): """Parse gml:Envelope""" tmp = self._exml.find(util.nspath_eval('gml:Envelope/gml:lowerCorner', self.nsmap)) if tmp is None: raise RuntimeError('Invalid gml:Envelope geometry.\ Missing gml:lowerCorner') else: lower_left = tmp.text tmp = self._exml.find(util.nspath_eval('gml:Envelope/gml:upperCorner', self.nsmap)) if tmp is None: raise RuntimeError('Invalid gml:Envelope geometry.\ Missing gml:upperCorner') else: upper_right = tmp.text llmin = lower_left.split() urmax = upper_right.split() if len(llmin) < 2 or len(urmax) < 2: raise RuntimeError('Invalid gml:Envelope geometry. \ gml:lowerCorner and gml:upperCorner must hold at least x and y') if self.crs.axisorder == 'yx': self.wkt = util.bbox2wktpolygon('%s,%s,%s,%s' % (llmin[1], llmin[0], urmax[1], urmax[0])) else: self.wkt = util.bbox2wktpolygon('%s,%s,%s,%s' % (llmin[0], llmin[1], urmax[0], urmax[1]))
def _build_xpath(node, path, namespaces, text): components = path.split("/") if util.nspath_eval(components[0], namespaces) == node.tag: components.pop(0) while components: # take in account positional indexes in the form /path/para[3] or /path/para[location()=3] if "[" in components[0]: component, trail = components[0].split("[", 1) target_index = int(trail.split("=")[-1].strip("]")) else: component = components[0] target_index = 0 components.pop(0) found_index = -1 for child in node.getchildren(): if child.tag == util.nspath_eval(component, namespaces): found_index += 1 if found_index == target_index: node = child break if found_index < target_index: new_node = None for i in range(target_index - found_index): new_node = etree.Element( util.nspath_eval(component, namespaces)) node.append(new_node) node = new_node node.text = text return node
def get_schemacomponents(self): ''' Return schema components as lxml.etree.Element list ''' node1 = etree.Element( util.nspath_eval('csw:SchemaComponent', self.context.namespaces), schemaLanguage='XMLSCHEMA', targetNamespace=self.namespace, parentSchema='gmd.xsd') schema_file = os.path.join(self.context.pycsw_home, 'plugins', 'profiles', 'apiso', 'schemas', 'ogc', 'iso', '19139', '20060504', 'gmd', 'identification.xsd') schema = etree.parse(schema_file, self.context.parser).getroot() node1.append(schema) node2 = etree.Element( util.nspath_eval('csw:SchemaComponent', self.context.namespaces), schemaLanguage='XMLSCHEMA', targetNamespace=self.namespace, parentSchema='gmd.xsd') schema_file = os.path.join(self.context.pycsw_home, 'plugins', 'profiles', 'apiso', 'schemas', 'ogc', 'iso', '19139', '20060504', 'srv', 'serviceMetadata.xsd') schema = etree.parse(schema_file, self.context.parser).getroot() node2.append(schema) return [node1, node2]
def write_extent(bbox, nsmap): ''' Generate BBOX extent ''' from shapely.wkt import loads if bbox is not None: try: bbox2 = util.wkt2geom(bbox) except: return None extent = etree.Element(util.nspath_eval('dif:Spatial_Coverage', nsmap)) etree.SubElement(extent, util.nspath_eval('dif:Southernmost_Latitude', nsmap)).text = str(bbox2[1]) etree.SubElement(extent, util.nspath_eval('dif:Northernmost_Latitude', nsmap)).text = str(bbox2[3]) etree.SubElement(extent, util.nspath_eval('dif:Westernmost_Longitude', nsmap)).text = str(bbox2[0]) etree.SubElement(extent, util.nspath_eval('dif:Easternmost_Longitude', nsmap)).text = str(bbox2[2]) return extent return None
def test_nspath_eval_invalid_element(): with pytest.raises(RuntimeError): util.nspath_eval(xpath="ns1:tag1/ns2:ns3:tag2", nsmap={ "ns1": "something", "ns2": "other", "ns3": "another", })
def _get_pt_freeurl(val, language): freeurl = etree.Element(util.nspath_eval('gm03:GM03_2_1Core.Core.PT_FreeURL', NAMESPACES)) urlgroup = etree.SubElement(freeurl, util.nspath_eval('gm03:URLGroup', NAMESPACES)) ptgroup = etree.SubElement(urlgroup, util.nspath_eval('gm03:GM03_2_1Core.Core.PT_URLGroup', NAMESPACES)) if language: etree.SubElement(ptgroup, util.nspath_eval('gm03:language', NAMESPACES)).text = language etree.SubElement(ptgroup, util.nspath_eval('gm03:plainURL', NAMESPACES)).text = val return freeurl
def _get_pt_freetext(val, language): freetext = etree.Element(util.nspath_eval('gm03:GM03_2_1Core.Core.PT_FreeText', NAMESPACES)) textgroup = etree.SubElement(freetext, util.nspath_eval('gm03:textGroup', NAMESPACES)) ptgroup = etree.SubElement(textgroup, util.nspath_eval('gm03:GM03_2_1Core.Core.PT_Group', NAMESPACES)) if language: etree.SubElement(ptgroup, util.nspath_eval('gm03:language', NAMESPACES)).text = language etree.SubElement(ptgroup, util.nspath_eval('gm03:plainText', NAMESPACES)).text = val return freetext
def test_nspath_eval_invalid_element(): with pytest.raises(RuntimeError): util.nspath_eval( xpath="ns1:tag1/ns2:ns3:tag2", nsmap={ "ns1": "something", "ns2": "other", "ns3": "another", } )
def _gen_soap_wrapper(self): ''' Generate SOAP wrapper ''' LOGGER.debug('Writing SOAP wrapper.') node = etree.Element(util.nspath_eval('soapenv:Envelope', self.context.namespaces), nsmap=self.context.namespaces) node.attrib[util.nspath_eval('xsi:schemaLocation', self.context.namespaces)] = '%s %s' % \ (self.context.namespaces['soapenv'], self.context.namespaces['soapenv']) node2 = etree.SubElement(node, util.nspath_eval('soapenv:Body', self.context.namespaces)) if self.exception: node3 = etree.SubElement(node2, util.nspath_eval('soapenv:Fault', self.context.namespaces)) node4 = etree.SubElement(node3, util.nspath_eval('soapenv:Code', self.context.namespaces)) etree.SubElement(node4, util.nspath_eval('soapenv:Value', self.context.namespaces)).text = 'soap:Server' node4 = etree.SubElement(node3, util.nspath_eval('soapenv:Reason', self.context.namespaces)) etree.SubElement(node4, util.nspath_eval('soapenv:Text', self.context.namespaces)).text = 'A server exception was encountered.' node4 = etree.SubElement(node3, util.nspath_eval('soapenv:Detail', self.context.namespaces)) node4.append(self.response) else: node2.append(self.response) self.response = node
def _gen_soap_wrapper(self): """ Generate SOAP wrapper """ LOGGER.debug("Writing SOAP wrapper.") node = etree.Element( util.nspath_eval("soapenv:Envelope", self.context.namespaces), nsmap=self.context.namespaces ) schema_location_ns = util.nspath_eval("xsi:schemaLocation", self.context.namespaces) node.attrib[schema_location_ns] = "%s %s" % ( self.context.namespaces["soapenv"], self.context.namespaces["soapenv"], ) node2 = etree.SubElement(node, util.nspath_eval("soapenv:Body", self.context.namespaces)) if self.exception: node3 = etree.SubElement(node2, util.nspath_eval("soapenv:Fault", self.context.namespaces)) node4 = etree.SubElement(node3, util.nspath_eval("soapenv:Code", self.context.namespaces)) etree.SubElement(node4, util.nspath_eval("soapenv:Value", self.context.namespaces)).text = "soap:Server" node4 = etree.SubElement(node3, util.nspath_eval("soapenv:Reason", self.context.namespaces)) etree.SubElement( node4, util.nspath_eval("soapenv:Text", self.context.namespaces) ).text = "A server exception was encountered." node4 = etree.SubElement(node3, util.nspath_eval("soapenv:Detail", self.context.namespaces)) node4.append(self.response) else: node2.append(self.response) self.response = node
def _write_date(dateval, datetypeval, nsmap): date1 = etree.Element(util.nspath_eval('gmd:date', nsmap)) date2 = etree.SubElement(date1, util.nspath_eval('gmd:CI_Date', nsmap)) date3 = etree.SubElement(date2, util.nspath_eval('gmd:date', nsmap)) if dateval.find('T') != -1: dateel = 'gco:DateTime' else: dateel = 'gco:Date' etree.SubElement(date3, util.nspath_eval(dateel, nsmap)).text = dateval datetype = etree.SubElement(date2, util.nspath_eval('gmd:dateType', nsmap)) datetype.append(_write_codelist_element('gmd:CI_DateTypeCode', datetypeval, nsmap)) return date1
def write_extent(bbox, nsmap): ''' Generate BBOX extent ''' if bbox is not None: try: bbox2 = util.wkt2geom(bbox) except: return None where = etree.Element(util.nspath_eval('georss:where', NAMESPACES)) envelope = etree.SubElement(where, util.nspath_eval('gml:Envelope', nsmap), srsName='http://www.opengis.net/def/crs/EPSG/0/4326') etree.SubElement(envelope, util.nspath_eval('gml:lowerCorner', nsmap)).text = '%s %s' % (bbox2[1], bbox2[0]) etree.SubElement(envelope, util.nspath_eval('gml:upperCorner', nsmap)).text = '%s %s' % (bbox2[3], bbox2[2]) return where return None
def write_extent(bbox, nsmap): ''' Generate BBOX extent ''' if bbox is not None: try: bbox2 = util.wkt2geom(bbox) except: return None bounding_box = etree.Element(util.nspath_eval('gm03:GM03_2_1Core.Core.EX_GeographicBoundingBox', NAMESPACES)) etree.SubElement(bounding_box, util.nspath_eval('gm03:northBoundLatitude', nsmap)).text = str(bbox2[3]) etree.SubElement(bounding_box, util.nspath_eval('gm03:southBoundLatitude', nsmap)).text = str(bbox2[1]) etree.SubElement(bounding_box, util.nspath_eval('gm03:eastBoundLongitude', nsmap)).text = str(bbox2[0]) etree.SubElement(bounding_box, util.nspath_eval('gm03:westBoundLongitude', nsmap)).text = str(bbox2[2]) return bounding_box return None
def _get_pt_freetext(val, language): freetext = etree.Element( util.nspath_eval('gm03:GM03_2_1Core.Core.PT_FreeText', NAMESPACES)) textgroup = etree.SubElement( freetext, util.nspath_eval('gm03:textGroup', NAMESPACES)) ptgroup = etree.SubElement( textgroup, util.nspath_eval('gm03:GM03_2_1Core.Core.PT_Group', NAMESPACES)) if language: etree.SubElement(ptgroup, util.nspath_eval('gm03:language', NAMESPACES)).text = language etree.SubElement(ptgroup, util.nspath_eval('gm03:plainText', NAMESPACES)).text = val return freetext
def _get_pt_freeurl(val, language): freeurl = etree.Element( util.nspath_eval('gm03:GM03_2_1Core.Core.PT_FreeURL', NAMESPACES)) urlgroup = etree.SubElement(freeurl, util.nspath_eval('gm03:URLGroup', NAMESPACES)) ptgroup = etree.SubElement( urlgroup, util.nspath_eval('gm03:GM03_2_1Core.Core.PT_URLGroup', NAMESPACES)) if language: etree.SubElement(ptgroup, util.nspath_eval('gm03:language', NAMESPACES)).text = language etree.SubElement(ptgroup, util.nspath_eval('gm03:plainURL', NAMESPACES)).text = val return freeurl
def _gen_soap_wrapper(self): """ Generate SOAP wrapper """ LOGGER.debug('Writing SOAP wrapper.') node = etree.Element( util.nspath_eval('soapenv:Envelope', self.context.namespaces), nsmap=self.context.namespaces ) schema_location_ns = util.nspath_eval('xsi:schemaLocation', self.context.namespaces) node.attrib[schema_location_ns] = '%s %s' % ( self.context.namespaces['soapenv'], self.context.namespaces['soapenv'] ) node2 = etree.SubElement( node, util.nspath_eval('soapenv:Body', self.context.namespaces)) if self.exception: node3 = etree.SubElement( node2, util.nspath_eval('soapenv:Fault', self.context.namespaces) ) node4 = etree.SubElement( node3, util.nspath_eval('soapenv:Code', self.context.namespaces) ) etree.SubElement( node4, util.nspath_eval('soapenv:Value', self.context.namespaces) ).text = 'soap:Server' node4 = etree.SubElement( node3, util.nspath_eval('soapenv:Reason', self.context.namespaces) ) etree.SubElement( node4, util.nspath_eval('soapenv:Text', self.context.namespaces) ).text = 'A server exception was encountered.' node4 = etree.SubElement( node3, util.nspath_eval('soapenv:Detail', self.context.namespaces) ) node4.append(self.response) else: node2.append(self.response) self.response = node
def test_nspath_eval(xpath_expression, expected): nsmap = { "ns1": "something", "ns2": "other", "ns3": "another", } result = util.nspath_eval(xpath_expression, nsmap) assert result == expected
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 exceptionreport2diagnostic(self, element): """transform a CSW exception into an SRU diagnostic""" node = etree.Element(util.nspath_eval('zs:searchRetrieveResponse', self.namespaces), nsmap=self.namespaces) etree.SubElement(node, util.nspath_eval( 'zs:version', self.namespaces)).text = self.sru_version diagnostics = etree.SubElement( node, util.nspath_eval('zs:diagnostics', self.namespaces)) diagnostic = etree.SubElement( diagnostics, util.nspath_eval('zs:diagnostic', self.namespaces)) etree.SubElement(diagnostic, util.nspath_eval('zd:diagnostic', self.namespaces)).text = \ 'info:srw/diagnostic/1/7' etree.SubElement(diagnostic, util.nspath_eval('zd:message', self.namespaces)).text = \ element.xpath('//ows:Exception/ows:ExceptionText|//ows20:Exception/ows20:ExceptionText', namespaces=self.context.namespaces)[0].text etree.SubElement(diagnostic, util.nspath_eval('zd:details', self.namespaces)).text = \ element.xpath('//ows:Exception|//ows20:Exception', namespaces=self.context.namespaces)[0].attrib.get('exceptionCode') return node
def _csw2_2_os(self): """CSW 2.0.2 Capabilities to OpenSearch Description""" if util.xmltag_split(self.exml.tag) == 'GetRecordsResponse': startindex = int(self.exml.xpath('//@nextRecord')[0]) - int( self.exml.xpath('//@numberOfRecordsReturned')[0]) if startindex < 1: startindex = 1 node = etree.Element(util.nspath_eval('atom:feed', self.context.namespaces), nsmap=self.namespaces) etree.SubElement(node, util.nspath_eval('atom:id', self.context.namespaces)).text = self.cfg.get('server', 'url') etree.SubElement(node, util.nspath_eval('atom:title', self.context.namespaces)).text = self.cfg.get('metadata:main', 'identification_title') #etree.SubElement(node, util.nspath_eval('atom:updated', # self.context.namespaces)).text = self.exml.xpath('//@timestamp')[0] etree.SubElement(node, util.nspath_eval('os:totalResults', self.context.namespaces)).text = self.exml.xpath( '//@numberOfRecordsMatched')[0] etree.SubElement(node, util.nspath_eval('os:startIndex', self.context.namespaces)).text = str(startindex) etree.SubElement(node, util.nspath_eval('os:itemsPerPage', self.context.namespaces)).text = self.exml.xpath( '//@numberOfRecordsReturned')[0] for rec in self.exml.xpath('//atom:entry', namespaces=self.context.namespaces): node.append(rec) elif util.xmltag_split(self.exml.tag) == 'Capabilities': node = etree.Element('OpenSearchDescription', nsmap=self.namespaces) etree.SubElement(node, 'ShortName').text = self.exml.xpath('//ows:Title', namespaces=self.context.namespaces)[0].text etree.SubElement(node, 'LongName').text = self.exml.xpath('//ows:Title', namespaces=self.context.namespaces)[0].text etree.SubElement(node, 'Description').text = self.exml.xpath('//ows:Abstract', namespaces=self.context.namespaces)[0].text etree.SubElement(node, 'Tags').text = ' '.join(x.text for x in self.exml.xpath('//ows:Keyword', namespaces=self.context.namespaces)) node1 = etree.SubElement(node, 'Url') node1.set('type', 'application/atom+xml') node1.set('method', 'get') node1.set('template', '%smode=opensearch&service=CSW&version=2.0.2&request=GetRecords&elementsetname=full&typenames=csw:Record&resulttype=results&q={searchTerms?}&bbox={geo:box?}&time={time:start?}/{time:end?}' % self.bind_url) node1 = etree.SubElement(node, 'Image') node1.set('type', 'image/vnd.microsoft.icon') node1.set('width', '16') node1.set('height', '16') node1.text = 'http://pycsw.org/img/favicon.ico' etree.SubElement(node, 'Developer').text = self.exml.xpath('//ows:IndividualName', namespaces=self.context.namespaces)[0].text etree.SubElement(node, 'Contact').text = self.exml.xpath('//ows:ElectronicMailAddress', namespaces=self.context.namespaces)[0].text etree.SubElement(node, 'Attribution').text = self.exml.xpath('//ows:ProviderName', namespaces=self.context.namespaces)[0].text elif util.xmltag_split(self.exml.tag) == 'ExceptionReport': node = self.exml else: # return Description document node = etree.Element(util.nspath_eval('os:Description', self.context.namespaces)) return node
def _write_codelist_element(codelist_element, codelist_value, nsmap): namespace, codelist = codelist_element.split(':') element = etree.Element(util.nspath_eval(codelist_element, nsmap), codeSpace=CODESPACE, codeList='%s#%s' % (CODELIST, codelist), codeListValue=codelist_value) element.text = codelist_value return element
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 _get_polygon(self): """Parse gml:Polygon""" tmp = self._exml.find('.//%s' % util.nspath_eval('gml:posList', self.nsmap)) if tmp is None: raise RuntimeError('Invalid gml:LineString geometry.\ Missing gml:posList') else: self.wkt = 'POLYGON((%s))' % _poslist2wkt( tmp.text, self.crs.axisorder, 'polygon')
def _get_linestring(self): """Parse gml:LineString""" tmp = self._exml.find( util.nspath_eval('gml:LineString/gml:posList', self.nsmap)) if tmp is None: raise RuntimeError('Invalid gml:LineString geometry.\ Missing gml:posList') else: self.wkt = 'LINESTRING(%s)' % _poslist2wkt( tmp.text, self.crs.axisorder, 'line')
def _get_linestring(self): """Parse gml:LineString""" tmp = self._exml.find(util.nspath_eval('gml:LineString/gml:posList', self.nsmap)) if tmp is None: raise RuntimeError('Invalid gml:LineString geometry.\ Missing gml:posList') else: self.wkt = 'LINESTRING(%s)' % _poslist2wkt(tmp.text, self.crs.axisorder)
def _get_polygon(self): """Parse gml:Polygon""" tmp = self._exml.find('.//%s' % util.nspath_eval('gml:posList', self.nsmap)) if tmp is None: raise RuntimeError('Invalid gml:LineString geometry.\ Missing gml:posList') else: self.wkt = 'POLYGON((%s))' % _poslist2wkt(tmp.text, self.crs.axisorder)
def _get_point(self): """Parse gml:Point""" tmp = self._exml.find(util.nspath_eval('gml:Point/gml:pos', self.nsmap)) if tmp is None: raise RuntimeError('Invalid gml:Point geometry. Missing gml:pos') else: xypoint = tmp.text.split() if self.crs.axisorder == 'yx': self.wkt = 'POINT(%s %s)' % (xypoint[1], xypoint[0]) else: self.wkt = 'POINT(%s %s)' % (xypoint[0], xypoint[1])
def get_schemacomponents(self): ''' Return schema components as lxml.etree.Element list ''' node = etree.Element( util.nspath_eval('csw:SchemaComponent', self.context.namespaces), schemaLanguage='XMLSCHEMA', targetNamespace=self.namespace) schema = etree.parse(os.path.join(self.context.pycsw_home, 'plugins', 'profiles', 'ebrim', 'schemas', 'ogc', 'csw', '2.0.2', 'profiles', 'ebrim', '1.0', 'csw-ebrim.xsd')).getroot() node.append(schema) return [node]
def get_schemacomponents(self): ''' Return schema components as lxml.etree.Element list ''' node = etree.Element(util.nspath_eval('csw:SchemaComponent', self.context.namespaces), schemaLanguage='XMLSCHEMA', targetNamespace=self.namespace) schema = etree.parse( os.path.join(self.context.pycsw_home, 'plugins', 'profiles', 'ebrim', 'schemas', 'ogc', 'csw', '2.0.2', 'profiles', 'ebrim', '1.0', 'csw-ebrim.xsd')).getroot() node.append(schema) return [node]
def _transform_element(self, parent, element, elname): """tests for existence of a given xpath, writes out text if exists""" xpath = self.metadata_formats[self.metadata_prefix][elname.split(':')[1]] if xpath.startswith('//'): value = element.xpath(xpath, namespaces=self.context.namespaces) if value: value = value[0].text else: # bare string literal value = xpath el = etree.SubElement(parent, util.nspath_eval(elname, self.context.namespaces)) if value: if elname == 'oai:setSpec': value = None for k, v in self.metadata_sets.items(): if v[1] == elname: value = k break el.text = value
def get_schemacomponents(self): ''' Return schema components as lxml.etree.Element list ''' schema_nodes = [] for schema_path in self.schemas_paths: node = etree.Element(util.nspath_eval('csw:SchemaComponent', self.context.namespaces), schemaLanguage='XMLSCHEMA', targetNamespace=self.namespace) schema_file = os.path.join(self.context.pycsw_home, *schema_path) schema = etree.parse(schema_file, self.context.parser).getroot() node.append(schema) schema_nodes.append(node) return schema_nodes
def exceptionreport2diagnostic(self, element): """transform a CSW exception into an SRU diagnostic""" node = etree.Element( util.nspath_eval('zs:searchRetrieveResponse', self.namespaces), nsmap=self.namespaces) etree.SubElement(node, util.nspath_eval('zs:version', self.namespaces)).text = self.sru_version diagnostics = etree.SubElement(node, util.nspath_eval('zs:diagnostics', self.namespaces)) diagnostic = etree.SubElement( diagnostics, util.nspath_eval('zs:diagnostic', self.namespaces)) etree.SubElement(diagnostic, util.nspath_eval('zd:diagnostic', self.namespaces)).text = \ 'info:srw/diagnostic/1/7' etree.SubElement(diagnostic, util.nspath_eval('zd:message', self.namespaces)).text = \ element.xpath('//ows:Exception/ows:ExceptionText|//ows20:Exception/ows20:ExceptionText', namespaces=self.context.namespaces)[0].text etree.SubElement(diagnostic, util.nspath_eval('zd:details', self.namespaces)).text = \ element.xpath('//ows:Exception|//ows20:Exception', namespaces=self.context.namespaces)[0].attrib.get('exceptionCode') return node
def _csw3_2_os(self): """CSW 3.0.0 Capabilities to OpenSearch Description""" response_name = etree.QName(self.exml).localname if response_name == 'GetRecordsResponse': startindex = int(self.exml.xpath('//@nextRecord')[0]) - int( self.exml.xpath('//@numberOfRecordsReturned')[0]) if startindex < 1: startindex = 1 node = etree.Element(util.nspath_eval('atom:feed', self.context.namespaces), nsmap=self.namespaces) etree.SubElement(node, util.nspath_eval('atom:id', self.context.namespaces)).text = self.cfg.get('server', 'url') etree.SubElement(node, util.nspath_eval('atom:title', self.context.namespaces)).text = self.cfg.get('metadata:main', 'identification_title') author = etree.SubElement(node, util.nspath_eval('atom:author', self.context.namespaces)) etree.SubElement(author, util.nspath_eval('atom:name', self.context.namespaces)).text = self.cfg.get('metadata:main', 'provider_name') etree.SubElement(node, util.nspath_eval('atom:link', self.context.namespaces), rel='search', type='application/opensearchdescription+xml', href='%smode=opensearch&service=CSW&version=3.0.0&request=GetCapabilities' % self.bind_url) etree.SubElement(node, util.nspath_eval('atom:updated', self.context.namespaces)).text = self.exml.xpath('//@timestamp')[0] etree.SubElement(node, util.nspath_eval('os:Query', self.context.namespaces), role='request') matched = sum(int(x) for x in self.exml.xpath('//@numberOfRecordsMatched')) etree.SubElement(node, util.nspath_eval('os:totalResults', self.context.namespaces)).text = str(matched) etree.SubElement(node, util.nspath_eval('os:startIndex', self.context.namespaces)).text = str(startindex) returned = sum(int(x) for x in self.exml.xpath('//@numberOfRecordsReturned')) etree.SubElement(node, util.nspath_eval('os:itemsPerPage', self.context.namespaces)).text = str(returned) for rec in self.exml.xpath('//atom:entry', namespaces=self.context.namespaces): LOGGER.debug('ADDING ATOM ENTRY') node.append(rec) for rec in self.exml.xpath('//csw30:Record|//csw30:BriefRecord|//csw30:SummaryRecord', namespaces=self.context.namespaces): node.append(self.cswrecord2atom(rec)) elif response_name == 'Capabilities': node = etree.Element(util.nspath_eval('os:OpenSearchDescription', self.namespaces), nsmap=self.namespaces) etree.SubElement(node, util.nspath_eval('os:ShortName', self.namespaces)).text = self.exml.xpath('//ows20:Title', namespaces=self.context.namespaces)[0].text[:16] etree.SubElement(node, util.nspath_eval('os:LongName', self.namespaces)).text = self.exml.xpath('//ows20:Title', namespaces=self.context.namespaces)[0].text etree.SubElement(node, util.nspath_eval('os:Description', self.namespaces)).text = self.exml.xpath('//ows20:Abstract', namespaces=self.context.namespaces)[0].text etree.SubElement(node, util.nspath_eval('os:Tags', self.namespaces)).text = ' '.join(x.text for x in self.exml.xpath('//ows20:Keyword', namespaces=self.context.namespaces)) # Requirement-022 node1 = etree.SubElement(node, util.nspath_eval('os:Url', self.namespaces)) node1.set('type', 'application/xml') kvps = { 'service': 'CSW', 'version': '3.0.0', 'request': 'GetRecords', 'elementsetname': 'full', 'typenames': 'csw:Record', 'outputformat': 'application/xml', 'outputschema': 'http://www.opengis.net/cat/csw/3.0', 'recordids': '{geo:uid?}', 'q': '{searchTerms?}', 'bbox': '{geo:box?}', 'time': '{time:start?}/{time:end?}', 'start': '{time:start?}', 'stop': '{time:end?}', 'startposition': '{startIndex?}', 'maxrecords': '{count?}', 'eo:cloudCover': '{eo:cloudCover?}', 'eo:instrument': '{eo:instrument?}', 'eo:orbitDirection': '{eo:orbitDirection?}', 'eo:orbitNumber': '{eo:orbitNumber?}', 'eo:parentIdentifier': '{eo:parentIdentifier?}', 'eo:platform': '{eo:platform?}', 'eo:processingLevel': '{eo:processingLevel?}', 'eo:productType': '{eo:productType?}', 'eo:sensorType': '{eo:sensorType?}', 'eo:snowCover': '{eo:snowCover?}', 'eo:spectralRange': '{eo:spectralRange?}' } node1.set('template', '%s%s' % (self.bind_url, '&'.join('{}={}'.format(*i) for i in kvps.items()))) # Requirement-023 node1 = etree.SubElement(node, util.nspath_eval('os:Url', self.namespaces)) node1.set('type', 'application/atom+xml') kvps['outputformat'] = r'application%2Fatom%2Bxml' kvps['mode'] = 'opensearch' node1.set('template', '%s%s' % (self.bind_url, '&'.join('{}={}'.format(*i) for i in kvps.items()))) node1 = etree.SubElement(node, util.nspath_eval('os:Image', self.namespaces)) node1.set('type', 'image/vnd.microsoft.icon') node1.set('width', '16') node1.set('height', '16') node1.text = 'https://pycsw.org/img/favicon.ico' os_query = etree.SubElement(node, util.nspath_eval('os:Query', self.namespaces), role='example', searchTerms='cat') etree.SubElement(node, util.nspath_eval('os:Developer', self.namespaces)).text = self.exml.xpath('//ows20:IndividualName', namespaces=self.context.namespaces)[0].text etree.SubElement(node, util.nspath_eval('os:Contact', self.namespaces)).text = self.exml.xpath('//ows20:ElectronicMailAddress', namespaces=self.context.namespaces)[0].text etree.SubElement(node, util.nspath_eval('os:Attribution', self.namespaces)).text = self.exml.xpath('//ows20:ProviderName', namespaces=self.context.namespaces)[0].text elif response_name == 'ExceptionReport': node = self.exml else: # GetRecordById output node = etree.Element(util.nspath_eval('atom:feed', self.context.namespaces), nsmap=self.namespaces) etree.SubElement(node, util.nspath_eval('atom:id', self.context.namespaces)).text = self.cfg.get('server', 'url') etree.SubElement(node, util.nspath_eval('atom:title', self.context.namespaces)).text = self.cfg.get('metadata:main', 'identification_title') #etree.SubElement(node, util.nspath_eval('atom:updated', # self.context.namespaces)).text = self.exml.xpath('//@timestamp')[0] etree.SubElement(node, util.nspath_eval('os:totalResults', self.context.namespaces)).text = '1' etree.SubElement(node, util.nspath_eval('os:startIndex', self.context.namespaces)).text = '1' etree.SubElement(node, util.nspath_eval('os:itemsPerPage', self.context.namespaces)).text = '1' for rec in self.exml.xpath('//atom:entry', namespaces=self.context.namespaces): #node.append(rec) node = rec return node
def _csw2_2_os(self): """CSW 2.0.2 Capabilities to OpenSearch Description""" operation_name = etree.QName(self.exml).localname if operation_name == 'GetRecordsResponse': startindex = int(self.exml.xpath('//@nextRecord')[0]) - int( self.exml.xpath('//@numberOfRecordsReturned')[0]) if startindex < 1: startindex = 1 node = etree.Element(util.nspath_eval('atom:feed', self.context.namespaces), nsmap=self.namespaces) etree.SubElement(node, util.nspath_eval('atom:id', self.context.namespaces)).text = self.cfg.get('server', 'url') etree.SubElement(node, util.nspath_eval('atom:title', self.context.namespaces)).text = self.cfg.get('metadata:main', 'identification_title') #etree.SubElement(node, util.nspath_eval('atom:updated', # self.context.namespaces)).text = self.exml.xpath('//@timestamp')[0] etree.SubElement(node, util.nspath_eval('os:totalResults', self.context.namespaces)).text = self.exml.xpath( '//@numberOfRecordsMatched')[0] etree.SubElement(node, util.nspath_eval('os:startIndex', self.context.namespaces)).text = str(startindex) etree.SubElement(node, util.nspath_eval('os:itemsPerPage', self.context.namespaces)).text = self.exml.xpath( '//@numberOfRecordsReturned')[0] for rec in self.exml.xpath('//atom:entry', namespaces=self.context.namespaces): node.append(rec) for rec in self.exml.xpath('//csw:Record|//csw:BriefRecord|//csw:SummaryRecord', namespaces=self.context.namespaces): node.append(self.cswrecord2atom(rec)) elif operation_name == 'Capabilities': node = etree.Element(util.nspath_eval('os:OpenSearchDescription', self.namespaces), nsmap=self.namespaces) etree.SubElement(node, util.nspath_eval('os:ShortName', self.namespaces)).text = self.exml.xpath('//ows:Title', namespaces=self.context.namespaces)[0].text etree.SubElement(node, util.nspath_eval('os:LongName', self.namespaces)).text = self.exml.xpath('//ows:Title', namespaces=self.context.namespaces)[0].text etree.SubElement(node, util.nspath_eval('os:Description', self.namespaces)).text = self.exml.xpath('//ows:Abstract', namespaces=self.context.namespaces)[0].text etree.SubElement(node, util.nspath_eval('os:Tags', self.namespaces)).text = ' '.join(x.text for x in self.exml.xpath('//ows:Keyword', namespaces=self.context.namespaces)) node1 = etree.SubElement(node, util.nspath_eval('os:Url', self.namespaces)) node1.set('type', 'application/atom+xml') node1.set('method', 'get') kvps = { 'mode': 'opensearch', 'service': 'CSW', 'version': '2.0.2', 'request': 'GetRecords', 'elementsetname': 'full', 'typenames': 'csw:Record', 'resulttype': 'results', 'q': '{searchTerms?}', 'bbox': '{geo:box?}', 'time': '{time:start?}/{time:end?}', 'start': '{time:start?}', 'stop': '{time:end?}', 'startposition': '{startIndex?}', 'maxrecords': '{count?}', 'eo:cloudCover': '{eo:cloudCover?}', 'eo:instrument': '{eo:instrument?}', 'eo:orbitDirection': '{eo:orbitDirection?}', 'eo:orbitNumber': '{eo:orbitNumber?}', 'eo:parentIdentifier': '{eo:parentIdentifier?}', 'eo:platform': '{eo:platform?}', 'eo:processingLevel': '{eo:processingLevel?}', 'eo:productType': '{eo:productType?}', 'eo:sensorType': '{eo:sensorType?}', 'eo:snowCover': '{eo:snowCover?}', 'eo:spectralRange': '{eo:spectralRange?}' } node1.set('template', '%s%s' % (self.bind_url, '&'.join('{}={}'.format(*i) for i in kvps.items()))) #node1.set('template', '%smode=opensearch&service=CSW&version=2.0.2&request=GetRecords&elementsetname=full&typenames=csw:Record&resulttype=results&q={searchTerms?}&bbox={geo:box?}&time={time:start?}/{time:end?}&start={time:start?}&stop={time:end?}&startposition={startIndex?}&maxrecords={count?}' % self.bind_url) node1 = etree.SubElement(node, util.nspath_eval('os:Image', self.namespaces)) node1.set('type', 'image/vnd.microsoft.icon') node1.set('width', '16') node1.set('height', '16') node1.text = 'https://pycsw.org/img/favicon.ico' etree.SubElement(node, util.nspath_eval('os:Developer', self.namespaces)).text = self.exml.xpath('//ows:IndividualName', namespaces=self.context.namespaces)[0].text etree.SubElement(node, util.nspath_eval('os:Context', self.namespaces)).text = self.exml.xpath('//ows:ElectronicMailAddress', namespaces=self.context.namespaces)[0].text etree.SubElement(node, util.nspath_eval('os:Attribution', self.namespaces)).text = self.exml.xpath('//ows:ProviderName', namespaces=self.context.namespaces)[0].text elif operation_name == 'ExceptionReport': node = self.exml else: # return Description document node = etree.Element(util.nspath_eval('os:Description', self.context.namespaces)) return node
def write_record(result, esn, context, url=None): ''' Return csw:SearchResults child as lxml.etree.Element ''' typename = util.getqattr( result, context.md_core_model['mappings']['pycsw:Typename']) if esn == 'full' and typename == 'atom:entry': # dump record as is and exit return etree.fromstring( util.getqattr(result, context.md_core_model['mappings']['pycsw:XML']), context.parser) node = etree.Element(util.nspath_eval('atom:entry', NAMESPACES), nsmap=NAMESPACES) node.attrib[util.nspath_eval('xsi:schemaLocation', context.namespaces)] = \ '%s http://www.kbcafe.com/rss/atom.xsd.xml' % NAMESPACES['atom'] # author val = util.getqattr(result, context.md_core_model['mappings']['pycsw:Creator']) if val: author = etree.SubElement(node, util.nspath_eval('atom:author', NAMESPACES)) etree.SubElement(author, util.nspath_eval('atom:name', NAMESPACES)).text = val # category val = util.getqattr(result, context.md_core_model['mappings']['pycsw:Keywords']) if val: for kw in val.split(','): etree.SubElement(node, util.nspath_eval('atom:category', NAMESPACES), term=kw) for qval in ['pycsw:Contributor', 'pycsw:Identifier']: val = util.getqattr(result, context.md_core_model['mappings'][qval]) if val: etree.SubElement( node, util.nspath_eval(XPATH_MAPPINGS[qval], NAMESPACES)).text = val if qval == 'pycsw:Identifier': etree.SubElement( node, util.nspath_eval('dc:identifier', context.namespaces)).text = val rlinks = util.getqattr(result, context.md_core_model['mappings']['pycsw:Links']) if rlinks: for link in util.jsonify_links(rlinks): url2 = etree.SubElement(node, util.nspath_eval('atom:link', NAMESPACES), href=link['url']) if link['description']: url2.attrib['title'] = link['description'] if link['protocol']: if link['protocol'] == 'enclosure': url2.attrib['rel'] = link['protocol'] url2.attrib['type'] = 'application/octet-stream' else: url2.attrib['type'] = link['protocol'] etree.SubElement( node, util.nspath_eval('atom:link', NAMESPACES), href='%s?service=CSW&version=2.0.2&request=GetRepositoryItem&id=%s' % (url, util.getqattr(result, context.md_core_model['mappings']['pycsw:Identifier']))) # atom:title el = etree.SubElement( node, util.nspath_eval(XPATH_MAPPINGS['pycsw:Title'], NAMESPACES)) val = util.getqattr(result, context.md_core_model['mappings']['pycsw:Title']) if val: el.text = val # atom:updated el = etree.SubElement( node, util.nspath_eval(XPATH_MAPPINGS['pycsw:Modified'], NAMESPACES)) val = util.getqattr(result, context.md_core_model['mappings']['pycsw:Modified']) if val: el.text = val else: val = util.getqattr( result, context.md_core_model['mappings']['pycsw:InsertDate']) el.text = val for qval in [ 'pycsw:PublicationDate', 'pycsw:AccessConstraints', 'pycsw:Source', 'pycsw:Abstract' ]: val = util.getqattr(result, context.md_core_model['mappings'][qval]) if val: etree.SubElement( node, util.nspath_eval(XPATH_MAPPINGS[qval], NAMESPACES)).text = val # bbox extent val = util.getqattr(result, context.md_core_model['mappings']['pycsw:BoundingBox']) bboxel = write_extent(val, context.namespaces) if bboxel is not None: node.append(bboxel) return node
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 evaluate_literal(context, pname, pvalue): """ Transforms OpenSearch EO mathematical notation to OGC FES syntax :param pname: parameter name :param pvalue: parameter value :returns: lxml Element of predicate """ LOGGER.debug('property name: {}'.format(pname)) LOGGER.debug('property value: {}'.format(pvalue)) if pvalue.startswith('{') and pvalue.endswith('}'): # {n1,n2,…} equals to field=n1 OR field=n2 OR … values = pvalue.lstrip('{').rstrip('}').split(',') el = etree.Element(util.nspath_eval('ogc:Or', context.namespaces)) for value in values: el2 = etree.SubElement(el, util.nspath_eval('ogc:PropertyIsEqualTo', context.namespaces)) etree.SubElement(el2, util.nspath_eval('ogc:PropertyName', context.namespaces)).text = pname etree.SubElement(el2, util.nspath_eval('ogc:Literal', context.namespaces)).text = value elif pvalue.startswith('[') and pvalue.endswith(']'): # [n1,n2] equal to n1 <= field <= n2 values = pvalue.lstrip('[').rstrip(']').split(',') el = etree.Element(util.nspath_eval('ogc:And', context.namespaces)) el2 = etree.SubElement(el, util.nspath_eval('ogc:PropertyIsLessThanOrEqualTo', context.namespaces)) etree.SubElement(el2, util.nspath_eval('ogc:PropertyName', context.namespaces)).text = pname etree.SubElement(el2, util.nspath_eval('ogc:Literal', context.namespaces)).text = values[0] el3 = etree.SubElement(el, util.nspath_eval('ogc:PropertyIsGreaterThanOrEqualTo', context.namespaces)) etree.SubElement(el3, util.nspath_eval('ogc:PropertyName', context.namespaces)).text = pname etree.SubElement(el3, util.nspath_eval('ogc:Literal', context.namespaces)).text = values[1] elif pvalue.startswith(']') and pvalue.endswith('['): # ]n1,n2[ equals to n1 < field < n2 values = pvalue.lstrip(']').rstrip('[').split(',') el = etree.Element(util.nspath_eval('ogc:And', context.namespaces)) el2 = etree.SubElement(el, util.nspath_eval('ogc:PropertyIsLessThan', context.namespaces)) etree.SubElement(el2, util.nspath_eval('ogc:PropertyName', context.namespaces)).text = pname etree.SubElement(el2, util.nspath_eval('ogc:Literal', context.namespaces)).text = values[0] el3 = etree.SubElement(el, util.nspath_eval('ogc:PropertyIsGreaterThan', context.namespaces)) etree.SubElement(el3, util.nspath_eval('ogc:PropertyName', context.namespaces)).text = pname etree.SubElement(el3, util.nspath_eval('ogc:Literal', context.namespaces)).text = values[1] elif pvalue.startswith('['): # [n1 equals to n1<= field el = etree.Element(util.nspath_eval('ogc:PropertyIsGreaterThanOrEqualTo', context.namespaces)) etree.SubElement(el, util.nspath_eval('ogc:PropertyName', context.namespaces)).text = pname etree.SubElement(el, util.nspath_eval('ogc:Literal', context.namespaces)).text = pvalue.lstrip('[') elif pvalue.endswith(']'): # n2] equals to field <= n2 el = etree.Element(util.nspath_eval('ogc:PropertyIsLessThanOrEqualTo', context.namespaces)) etree.SubElement(el, util.nspath_eval('ogc:PropertyName', context.namespaces)).text = pname etree.SubElement(el, util.nspath_eval('ogc:Literal', context.namespaces)).text = pvalue.rstrip(']') elif pvalue.startswith('[') and pvalue.endswith('['): # [n1,n2[ equals to n1 <= field < n2 values = pvalue.lstrip('[').rstrip('[').split(',') el = etree.Element(util.nspath_eval('ogc:And', context.namespaces)) el2 = etree.SubElement(el, util.nspath_eval('ogc:PropertyIsLessThanOrEqualTo', context.namespaces)) etree.SubElement(el2, util.nspath_eval('ogc:PropertyName', context.namespaces)).text = pname etree.SubElement(el2, util.nspath_eval('ogc:Literal', context.namespaces)).text = values[0] el3 = etree.SubElement(el, util.nspath_eval('ogc:PropertyIsGreaterThan', context.namespaces)) etree.SubElement(el3, util.nspath_eval('ogc:PropertyName', context.namespaces)).text = pname etree.SubElement(el3, util.nspath_eval('ogc:Literal', context.namespaces)).text = values[1] elif pvalue.startswith(']') and pvalue.endswith(']'): # ]n1,n2] equal to n1 < field <= n2 values = pvalue.lstrip(']').rstrip(']').split(',') el = etree.Element(util.nspath_eval('ogc:And', context.namespaces)) el2 = etree.SubElement(el, util.nspath_eval('ogc:PropertyIsLessThan', context.namespaces)) etree.SubElement(el2, util.nspath_eval('ogc:PropertyName', context.namespaces)).text = pname etree.SubElement(el2, util.nspath_eval('ogc:Literal', context.namespaces)).text = values[0] el3 = etree.SubElement(el, util.nspath_eval('ogc:PropertyIsGreaterThanOrEqualTo', context.namespaces)) etree.SubElement(el3, util.nspath_eval('ogc:PropertyName', context.namespaces)).text = pname etree.SubElement(el3, util.nspath_eval('ogc:Literal', context.namespaces)).text = values[1] elif pvalue.startswith(']'): # ]n1 equals to n1 < field el = etree.Element(util.nspath_eval('ogc:PropertyIsGreaterThan', context.namespaces)) etree.SubElement(el, util.nspath_eval('ogc:PropertyName', context.namespaces)).text = pname etree.SubElement(el, util.nspath_eval('ogc:Literal', context.namespaces)).text = pvalue.lstrip(']') elif pvalue.endswith('['): # n2[ equals to field < n2 el = etree.Element(util.nspath_eval('ogc:PropertyIsLessThan', context.namespaces)) etree.SubElement(el, util.nspath_eval('ogc:PropertyName', context.namespaces)).text = pname etree.SubElement(el, util.nspath_eval('ogc:Literal', context.namespaces)).text = pvalue.rstrip('[') else: # n1 equal to field = n1 el = etree.Element(util.nspath_eval('ogc:PropertyIsEqualTo', context.namespaces)) etree.SubElement(el, util.nspath_eval('ogc:PropertyName', context.namespaces)).text = pname etree.SubElement(el, util.nspath_eval('ogc:Literal', context.namespaces)).text = pvalue return el
def parse(element, queryables, dbtype, nsmap, orm='sqlalchemy', language='english', fts=False): """OGC Filter object support""" boq = None is_pg = dbtype.startswith('postgresql') tmp = element.xpath('ogc:And|ogc:Or|ogc:Not', namespaces=nsmap) if len(tmp) > 0: # this is binary logic query boq = ' %s ' % util.xmltag_split(tmp[0].tag).lower() LOGGER.debug('Binary logic detected; operator=%s' % boq) tmp = tmp[0] else: tmp = element pvalue_serial = [0] # in list as python 2 has no nonlocal variable def assign_param(): if orm == 'django': return '%s' param = ':pvalue%d' % pvalue_serial[0] pvalue_serial[0] += 1 return param def _get_comparison_expression(elem): """return the SQL expression based on Filter query""" fname = None matchcase = elem.attrib.get('matchCase') wildcard = elem.attrib.get('wildCard') singlechar = elem.attrib.get('singleChar') expression = None if wildcard is None: wildcard = '%' if singlechar is None: singlechar = '_' if (elem.xpath('child::*')[0].tag == util.nspath_eval('ogc:Function', nsmap)): LOGGER.debug('ogc:Function detected') if (elem.xpath('child::*')[0].attrib['name'] not in MODEL['Functions']): raise RuntimeError('Invalid ogc:Function: %s' % (elem.xpath('child::*')[0].attrib['name'])) fname = elem.xpath('child::*')[0].attrib['name'] try: LOGGER.debug('Testing existence of ogc:PropertyName') pname = queryables[elem.find(util.nspath_eval('ogc:Function/ogc:PropertyName', nsmap)).text]['dbcol'] except Exception as err: raise RuntimeError('Invalid PropertyName: %s. %s' % (elem.find(util.nspath_eval('ogc:Function/ogc:PropertyName', nsmap)).text, str(err))) else: try: LOGGER.debug('Testing existence of ogc:PropertyName') pname = queryables[elem.find( util.nspath_eval('ogc:PropertyName', nsmap)).text]['dbcol'] except Exception as err: raise RuntimeError('Invalid PropertyName: %s. %s' % (elem.find(util.nspath_eval('ogc:PropertyName', nsmap)).text, str(err))) if (elem.tag != util.nspath_eval('ogc:PropertyIsBetween', nsmap)): if elem.tag in [util.nspath_eval('ogc:%s' % n, nsmap) for n in MODEL['SpatialOperators']['values']]: boolean_true = '\'true\'' boolean_false = '\'false\'' if dbtype == 'mysql': boolean_true = 'true' boolean_false = 'false' return "%s = %s" % (_get_spatial_operator(queryables['pycsw:BoundingBox'], elem, dbtype, nsmap), boolean_true) else: pval = elem.find(util.nspath_eval('ogc:Literal', nsmap)).text com_op = _get_comparison_operator(elem) LOGGER.debug('Comparison operator: %s' % com_op) # if this is a case insensitive search # then set the DB-specific LIKE comparison operator LOGGER.debug('Setting csw:AnyText property') anytext = queryables['csw:AnyText']['dbcol'] if ((matchcase is not None and matchcase == 'false') or pname == anytext): com_op = 'ilike' if is_pg else 'like' if (elem.tag == util.nspath_eval('ogc:PropertyIsBetween', nsmap)): com_op = 'between' lower_boundary = elem.find( util.nspath_eval('ogc:LowerBoundary/ogc:Literal', nsmap)).text upper_boundary = elem.find( util.nspath_eval('ogc:UpperBoundary/ogc:Literal', nsmap)).text expression = "%s %s %s and %s" % \ (pname, com_op, assign_param(), assign_param()) values.append(lower_boundary) values.append(upper_boundary) else: if pname == anytext and is_pg and fts: LOGGER.debug('PostgreSQL FTS specific search') # do nothing, let FTS do conversion (#212) pvalue = pval else: LOGGER.debug('PostgreSQL non-FTS specific search') pvalue = pval.replace(wildcard, '%').replace(singlechar, '_') if pname == anytext: # pad anytext with wildcards LOGGER.debug('PostgreSQL non-FTS specific anytext search') LOGGER.debug('old value: %s', pval) pvalue = '%%%s%%' % pvalue.rstrip('%').lstrip('%') LOGGER.debug('new value: %s', pvalue) values.append(pvalue) if boq == ' not ': if fname is not None: expression = "%s is null or not %s(%s) %s %s" % \ (pname, fname, pname, com_op, assign_param()) elif pname == anytext and is_pg and fts: LOGGER.debug('PostgreSQL FTS specific search') expression = ("%s is null or not plainto_tsquery('%s', %s) @@ anytext_tsvector" % (anytext, language, assign_param())) else: LOGGER.debug('PostgreSQL non-FTS specific search') expression = "%s is null or not %s %s %s" % \ (pname, pname, com_op, assign_param()) else: if fname is not None: expression = "%s(%s) %s %s" % \ (fname, pname, com_op, assign_param()) elif pname == anytext and is_pg and fts: LOGGER.debug('PostgreSQL FTS specific search') expression = ("plainto_tsquery('%s', %s) @@ anytext_tsvector" % (language, assign_param())) else: LOGGER.debug('PostgreSQL non-FTS specific search') expression = "%s %s %s" % (pname, com_op, assign_param()) return expression queries = [] queries_nested = [] values = [] LOGGER.debug('Scanning children elements') for child in tmp.xpath('child::*'): com_op = '' boolean_true = '\'true\'' boolean_false = '\'false\'' if dbtype == 'mysql': boolean_true = 'true' boolean_false = 'false' if child.tag == util.nspath_eval('ogc:Not', nsmap): LOGGER.debug('ogc:Not query detected') child_not = child.xpath('child::*')[0] if child_not.tag in \ [util.nspath_eval('ogc:%s' % n, nsmap) for n in MODEL['SpatialOperators']['values']]: LOGGER.debug('ogc:Not / spatial operator detected: %s' % child.tag) queries.append("%s = %s" % (_get_spatial_operator( queryables['pycsw:BoundingBox'], child.xpath('child::*')[0], dbtype, nsmap), boolean_false)) else: LOGGER.debug('ogc:Not / comparison operator detected: %s' % child.tag) queries.append('not %s' % _get_comparison_expression(child_not)) elif child.tag in \ [util.nspath_eval('ogc:%s' % n, nsmap) for n in MODEL['SpatialOperators']['values']]: LOGGER.debug('spatial operator detected: %s' % child.tag) if boq is not None and boq == ' not ': # for ogc:Not spatial queries in PostGIS we must explictly # test that pycsw:BoundingBox is null as well # TODO: Do we need the same for 'postgresql+postgis+native'??? if dbtype == 'postgresql+postgis+wkt': LOGGER.debug('Setting bbox is null test in PostgreSQL') queries.append("%s = %s or %s is null" % (_get_spatial_operator( queryables['pycsw:BoundingBox'], child, dbtype, nsmap), boolean_false, queryables['pycsw:BoundingBox'])) else: queries.append("%s = %s" % (_get_spatial_operator( queryables['pycsw:BoundingBox'], child, dbtype, nsmap), boolean_false)) else: queries.append("%s = %s" % (_get_spatial_operator( queryables['pycsw:BoundingBox'], child, dbtype, nsmap), boolean_true)) elif child.tag == util.nspath_eval('ogc:FeatureId', nsmap): LOGGER.debug('ogc:FeatureId filter detected') queries.append("%s = %s" % (queryables['pycsw:Identifier'], assign_param())) values.append(child.attrib.get('fid')) else: # comparison operator LOGGER.debug('Comparison operator processing') tagname = ' %s ' % util.xmltag_split(child.tag).lower() if tagname in [' or ', ' and ']: # this is a nested binary logic query LOGGER.debug('Nested binary logic detected; operator=%s' % tagname) for child2 in child.xpath('child::*'): queries_nested.append(_get_comparison_expression(child2)) queries.append('(%s)' % tagname.join(queries_nested)) else: queries.append(_get_comparison_expression(child)) where = boq.join(queries) if (boq is not None and boq != ' not ') \ else queries[0] return where, values
def _get_spatial_operator(geomattr, element, dbtype, nsmap, postgis_geometry_column='wkb_geometry'): """return the spatial predicate function""" property_name = element.find(util.nspath_eval('ogc:PropertyName', nsmap)) distance = element.find(util.nspath_eval('ogc:Distance', nsmap)) distance = 'false' if distance is None else distance.text LOGGER.debug('Scanning for spatial property name') if property_name is None: raise RuntimeError('Missing ogc:PropertyName in spatial filter') if (property_name.text.find('BoundingBox') == -1 and property_name.text.find('Envelope') == -1): raise RuntimeError('Invalid ogc:PropertyName in spatial filter: %s' % property_name.text) geometry = gml3.Geometry(element, nsmap) #make decision to apply spatial ranking to results set_spatial_ranking(geometry) spatial_predicate = util.xmltag_split(element.tag).lower() LOGGER.debug('Spatial predicate: %s' % spatial_predicate) if dbtype == 'mysql': # adjust spatial query for MySQL LOGGER.debug('Adjusting spatial query for MySQL') if spatial_predicate == 'bbox': spatial_predicate = 'intersects' if spatial_predicate == 'beyond': spatial_query = "ifnull(distance(geomfromtext(%s), \ geomfromtext('%s')) > convert(%s, signed),false)" % \ (geomattr, geometry.wkt, distance) elif spatial_predicate == 'dwithin': spatial_query = "ifnull(distance(geomfromtext(%s), \ geomfromtext('%s')) <= convert(%s, signed),false)" % \ (geomattr, geometry.wkt, distance) else: spatial_query = "ifnull(%s(geomfromtext(%s), \ geomfromtext('%s')),false)" % \ (spatial_predicate, geomattr, geometry.wkt) elif dbtype == 'postgresql+postgis+wkt': # adjust spatial query for PostGIS with WKT geometry column LOGGER.debug('Adjusting spatial query for PostgreSQL+PostGIS+WKT') if spatial_predicate == 'bbox': spatial_predicate = 'intersects' if spatial_predicate == 'beyond': spatial_query = "not st_dwithin(st_geomfromtext(%s), \ st_geomfromtext('%s'), %f)" % \ (geomattr, geometry.wkt, float(distance)) elif spatial_predicate == 'dwithin': spatial_query = "st_dwithin(st_geomfromtext(%s), \ st_geomfromtext('%s'), %f)" % \ (geomattr, geometry.wkt, float(distance)) else: spatial_query = "st_%s(st_geomfromtext(%s), \ st_geomfromtext('%s'))" % \ (spatial_predicate, geomattr, geometry.wkt) elif dbtype == 'postgresql+postgis+native': # adjust spatial query for PostGIS with native geometry LOGGER.debug('Adjusting spatial query for PostgreSQL+PostGIS+native') if spatial_predicate == 'bbox': spatial_predicate = 'intersects' if spatial_predicate == 'beyond': spatial_query = "not st_dwithin(%s, \ st_geomfromtext('%s',4326), %f)" % \ (postgis_geometry_column, geometry.wkt, float(distance)) elif spatial_predicate == 'dwithin': spatial_query = "st_dwithin(%s, \ st_geomfromtext('%s',4326), %f)" % \ (postgis_geometry_column, geometry.wkt, float(distance)) else: spatial_query = "st_%s(%s, \ st_geomfromtext('%s',4326))" % \ (spatial_predicate, postgis_geometry_column, geometry.wkt) else: LOGGER.debug('Adjusting spatial query') spatial_query = "query_spatial(%s,'%s','%s','%s')" % \ (geomattr, geometry.wkt, spatial_predicate, distance) return spatial_query
def response_csw2sru(self, element, environ): """transform a CSW response into an SRU response""" response_name = etree.QName(element).localname if response_name == 'Capabilities': # explain node = etree.Element(util.nspath_eval('sru:explainResponse', self.namespaces), nsmap=self.namespaces) etree.SubElement(node, util.nspath_eval('sru:version', self.namespaces)).text = self.sru_version record = etree.SubElement(node, util.nspath_eval('sru:record', self.namespaces)) etree.SubElement(record, util.nspath_eval('sru:recordPacking', self.namespaces)).text = 'XML' etree.SubElement(record, util.nspath_eval('sru:recordSchema', self.namespaces)).text = 'http://explain.z3950.org/dtd/2.1/' recorddata = etree.SubElement(record, util.nspath_eval('sru:recordData', self.namespaces)) explain = etree.SubElement(recorddata, util.nspath_eval('zr:explain', self.namespaces)) serverinfo = etree.SubElement(explain, util.nspath_eval('zr:serverInfo', self.namespaces), protocol='SRU', version=self.sru_version, transport='http', method='GET POST SOAP') etree.SubElement(serverinfo, util.nspath_eval('zr:host', self.namespaces)).text = environ.get('HTTP_HOST', environ["SERVER_NAME"]) # WSGI allows for either of these etree.SubElement(serverinfo, util.nspath_eval('zr:port', self.namespaces)).text = environ['SERVER_PORT'] etree.SubElement(serverinfo, util.nspath_eval('zr:database', self.namespaces)).text = 'pycsw' databaseinfo = etree.SubElement(explain, util.nspath_eval('zr:databaseInfo', self.namespaces)) etree.SubElement(databaseinfo, util.nspath_eval('zr:title', self.namespaces), lang='en', primary='true').text = element.xpath('//ows:Title|//ows20:Title', namespaces=self.context.namespaces)[0].text etree.SubElement(databaseinfo, util.nspath_eval('zr:description', self.namespaces), lang='en', primary='true').text = element.xpath('//ows:Abstract|//ows20:Abstract', namespaces=self.context.namespaces)[0].text indexinfo = etree.SubElement(explain, util.nspath_eval('zr:indexInfo', self.namespaces)) etree.SubElement(indexinfo, util.nspath_eval('zr:set', self.namespaces), name='dc', identifier='info:srw/cql-context-set/1/dc-v1.1') for key, value in sorted(self.mappings['csw:Record']['index'].items()): zrindex = etree.SubElement(indexinfo, util.nspath_eval('zr:index', self.namespaces), id=value) etree.SubElement(zrindex, util.nspath_eval('zr:title', self.namespaces)).text = key zrmap = etree.SubElement(zrindex, util.nspath_eval('zr:map', self.namespaces)) etree.SubElement(zrmap, util.nspath_eval('zr:map', self.namespaces), set='dc').text = key zrindex = etree.SubElement(indexinfo, util.nspath_eval('zr:index', self.namespaces)) zrmap = etree.SubElement(zrindex, util.nspath_eval('zr:map', self.namespaces)) etree.SubElement(zrmap, util.nspath_eval('zr:name', self.namespaces), set='dc').text = 'title222' schemainfo = etree.SubElement(explain, util.nspath_eval('zr:schemaInfo', self.namespaces)) zrschema = etree.SubElement(schemainfo, util.nspath_eval('zr:schema', self.namespaces), name='dc', identifier='info:srw/schema/1/dc-v1.1') etree.SubElement(zrschema, util.nspath_eval('zr:title', self.namespaces)).text = 'Simple Dublin Core' configinfo = etree.SubElement(explain, util.nspath_eval('zr:configInfo', self.namespaces)) etree.SubElement(configinfo, util.nspath_eval('zr:default', self.namespaces), type='numberOfRecords').text = '0' elif response_name == 'GetRecordsResponse': recpos = int(element.xpath('//@nextRecord')[0]) - int(element.xpath('//@numberOfRecordsReturned')[0]) node = etree.Element(util.nspath_eval('zs:searchRetrieveResponse', self.namespaces), nsmap=self.namespaces) etree.SubElement(node, util.nspath_eval('zs:version', self.namespaces)).text = self.sru_version etree.SubElement(node, util.nspath_eval('zs:numberOfRecords', self.namespaces)).text = element.xpath('//@numberOfRecordsMatched')[0] for rec in element.xpath('//csw:BriefRecord', namespaces=self.context.namespaces): record = etree.SubElement(node, util.nspath_eval('zs:record', self.namespaces)) etree.SubElement(node, util.nspath_eval('zs:recordSchema', self.namespaces)).text = 'info:srw/schema/1/dc-v1.1' etree.SubElement(node, util.nspath_eval('zs:recordPacking', self.namespaces)).text = 'xml' recorddata = etree.SubElement(record, util.nspath_eval('zs:recordData', self.namespaces)) rec.tag = util.nspath_eval('srw_dc:srw_dc', self.namespaces) recorddata.append(rec) etree.SubElement(record, util.nspath_eval('zs:recordPosition', self.namespaces)).text = str(recpos) recpos += 1 elif response_name == 'ExceptionReport': node = self.exceptionreport2diagnostic(element) return node
def _csw3_2_os(self): """CSW 3.0.0 Capabilities to OpenSearch Description""" if util.xmltag_split(self.exml.tag) == 'GetRecordsResponse': startindex = int(self.exml.xpath('//@nextRecord')[0]) - int( self.exml.xpath('//@numberOfRecordsReturned')[0]) if startindex < 1: startindex = 1 node = etree.Element(util.nspath_eval('atom:feed', self.context.namespaces), nsmap=self.namespaces) etree.SubElement(node, util.nspath_eval('atom:id', self.context.namespaces)).text = self.cfg.get('server', 'url') etree.SubElement(node, util.nspath_eval('atom:title', self.context.namespaces)).text = self.cfg.get('metadata:main', 'identification_title') author = etree.SubElement(node, util.nspath_eval('atom:author', self.context.namespaces)) etree.SubElement(author, util.nspath_eval('atom:name', self.context.namespaces)).text = self.cfg.get('metadata:main', 'provider_name') etree.SubElement(node, util.nspath_eval('atom:link', self.context.namespaces), rel='search', type='application/opensearchdescription+xml', href='%smode=opensearch&service=CSW&version=3.0.0&request=GetCapabilities' % self.bind_url) etree.SubElement(node, util.nspath_eval('atom:updated', self.context.namespaces)).text = self.exml.xpath('//@timestamp')[0] etree.SubElement(node, util.nspath_eval('os:Query', self.context.namespaces), role='request') etree.SubElement(node, util.nspath_eval('os:totalResults', self.context.namespaces)).text = self.exml.xpath( '//@numberOfRecordsMatched')[0] etree.SubElement(node, util.nspath_eval('os:startIndex', self.context.namespaces)).text = str(startindex) etree.SubElement(node, util.nspath_eval('os:itemsPerPage', self.context.namespaces)).text = self.exml.xpath( '//@numberOfRecordsReturned')[0] for rec in self.exml.xpath('//atom:entry', namespaces=self.context.namespaces): node.append(rec) elif util.xmltag_split(self.exml.tag) == 'Capabilities': node = etree.Element(util.nspath_eval('os:OpenSearchDescription', self.namespaces), nsmap=self.namespaces) etree.SubElement(node, util.nspath_eval('os:ShortName', self.namespaces)).text = self.exml.xpath('//ows20:Title', namespaces=self.context.namespaces)[0].text[:16] etree.SubElement(node, util.nspath_eval('os:LongName', self.namespaces)).text = self.exml.xpath('//ows20:Title', namespaces=self.context.namespaces)[0].text etree.SubElement(node, util.nspath_eval('os:Description', self.namespaces)).text = self.exml.xpath('//ows20:Abstract', namespaces=self.context.namespaces)[0].text etree.SubElement(node, util.nspath_eval('os:Tags', self.namespaces)).text = ' '.join(x.text for x in self.exml.xpath('//ows20:Keyword', namespaces=self.context.namespaces)) # Requirement-022 node1 = etree.SubElement(node, util.nspath_eval('os:Url', self.namespaces)) node1.set('type', 'application/xml') node1.set('template', '%sservice=CSW&version=3.0.0&request=GetRecords&elementsetname=full&typenames=csw:Record&resulttype=results&q={searchTerms?}&bbox={geo:box?}&time={time:start?}/{time:end?}&outputformat=application/xml&outputschema=http://www.opengis.net/cat/csw/3.0&startposition={startIndex?}&maxrecords={count?}&recordids={geo:uid}' % self.bind_url) # Requirement-023 node1 = etree.SubElement(node, util.nspath_eval('os:Url', self.namespaces)) node1.set('type', 'application/atom+xml') node1.set('template', '%smode=opensearch&service=CSW&version=3.0.0&request=GetRecords&elementsetname=full&typenames=csw:Record&resulttype=results&q={searchTerms?}&bbox={geo:box?}&time={time:start?}/{time:end?}&outputformat=application/atom%%2Bxml&&startposition={startIndex?}&maxrecords={count?}&recordids={geo:uid}' % self.bind_url) node1 = etree.SubElement(node, util.nspath_eval('os:Image', self.namespaces)) node1.set('type', 'image/vnd.microsoft.icon') node1.set('width', '16') node1.set('height', '16') node1.text = 'http://pycsw.org/img/favicon.ico' os_query = etree.SubElement(node, util.nspath_eval('os:Query', self.namespaces), role='example') os_query.attrib[util.nspath_eval('geo:box', self.namespaces)] = '-180,-90,180,90' etree.SubElement(node, util.nspath_eval('os:Developer', self.namespaces)).text = self.exml.xpath('//ows20:IndividualName', namespaces=self.context.namespaces)[0].text etree.SubElement(node, util.nspath_eval('os:Contact', self.namespaces)).text = self.exml.xpath('//ows20:ElectronicMailAddress', namespaces=self.context.namespaces)[0].text etree.SubElement(node, util.nspath_eval('os:Attribution', self.namespaces)).text = self.exml.xpath('//ows20:ProviderName', namespaces=self.context.namespaces)[0].text elif util.xmltag_split(self.exml.tag) == 'ExceptionReport': node = self.exml else: # GetRecordById output node = etree.Element(util.nspath_eval('atom:feed', self.context.namespaces), nsmap=self.namespaces) etree.SubElement(node, util.nspath_eval('atom:id', self.context.namespaces)).text = self.cfg.get('server', 'url') etree.SubElement(node, util.nspath_eval('atom:title', self.context.namespaces)).text = self.cfg.get('metadata:main', 'identification_title') #etree.SubElement(node, util.nspath_eval('atom:updated', # self.context.namespaces)).text = self.exml.xpath('//@timestamp')[0] etree.SubElement(node, util.nspath_eval('os:totalResults', self.context.namespaces)).text = '1' etree.SubElement(node, util.nspath_eval('os:startIndex', self.context.namespaces)).text = '1' etree.SubElement(node, util.nspath_eval('os:itemsPerPage', self.context.namespaces)).text = '1' for rec in self.exml.xpath('//atom:entry', namespaces=self.context.namespaces): #node.append(rec) node = rec return node
def cswrecord2atom(self, rec): entry = etree.Element(util.nspath_eval('atom:entry', self.namespaces)) etree.SubElement(entry, util.nspath_eval('atom:id', self.context.namespaces)).text = rec.xpath('dc:identifier', namespaces=self.context.namespaces)[0].text etree.SubElement(entry, util.nspath_eval('dc:identifier', self.context.namespaces)).text = rec.xpath('dc:identifier', namespaces=self.context.namespaces)[0].text etree.SubElement(entry, util.nspath_eval('atom:title', self.context.namespaces)).text = rec.xpath('dc:title', namespaces=self.context.namespaces)[0].text dc_date = rec.xpath('dc:date', namespaces=self.context.namespaces) if dc_date: etree.SubElement(entry, util.nspath_eval('atom:updated', self.context.namespaces)).text = dc_date[0].text for s in rec.xpath('dc:subject', namespaces=self.context.namespaces): etree.SubElement(entry, util.nspath_eval('atom:category', self.context.namespaces), term=s.text) for d in rec.xpath('dct:references', namespaces=self.context.namespaces): link = etree.SubElement(entry, util.nspath_eval('atom:link', self.context.namespaces)) link.attrib['href'] = d.text scheme = d.attrib.get('scheme') if scheme is not None: if scheme == 'enclosure': link.attrib['rel'] = scheme link.attrib['type'] = 'application/octet-stream' else: link.attrib['type'] = scheme bbox = rec.xpath('ows:BoundingBox|ows20:BoundingBox', namespaces=self.context.namespaces) if bbox: where = etree.SubElement(entry, util.nspath_eval('georss:where', {'georss': 'http://www.georss.org/georss'})) envelope = etree.SubElement(where, util.nspath_eval('gml:Envelope', self.context.namespaces)) envelope.attrib['srsName'] = bbox[0].attrib.get('crs') etree.SubElement(envelope, util.nspath_eval('gml:lowerCorner', self.context.namespaces)).text = bbox[0].xpath('ows:LowerCorner|ows20:LowerCorner', namespaces=self.context.namespaces)[0].text etree.SubElement(envelope, util.nspath_eval('gml:upperCorner', self.context.namespaces)).text = bbox[0].xpath('ows:UpperCorner|ows20:UpperCorner', namespaces=self.context.namespaces)[0].text return entry
def _get_comparison_expression(elem): """return the SQL expression based on Filter query""" fname = None matchcase = elem.attrib.get('matchCase') wildcard = elem.attrib.get('wildCard') singlechar = elem.attrib.get('singleChar') expression = None if wildcard is None: wildcard = '%' if singlechar is None: singlechar = '_' if (elem.xpath('child::*')[0].tag == util.nspath_eval('ogc:Function', nsmap)): LOGGER.debug('ogc:Function detected') if (elem.xpath('child::*')[0].attrib['name'] not in MODEL['Functions']): raise RuntimeError('Invalid ogc:Function: %s' % (elem.xpath('child::*')[0].attrib['name'])) fname = elem.xpath('child::*')[0].attrib['name'] try: LOGGER.debug('Testing existence of ogc:PropertyName') pname = queryables[elem.find(util.nspath_eval('ogc:Function/ogc:PropertyName', nsmap)).text]['dbcol'] except Exception as err: raise RuntimeError('Invalid PropertyName: %s. %s' % (elem.find(util.nspath_eval('ogc:Function/ogc:PropertyName', nsmap)).text, str(err))) else: try: LOGGER.debug('Testing existence of ogc:PropertyName') pname = queryables[elem.find( util.nspath_eval('ogc:PropertyName', nsmap)).text]['dbcol'] except Exception as err: raise RuntimeError('Invalid PropertyName: %s. %s' % (elem.find(util.nspath_eval('ogc:PropertyName', nsmap)).text, str(err))) if (elem.tag != util.nspath_eval('ogc:PropertyIsBetween', nsmap)): if elem.tag in [util.nspath_eval('ogc:%s' % n, nsmap) for n in MODEL['SpatialOperators']['values']]: boolean_true = '\'true\'' boolean_false = '\'false\'' if dbtype == 'mysql': boolean_true = 'true' boolean_false = 'false' return "%s = %s" % (_get_spatial_operator(queryables['pycsw:BoundingBox'], elem, dbtype, nsmap), boolean_true) else: pval = elem.find(util.nspath_eval('ogc:Literal', nsmap)).text com_op = _get_comparison_operator(elem) LOGGER.debug('Comparison operator: %s' % com_op) # if this is a case insensitive search # then set the DB-specific LIKE comparison operator LOGGER.debug('Setting csw:AnyText property') anytext = queryables['csw:AnyText']['dbcol'] if ((matchcase is not None and matchcase == 'false') or pname == anytext): com_op = 'ilike' if is_pg else 'like' if (elem.tag == util.nspath_eval('ogc:PropertyIsBetween', nsmap)): com_op = 'between' lower_boundary = elem.find( util.nspath_eval('ogc:LowerBoundary/ogc:Literal', nsmap)).text upper_boundary = elem.find( util.nspath_eval('ogc:UpperBoundary/ogc:Literal', nsmap)).text expression = "%s %s %s and %s" % \ (pname, com_op, assign_param(), assign_param()) values.append(lower_boundary) values.append(upper_boundary) else: if pname == anytext and is_pg and fts: LOGGER.debug('PostgreSQL FTS specific search') # do nothing, let FTS do conversion (#212) pvalue = pval else: LOGGER.debug('PostgreSQL non-FTS specific search') pvalue = pval.replace(wildcard, '%').replace(singlechar, '_') if pname == anytext: # pad anytext with wildcards LOGGER.debug('PostgreSQL non-FTS specific anytext search') LOGGER.debug('old value: %s', pval) pvalue = '%%%s%%' % pvalue.rstrip('%').lstrip('%') LOGGER.debug('new value: %s', pvalue) values.append(pvalue) if boq == ' not ': if fname is not None: expression = "%s is null or not %s(%s) %s %s" % \ (pname, fname, pname, com_op, assign_param()) elif pname == anytext and is_pg and fts: LOGGER.debug('PostgreSQL FTS specific search') expression = ("%s is null or not plainto_tsquery('%s', %s) @@ anytext_tsvector" % (anytext, language, assign_param())) else: LOGGER.debug('PostgreSQL non-FTS specific search') expression = "%s is null or not %s %s %s" % \ (pname, pname, com_op, assign_param()) else: if fname is not None: expression = "%s(%s) %s %s" % \ (fname, pname, com_op, assign_param()) elif pname == anytext and is_pg and fts: LOGGER.debug('PostgreSQL FTS specific search') expression = ("plainto_tsquery('%s', %s) @@ anytext_tsvector" % (language, assign_param())) else: LOGGER.debug('PostgreSQL non-FTS specific search') expression = "%s %s %s" % (pname, com_op, assign_param()) return expression
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 _get_comparison_expression(elem): """return the SQL expression based on Filter query""" fname = None matchcase = elem.attrib.get('matchCase') wildcard = elem.attrib.get('wildCard') singlechar = elem.attrib.get('singleChar') expression = None if wildcard is None: wildcard = '%' if singlechar is None: singlechar = '_' if (elem.xpath('child::*')[0].tag == util.nspath_eval( 'ogc:Function', nsmap)): LOGGER.debug('ogc:Function detected') if (elem.xpath('child::*')[0].attrib['name'] not in MODEL['Functions']): raise RuntimeError('Invalid ogc:Function: %s' % (elem.xpath('child::*')[0].attrib['name'])) fname = elem.xpath('child::*')[0].attrib['name'] try: LOGGER.debug('Testing existence of ogc:PropertyName') pname = queryables[elem.find( util.nspath_eval('ogc:Function/ogc:PropertyName', nsmap)).text]['dbcol'] except Exception as err: raise RuntimeError( 'Invalid PropertyName: %s. %s' % (elem.find( util.nspath_eval('ogc:Function/ogc:PropertyName', nsmap)).text, str(err))) else: try: LOGGER.debug('Testing existence of ogc:PropertyName') pname = queryables[elem.find( util.nspath_eval('ogc:PropertyName', nsmap)).text]['dbcol'] except Exception as err: raise RuntimeError( 'Invalid PropertyName: %s. %s' % (elem.find(util.nspath_eval('ogc:PropertyName', nsmap)).text, str(err))) if (elem.tag != util.nspath_eval('ogc:PropertyIsBetween', nsmap)): if elem.tag in [ util.nspath_eval('ogc:%s' % n, nsmap) for n in MODEL['SpatialOperators']['values'] ]: boolean_true = '\'true\'' boolean_false = '\'false\'' if dbtype == 'mysql': boolean_true = 'true' boolean_false = 'false' return "%s = %s" % (_get_spatial_operator( queryables['pycsw:BoundingBox'], elem, dbtype, nsmap), boolean_true) else: pval = elem.find(util.nspath_eval('ogc:Literal', nsmap)).text com_op = _get_comparison_operator(elem) LOGGER.debug('Comparison operator: %s' % com_op) # if this is a case insensitive search # then set the DB-specific LIKE comparison operator LOGGER.debug('Setting csw:AnyText property') anytext = queryables['csw:AnyText']['dbcol'] if ((matchcase is not None and matchcase == 'false') or pname == anytext): com_op = 'ilike' if is_pg else 'like' if (elem.tag == util.nspath_eval('ogc:PropertyIsBetween', nsmap)): com_op = 'between' lower_boundary = elem.find( util.nspath_eval('ogc:LowerBoundary/ogc:Literal', nsmap)).text upper_boundary = elem.find( util.nspath_eval('ogc:UpperBoundary/ogc:Literal', nsmap)).text expression = "%s %s %s and %s" % \ (pname, com_op, assign_param(), assign_param()) values.append(lower_boundary) values.append(upper_boundary) else: if pname == anytext and is_pg and fts: LOGGER.debug('PostgreSQL FTS specific search') # do nothing, let FTS do conversion (#212) pvalue = pval else: LOGGER.debug('PostgreSQL non-FTS specific search') pvalue = pval.replace(wildcard, '%').replace(singlechar, '_') if pname == anytext: # pad anytext with wildcards LOGGER.debug('PostgreSQL non-FTS specific anytext search') LOGGER.debug('old value: %s', pval) pvalue = '%%%s%%' % pvalue.rstrip('%').lstrip('%') LOGGER.debug('new value: %s', pvalue) values.append(pvalue) if boq == ' not ': if fname is not None: expression = "%s is null or not %s(%s) %s %s" % \ (pname, fname, pname, com_op, assign_param()) elif pname == anytext and is_pg and fts: LOGGER.debug('PostgreSQL FTS specific search') expression = ( "%s is null or not plainto_tsquery('%s', %s) @@ anytext_tsvector" % (anytext, language, assign_param())) else: LOGGER.debug('PostgreSQL non-FTS specific search') expression = "%s is null or not %s %s %s" % \ (pname, pname, com_op, assign_param()) else: if fname is not None: expression = "%s(%s) %s %s" % \ (fname, pname, com_op, assign_param()) elif pname == anytext and is_pg and fts: LOGGER.debug('PostgreSQL FTS specific search') expression = ( "plainto_tsquery('%s', %s) @@ anytext_tsvector" % (language, assign_param())) else: LOGGER.debug('PostgreSQL non-FTS specific search') expression = "%s %s %s" % (pname, com_op, assign_param()) return expression
def _csw3_2_os(self): """CSW 3.0.0 Capabilities to OpenSearch Description""" if util.xmltag_split(self.exml.tag) == 'GetRecordsResponse': startindex = int(self.exml.xpath('//@nextRecord')[0]) - int( self.exml.xpath('//@numberOfRecordsReturned')[0]) if startindex < 1: startindex = 1 node = etree.Element(util.nspath_eval('atom:feed', self.context.namespaces), nsmap=self.namespaces) etree.SubElement(node, util.nspath_eval('atom:id', self.context.namespaces)).text = self.cfg.get('server', 'url') etree.SubElement(node, util.nspath_eval('atom:title', self.context.namespaces)).text = self.cfg.get('metadata:main', 'identification_title') author = etree.SubElement(node, util.nspath_eval('atom:author', self.context.namespaces)) etree.SubElement(author, util.nspath_eval('atom:name', self.context.namespaces)).text = self.cfg.get('metadata:main', 'provider_name') etree.SubElement(node, util.nspath_eval('atom:link', self.context.namespaces), rel='search', type='application/opensearchdescription+xml', href='%smode=opensearch&service=CSW&version=3.0.0&request=GetCapabilities' % self.bind_url) etree.SubElement(node, util.nspath_eval('atom:updated', self.context.namespaces)).text = self.exml.xpath('//@timestamp')[0] etree.SubElement(node, util.nspath_eval('os:Query', self.context.namespaces), role='request') etree.SubElement(node, util.nspath_eval('os:totalResults', self.context.namespaces)).text = self.exml.xpath( '//@numberOfRecordsMatched')[0] etree.SubElement(node, util.nspath_eval('os:startIndex', self.context.namespaces)).text = str(startindex) etree.SubElement(node, util.nspath_eval('os:itemsPerPage', self.context.namespaces)).text = self.exml.xpath( '//@numberOfRecordsReturned')[0] for rec in self.exml.xpath('//atom:entry', namespaces=self.context.namespaces): node.append(rec) elif util.xmltag_split(self.exml.tag) == 'Capabilities': node = etree.Element(util.nspath_eval('os:OpenSearchDescription', self.namespaces), nsmap=self.namespaces) etree.SubElement(node, util.nspath_eval('os:ShortName', self.namespaces)).text = self.exml.xpath('//ows20:Title', namespaces=self.context.namespaces)[0].text[:16] etree.SubElement(node, util.nspath_eval('os:LongName', self.namespaces)).text = self.exml.xpath('//ows20:Title', namespaces=self.context.namespaces)[0].text etree.SubElement(node, util.nspath_eval('os:Description', self.namespaces)).text = self.exml.xpath('//ows20:Abstract', namespaces=self.context.namespaces)[0].text etree.SubElement(node, util.nspath_eval('os:Tags', self.namespaces)).text = ' '.join(x.text for x in self.exml.xpath('//ows20:Keyword', namespaces=self.context.namespaces)) # Requirement-022 node1 = etree.SubElement(node, util.nspath_eval('os:Url', self.namespaces)) node1.set('type', 'application/xml') node1.set('template', '%sservice=CSW&version=3.0.0&request=GetRecords&elementsetname=full&typenames=csw:Record&resulttype=results&q={searchTerms?}&bbox={geo:box?}&time={time:start?}/{time:end?}&outputformat=application/xml&outputschema=http://www.opengis.net/cat/csw/3.0&startposition={startIndex?}&maxrecords={count?}&recordids={geo:uid}' % self.bind_url) # Requirement-023 node1 = etree.SubElement(node, util.nspath_eval('os:Url', self.namespaces)) node1.set('type', 'application/atom+xml') node1.set('template', '%smode=opensearch&service=CSW&version=3.0.0&request=GetRecords&elementsetname=full&typenames=csw:Record&resulttype=results&q={searchTerms?}&bbox={geo:box?}&time={time:start?}/{time:end?}&outputformat=application/atom+xml&&startposition={startIndex?}&maxrecords={count?}&recordids={geo:uid}' % self.bind_url) node1 = etree.SubElement(node, util.nspath_eval('os:Image', self.namespaces)) node1.set('type', 'image/vnd.microsoft.icon') node1.set('width', '16') node1.set('height', '16') node1.text = 'http://pycsw.org/img/favicon.ico' os_query = etree.SubElement(node, util.nspath_eval('os:Query', self.namespaces), role='example') os_query.attrib[util.nspath_eval('geo:box', self.namespaces)] = '-180,-90,180,90' etree.SubElement(node, util.nspath_eval('os:Developer', self.namespaces)).text = self.exml.xpath('//ows20:IndividualName', namespaces=self.context.namespaces)[0].text etree.SubElement(node, util.nspath_eval('os:Contact', self.namespaces)).text = self.exml.xpath('//ows20:ElectronicMailAddress', namespaces=self.context.namespaces)[0].text etree.SubElement(node, util.nspath_eval('os:Attribution', self.namespaces)).text = self.exml.xpath('//ows20:ProviderName', namespaces=self.context.namespaces)[0].text elif util.xmltag_split(self.exml.tag) == 'ExceptionReport': node = self.exml else: # GetRecordById output node = etree.Element(util.nspath_eval('atom:feed', self.context.namespaces), nsmap=self.namespaces) etree.SubElement(node, util.nspath_eval('atom:id', self.context.namespaces)).text = self.cfg.get('server', 'url') etree.SubElement(node, util.nspath_eval('atom:title', self.context.namespaces)).text = self.cfg.get('metadata:main', 'identification_title') #etree.SubElement(node, util.nspath_eval('atom:updated', # self.context.namespaces)).text = self.exml.xpath('//@timestamp')[0] etree.SubElement(node, util.nspath_eval('os:totalResults', self.context.namespaces)).text = '1' etree.SubElement(node, util.nspath_eval('os:startIndex', self.context.namespaces)).text = '1' etree.SubElement(node, util.nspath_eval('os:itemsPerPage', self.context.namespaces)).text = '1' for rec in self.exml.xpath('//atom:entry', namespaces=self.context.namespaces): #node.append(rec) node = rec return node
def _csw2_2_os(self): """CSW 2.0.2 Capabilities to OpenSearch Description""" if util.xmltag_split(self.exml.tag) == 'GetRecordsResponse': startindex = int(self.exml.xpath('//@nextRecord')[0]) - int( self.exml.xpath('//@numberOfRecordsReturned')[0]) if startindex < 1: startindex = 1 node = etree.Element(util.nspath_eval('atom:feed', self.context.namespaces), nsmap=self.namespaces) etree.SubElement(node, util.nspath_eval('atom:id', self.context.namespaces)).text = self.cfg.get('server', 'url') etree.SubElement(node, util.nspath_eval('atom:title', self.context.namespaces)).text = self.cfg.get('metadata:main', 'identification_title') #etree.SubElement(node, util.nspath_eval('atom:updated', # self.context.namespaces)).text = self.exml.xpath('//@timestamp')[0] etree.SubElement(node, util.nspath_eval('os:totalResults', self.context.namespaces)).text = self.exml.xpath( '//@numberOfRecordsMatched')[0] etree.SubElement(node, util.nspath_eval('os:startIndex', self.context.namespaces)).text = str(startindex) etree.SubElement(node, util.nspath_eval('os:itemsPerPage', self.context.namespaces)).text = self.exml.xpath( '//@numberOfRecordsReturned')[0] for rec in self.exml.xpath('//atom:entry', namespaces=self.context.namespaces): node.append(rec) elif util.xmltag_split(self.exml.tag) == 'Capabilities': node = etree.Element(util.nspath_eval('os:OpenSearchDescription', self.namespaces), nsmap=self.namespaces) etree.SubElement(node, util.nspath_eval('os:ShortName', self.namespaces)).text = self.exml.xpath('//ows:Title', namespaces=self.context.namespaces)[0].text etree.SubElement(node, util.nspath_eval('os:LongName', self.namespaces)).text = self.exml.xpath('//ows:Title', namespaces=self.context.namespaces)[0].text etree.SubElement(node, util.nspath_eval('os:Description', self.namespaces)).text = self.exml.xpath('//ows:Abstract', namespaces=self.context.namespaces)[0].text etree.SubElement(node, util.nspath_eval('os:Tags', self.namespaces)).text = ' '.join(x.text for x in self.exml.xpath('//ows:Keyword', namespaces=self.context.namespaces)) node1 = etree.SubElement(node, util.nspath_eval('os:Url', self.namespaces)) node1.set('type', 'application/atom+xml') node1.set('method', 'get') node1.set('template', '%smode=opensearch&service=CSW&version=2.0.2&request=GetRecords&elementsetname=full&typenames=csw:Record&resulttype=results&q={searchTerms?}&bbox={geo:box?}&time={time:start?}/{time:end?}&startposition={startIndex?}&maxrecords={count?}' % self.bind_url) node1 = etree.SubElement(node, util.nspath_eval('os:Image', self.namespaces)) node1.set('type', 'image/vnd.microsoft.icon') node1.set('width', '16') node1.set('height', '16') node1.text = 'http://pycsw.org/img/favicon.ico' etree.SubElement(node, util.nspath_eval('os:Developer', self.namespaces)).text = self.exml.xpath('//ows:IndividualName', namespaces=self.context.namespaces)[0].text etree.SubElement(node, util.nspath_eval('os:Context', self.namespaces)).text = self.exml.xpath('//ows:ElectronicMailAddress', namespaces=self.context.namespaces)[0].text etree.SubElement(node, util.nspath_eval('os:Attribution', self.namespaces)).text = self.exml.xpath('//ows:ProviderName', namespaces=self.context.namespaces)[0].text elif util.xmltag_split(self.exml.tag) == 'ExceptionReport': node = self.exml else: # return Description document node = etree.Element(util.nspath_eval('os:Description', self.context.namespaces)) return node
def write_record(recobj, esn, context, url=None): ''' Return csw:SearchResults child as lxml.etree.Element ''' typename = util.getqattr(recobj, context.md_core_model['mappings']['pycsw:Typename']) if esn == 'full' and typename == 'fgdc:metadata': # dump record as is and exit return etree.fromstring(util.getqattr(recobj, context.md_core_model['mappings']['pycsw:XML']), context.parser) node = etree.Element('metadata') node.attrib[util.nspath_eval('xsi:noNamespaceSchemaLocation', context.namespaces)] = \ 'http://www.fgdc.gov/metadata/fgdc-std-001-1998.xsd' idinfo = etree.SubElement(node, 'idinfo') # identifier etree.SubElement(idinfo, 'datasetid').text = util.getqattr(recobj, context.md_core_model['mappings']['pycsw:Identifier']) citation = etree.SubElement(idinfo, 'citation') citeinfo = etree.SubElement(citation, 'citeinfo') # title val = util.getqattr(recobj, context.md_core_model['mappings']['pycsw:Title']) etree.SubElement(citeinfo, 'title').text = val # publisher publinfo = etree.SubElement(citeinfo, 'publinfo') val = util.getqattr(recobj, context.md_core_model['mappings']['pycsw:Publisher']) or '' etree.SubElement(publinfo, 'publish').text = val # origin val = util.getqattr(recobj, context.md_core_model['mappings']['pycsw:Creator']) or '' etree.SubElement(citeinfo, 'origin').text = val # keywords val = util.getqattr(recobj, context.md_core_model['mappings']['pycsw:Keywords']) if val: keywords = etree.SubElement(idinfo, 'keywords') theme = etree.SubElement(keywords, 'theme') for v in val.split(','): etree.SubElement(theme, 'themekey').text = v # accessconstraints val = util.getqattr(recobj, context.md_core_model['mappings']['pycsw:AccessConstraints']) or '' etree.SubElement(idinfo, 'accconst').text = val # abstract descript = etree.SubElement(idinfo, 'descript') val = util.getqattr(recobj, context.md_core_model['mappings']['pycsw:Abstract']) or '' etree.SubElement(descript, 'abstract').text = val # time datebegin = util.getqattr(recobj, context.md_core_model['mappings']['pycsw:TempExtent_begin']) dateend = util.getqattr(recobj, context.md_core_model['mappings']['pycsw:TempExtent_end']) if all([datebegin, dateend]): timeperd = etree.SubElement(idinfo, 'timeperd') timeinfo = etree.SubElement(timeperd, 'timeinfo') rngdates = etree.SubElement(timeinfo, 'timeinfo') begdate = etree.SubElement(rngdates, 'begdate').text = datebegin enddate = etree.SubElement(rngdates, 'enddate').text = dateend # bbox extent val = util.getqattr(recobj, context.md_core_model['mappings']['pycsw:BoundingBox']) bboxel = write_extent(val) if bboxel is not None: idinfo.append(bboxel) # contributor val = util.getqattr(recobj, context.md_core_model['mappings']['pycsw:Contributor']) or '' etree.SubElement(idinfo, 'datacred').text = val # direct spdoinfo = etree.SubElement(idinfo, 'spdoinfo') val = util.getqattr(recobj, context.md_core_model['mappings']['pycsw:Type']) or '' etree.SubElement(spdoinfo, 'direct').text = val # formname distinfo = etree.SubElement(node, 'distinfo') stdorder = etree.SubElement(distinfo, 'stdorder') digform = etree.SubElement(stdorder, 'digform') digtinfo = etree.SubElement(digform, 'digtinfo') val = util.getqattr(recobj, context.md_core_model['mappings']['pycsw:Format']) or '' etree.SubElement(digtinfo, 'formname').text = val etree.SubElement(citeinfo, 'geoform').text = val # source lineage = etree.SubElement(node, 'lineage') srcinfo = etree.SubElement(lineage, 'srcinfo') srccite = etree.SubElement(srcinfo, 'srccite') sciteinfo = etree.SubElement(srccite, 'citeinfo') val = util.getqattr(recobj, context.md_core_model['mappings']['pycsw:Source']) or '' etree.SubElement(sciteinfo, 'title').text = val val = util.getqattr(recobj, context.md_core_model['mappings']['pycsw:Relation']) or '' etree.SubElement(citeinfo, 'onlink').text = val # links rlinks = util.getqattr(recobj, context.md_core_model['mappings']['pycsw:Links']) if rlinks: for link in rlinks.split('^'): linkset = link.split(',') etree.SubElement(citeinfo, 'onlink', type=linkset[2]).text = linkset[-1] # metd metainfo = etree.SubElement(node, 'metainfo') val = util.getqattr(recobj, context.md_core_model['mappings']['pycsw:Modified']) or '' etree.SubElement(metainfo, 'metd').text = val return node