def response_csw2opensearch(self, element, cfg): """transform a CSW response into an OpenSearch response""" LOGGER.debug('RESPONSE: %s', util.xmltag_split(element.tag)) if util.xmltag_split(element.tag) == 'GetRecordsResponse': startindex = int(element.xpath('//@nextRecord')[0]) - int( element.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 = cfg.get('server', 'url') etree.SubElement(node, util.nspath_eval('atom:title', self.context.namespaces)).text = cfg.get('metadata:main', 'identification_title') #etree.SubElement(node, util.nspath_eval('atom:updated', # self.context.namespaces)).text = element.xpath('//@timestamp')[0] etree.SubElement(node, util.nspath_eval('os:totalResults', self.context.namespaces)).text = element.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 = element.xpath( '//@numberOfRecordsReturned')[0] for rec in element.xpath('//atom:entry', namespaces=self.context.namespaces): node.append(rec) elif util.xmltag_split(element.tag) == 'Capabilities': node = etree.Element('OpenSearchDescription', nsmap={None: self.namespaces['os']}) etree.SubElement(node, 'ShortName').text = element.xpath('//ows:Title', namespaces=self.context.namespaces)[0].text etree.SubElement(node, 'LongName').text = element.xpath('//ows:Title', namespaces=self.context.namespaces)[0].text etree.SubElement(node, 'Description').text = element.xpath('//ows:Abstract', namespaces=self.context.namespaces)[0].text etree.SubElement(node, 'Tags').text = ' '.join(x.text for x in element.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', '%s?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?}' % element.xpath('//ows:Get/@xlink:href', namespaces=self.context.namespaces)[0]) 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 = element.xpath('//ows:IndividualName', namespaces=self.context.namespaces)[0].text etree.SubElement(node, 'Contact').text = element.xpath('//ows:ElectronicMailAddress', namespaces=self.context.namespaces)[0].text etree.SubElement(node, 'Attribution').text = element.xpath('//ows:ProviderName', namespaces=self.context.namespaces)[0].text elif util.xmltag_split(element.tag) == 'ExceptionReport': node = element else: # return Description document node = etree.Element(util.nspath_eval('os:Description', self.context.namespaces)) return node
def parse(element, queryables, dbtype, nsmap, orm='sqlalchemy'): """OGC Filter object support""" boq = None 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, err: raise RuntimeError( 'Invalid PropertyName: %s. %s' % (elem.find( util.nspath_eval('ogc:Function/ogc:PropertyName', nsmap)).text, str(err))) else:
def _exml2dict(element, namespaces): ''' Convert an lxml object to a dict From: https://bitbucket.org/smulloni/pesterfish/src/1578db946d74/pesterfish.py ''' d = dict(tag='%s%s' % \ (xmltag_split2(element.tag, namespaces, True), xmltag_split(element.tag))) if element.text: if element.text.find('\n') == -1: d['text'] = element.text if element.attrib: d['attributes'] = dict(('%s%s' %(xmltag_split2(k, namespaces, True), \ xmltag_split(k)),f(v) if hasattr(v, 'keys') else v) \ for k,v in element.attrib.items()) children = element.getchildren() if children: d['children'] = map(lambda x: _exml2dict(x, namespaces), children) return d
def _exml2dict(element, namespaces): ''' Convert an lxml object to a dict From: https://bitbucket.org/smulloni/pesterfish/src/1578db946d74/pesterfish.py ''' d = dict(tag='%s%s' % \ (xmltag_split2(element.tag, namespaces, True), xmltag_split(element.tag))) if element.text: if element.text.find('\n') == -1: d['text'] = element.text if element.attrib: d['attributes'] = dict(('%s%s' %(xmltag_split2(k, namespaces, True), \ xmltag_split(k)),f(v) if hasattr(v, 'keys') else v) \ for k,v in element.attrib.items()) children = element.getchildren() if children: d['children'] = map(lambda x: _exml2dict(x, namespaces), children) return d
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, err: raise RuntimeError('Invalid PropertyName: %s. %s' % (elem.find(util.nspath_eval('ogc:Function/ogc:PropertyName', nsmap)).text, str(err))) else:
def response_csw2opensearch(self, element, cfg): """transform a CSW response into an OpenSearch response""" if util.xmltag_split(element.tag) == 'GetRecordsResponse': startindex = int(element.xpath('//@nextRecord')[0]) - int( element.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 = cfg.get( 'server', 'url') etree.SubElement( node, util.nspath_eval('atom:title', self.context.namespaces)).text = cfg.get( 'metadata:main', 'identification_title') #etree.SubElement(node, util.nspath_eval('atom:updated', self.context.namespaces)).text = element.xpath('//@timestamp')[0] etree.SubElement( node, util.nspath_eval( 'opensearch:totalResults', self.context.namespaces)).text = element.xpath( '//@numberOfRecordsMatched')[0] etree.SubElement( node, util.nspath_eval( 'opensearch:startIndex', self.context.namespaces)).text = str(startindex) etree.SubElement( node, util.nspath_eval( 'opensearch:itemsPerPage', self.context.namespaces)).text = element.xpath( '//@numberOfRecordsReturned')[0] for rec in element.xpath('//atom:entry', namespaces=self.context.namespaces): node.append(rec) return node
def __init__(self, element, nsmap): """initialize geometry parser""" self.nsmap = nsmap self.type = None self.wkt = None self.crs = None self._exml = element # return OGC WKT for GML geometry operand = element.xpath( '|'.join(TYPES), namespaces={'gml': 'http://www.opengis.net/gml'})[0] if 'srsName' in operand.attrib: LOGGER.debug('geometry srsName detected') self.crs = crs.Crs(operand.attrib['srsName']) else: LOGGER.debug('setting default geometry srsName %s' % DEFAULT_SRS) self.crs = DEFAULT_SRS self.type = util.xmltag_split(operand.tag) if self.type == 'Point': self._get_point() elif self.type == 'LineString': self._get_linestring() elif self.type == 'Polygon': self._get_polygon() elif self.type == 'Envelope': self._get_envelope() else: raise RuntimeError( 'Unsupported geometry type (Must be one of %s)' % ','.join(TYPES)) # reproject data if needed if self.crs is not None and self.crs.code != 4326: LOGGER.debug('transforming geometry to 4326') try: self.wkt = self.transform(self.crs.code, DEFAULT_SRS.code) except Exception, err: raise RuntimeError('Reprojection error: Invalid srsName ' '"%s": %s' % (self.crs.id, str(err)))
def __init__(self, element, nsmap): """initialize geometry parser""" self.nsmap = nsmap self.type = None self.wkt = None self.crs = None self._exml = element # return OGC WKT for GML geometry operand = element.xpath( '|'.join(TYPES), namespaces={'gml': 'http://www.opengis.net/gml'})[0] if 'srsName' in operand.attrib: LOGGER.debug('geometry srsName detected') self.crs = crs.Crs(operand.attrib['srsName']) else: LOGGER.debug('setting default geometry srsName %s' % DEFAULT_SRS) self.crs = DEFAULT_SRS self.type = util.xmltag_split(operand.tag) if self.type == 'Point': self._get_point() elif self.type == 'LineString': self._get_linestring() elif self.type == 'Polygon': self._get_polygon() elif self.type == 'Envelope': self._get_envelope() else: raise RuntimeError('Unsupported geometry type (Must be one of %s)' % ','.join(TYPES)) # reproject data if needed if self.crs is not None and self.crs.code != 4326: LOGGER.debug('transforming geometry to 4326') try: self.wkt = self.transform(self.crs.code, DEFAULT_SRS.code) except Exception, err: raise RuntimeError('Reprojection error: Invalid srsName ' '"%s": %s' % (self.crs.id, str(err)))
def response_csw2opensearch(self, element, cfg): """transform a CSW response into an OpenSearch response""" if util.xmltag_split(element.tag) == 'GetRecordsResponse': startindex = int(element.xpath('//@nextRecord')[0]) - int(element.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 = cfg.get('server', 'url') etree.SubElement(node, util.nspath_eval('atom:title', self.context.namespaces)).text = cfg.get('metadata:main', 'identification_title') #etree.SubElement(node, util.nspath_eval('atom:updated', self.context.namespaces)).text = element.xpath('//@timestamp')[0] etree.SubElement(node, util.nspath_eval('opensearch:totalResults', self.context.namespaces)).text = element.xpath('//@numberOfRecordsMatched')[0] etree.SubElement(node, util.nspath_eval('opensearch:startIndex', self.context.namespaces)).text = str(startindex) etree.SubElement(node, util.nspath_eval('opensearch:itemsPerPage', self.context.namespaces)).text = element.xpath('//@numberOfRecordsReturned')[0] for rec in element.xpath('//atom:entry', namespaces=self.context.namespaces): node.append(rec) return node
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 = gml.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
(_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'):
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 response_csw2sru(self, element, environ): """transform a CSW response into an SRU response""" if util.xmltag_split(element.tag) == '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', 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', 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 self.mappings['csw:Record']['index'].iteritems(): 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 util.xmltag_split(element.tag) == '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 util.xmltag_split(element.tag) == 'ExceptionReport': node = self.exceptionreport2diagnostic(element) return node
def parse(element, queryables, dbtype, nsmap): """OGC Filter object support""" boq = None 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 queries = [] 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') queries.append("%s = %s" % (_get_spatial_operator( queryables['pycsw:BoundingBox'], child.xpath('child::*')[0], dbtype, nsmap), boolean_false)) 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 if dbtype == 'postgresql+postgis': 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'], child.attrib.get('fid'))) else: fname = None matchcase = child.attrib.get('matchCase') wildcard = child.attrib.get('wildCard') singlechar = child.attrib.get('singleChar') if wildcard is None: wildcard = '%' if singlechar is None: singlechar = '_' if (child.xpath('child::*')[0].tag == util.nspath_eval('ogc:Function', nsmap)): LOGGER.debug('ogc:Function detected') if (child.xpath('child::*')[0].attrib['name'] not in MODEL['Functions']): raise RuntimeError('Invalid ogc:Function: %s' % (child.xpath('child::*')[0].attrib['name'])) fname = child.xpath('child::*')[0].attrib['name'] try: LOGGER.debug('Testing existence of ogc:PropertyName') pname = queryables[child.find(util.nspath_eval('ogc:Function/ogc:PropertyName', nsmap)).text]['dbcol'] except Exception, err: raise RuntimeError('Invalid PropertyName: %s. %s' % (child.find(util.nspath_eval('ogc:Function/ogc:PropertyName', nsmap)).text, str(err))) else: try: LOGGER.debug('Testing existence of ogc:PropertyName') pname = queryables[child.find( util.nspath_eval('ogc:PropertyName', nsmap)).text]['dbcol'] except Exception, err: raise RuntimeError('Invalid PropertyName: %s. %s' % (child.find(util.nspath_eval('ogc:PropertyName', nsmap)).text, str(err)))
def response(self, response, kvp, repository, server_url): """process OAI-PMH request""" mode = kvp.pop('mode', None) if 'config' in kvp: config_val = kvp.pop('config') url = '%smode=oaipmh' % util.bind_url(server_url) node = etree.Element(util.nspath_eval('oai:OAI-PMH', self.namespaces), nsmap=self.namespaces) node.set(util.nspath_eval('xsi:schemaLocation', self.namespaces), '%s http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd' % self.namespaces['oai']) LOGGER.info(etree.tostring(node)) etree.SubElement(node, util.nspath_eval('oai:responseDate', self.namespaces)).text = util.get_today_and_now() etree.SubElement(node, util.nspath_eval('oai:request', self.namespaces), attrib=kvp).text = url if 'verb' not in kvp: etree.SubElement(node, util.nspath_eval('oai:error', self.namespaces), code='badArgument').text = 'Missing \'verb\' parameter' return node if kvp['verb'] not in self.request_model.keys(): etree.SubElement(node, util.nspath_eval('oai:error', self.namespaces), code='badArgument').text = 'Unknown verb \'%s\'' % kvp['verb'] return node if util.xmltag_split(response.tag) == 'ExceptionReport': etree.SubElement(node, util.nspath_eval('oai:error', self.namespaces), code='badArgument').text = response.xpath('//ows:ExceptionText', namespaces=self.context.namespaces)[0].text return node verb = kvp.pop('verb') if verb in ['GetRecord', 'ListIdentifiers', 'ListRecords']: if 'metadataprefix' not in kvp: etree.SubElement(node, util.nspath_eval('oai:error', self.namespaces), code='badArgument').text = 'Missing metadataPrefix parameter' return node elif kvp['metadataprefix'] not in self.metadata_formats.keys(): etree.SubElement(node, util.nspath_eval('oai:error', self.namespaces), code='badArgument').text = 'Invalid metadataPrefix parameter' return node for key, value in kvp.iteritems(): if key != 'mode' and key not in self.request_model[verb]: etree.SubElement(node, util.nspath_eval('oai:error', self.namespaces), code='badArgument').text = 'Illegal parameter \'%s\'' % key return node verbnode = etree.SubElement(node, util.nspath_eval('oai:%s' % verb, self.namespaces)) if verb == 'Identify': etree.SubElement(verbnode, util.nspath_eval('oai:repositoryName', self.namespaces)).text = self.config.get('metadata:main', 'identification_title') etree.SubElement(verbnode, util.nspath_eval('oai:baseURL', self.namespaces)).text = url etree.SubElement(verbnode, util.nspath_eval('oai:protocolVersion', self.namespaces)).text = '2.0' etree.SubElement(verbnode, util.nspath_eval('oai:adminEmail', self.namespaces)).text = self.config.get('metadata:main', 'contact_email') etree.SubElement(verbnode, util.nspath_eval('oai:earliestDatestamp', self.namespaces)).text = repository.query_insert('min') etree.SubElement(verbnode, util.nspath_eval('oai:deletedRecord', self.namespaces)).text = 'no' etree.SubElement(verbnode, util.nspath_eval('oai:granularity', self.namespaces)).text = 'YYYY-MM-DDThh:mm:ssZ' elif verb == 'ListSets': for key, value in self.metadata_sets.iteritems(): setnode = etree.SubElement(verbnode, util.nspath_eval('oai:set', self.namespaces)) etree.SubElement(setnode, util.nspath_eval('oai:setSpec', self.namespaces)).text = key etree.SubElement(setnode, util.nspath_eval('oai:setName', self.namespaces)).text = value[0] elif verb == 'ListMetadataFormats': for key, value in self.metadata_formats.iteritems(): mdfnode = etree.SubElement(verbnode, util.nspath_eval('oai:metadataFormat', self.namespaces)) etree.SubElement(mdfnode, util.nspath_eval('oai:metadataPrefix', self.namespaces)).text = key etree.SubElement(mdfnode, util.nspath_eval('oai:schema', self.namespaces)).text = value['schema'] etree.SubElement(mdfnode, util.nspath_eval('oai:metadataNamespace', self.namespaces)).text = value['namespace'] elif verb in ['GetRecord', 'ListIdentifiers', 'ListRecords']: if verb == 'GetRecord': # GetRecordById records = response.getchildren() else: # GetRecords records = response.getchildren()[1].getchildren() for child in records: recnode = etree.SubElement(verbnode, util.nspath_eval('oai:record', self.namespaces)) header = etree.SubElement(recnode, util.nspath_eval('oai:header', self.namespaces)) self._transform_element(header, response, 'oai:identifier') self._transform_element(header, response, 'oai:dateStamp') self._transform_element(header, response, 'oai:setSpec') if verb in ['GetRecord', 'ListRecords']: metadata = etree.SubElement(recnode, util.nspath_eval('oai:metadata', self.namespaces)) if 'metadataprefix' in kvp and kvp['metadataprefix'] == 'oai_dc': child.tag = util.nspath_eval('oai_dc:dc', self.namespaces) metadata.append(child) if verb != 'GetRecord': complete_list_size = response.xpath('//@numberOfRecordsMatched')[0] next_record = response.xpath('//@nextRecord')[0] cursor = str(int(complete_list_size) - int(next_record) - 1) resumption_token = etree.SubElement(verbnode, util.nspath_eval('oai:resumptionToken', self.namespaces), completeListSize=complete_list_size, cursor=cursor).text = next_record return node
def _get_spatial_operator(geomattr, element, dbtype, nsmap): """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 = gml.Geometry(element, nsmap) 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": # adjust spatial query for PostGIS LOGGER.debug("Adjusting spatial query for PostgreSQL+PostGIS") 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) ) else: LOGGER.debug("Adjusting spatial query") spatial_query = "query_spatial(%s,'%s','%s','%s')" % (geomattr, geometry.wkt, spatial_predicate, distance) return spatial_query
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 = gml.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 _get_comparison_operator(element): """return the SQL operator based on Filter query""" return MODEL['ComparisonOperators']['ogc:%s' % util.xmltag_split( element.tag)]['opvalue']
def parse(element, queryables, dbtype, nsmap, orm="sqlalchemy"): """OGC Filter object support""" boq = None 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 queries = [] 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") queries.append( "%s = %s" % ( _get_spatial_operator(queryables["pycsw:BoundingBox"], child.xpath("child::*")[0], dbtype, nsmap), boolean_false, ) ) 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 if dbtype == "postgresql+postgis": 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: fname = None matchcase = child.attrib.get("matchCase") wildcard = child.attrib.get("wildCard") singlechar = child.attrib.get("singleChar") if wildcard is None: wildcard = "%" if singlechar is None: singlechar = "_" if child.xpath("child::*")[0].tag == util.nspath_eval("ogc:Function", nsmap): LOGGER.debug("ogc:Function detected") if child.xpath("child::*")[0].attrib["name"] not in MODEL["Functions"]: raise RuntimeError("Invalid ogc:Function: %s" % (child.xpath("child::*")[0].attrib["name"])) fname = child.xpath("child::*")[0].attrib["name"] try: LOGGER.debug("Testing existence of ogc:PropertyName") pname = queryables[child.find(util.nspath_eval("ogc:Function/ogc:PropertyName", nsmap)).text][ "dbcol" ] except Exception, err: raise RuntimeError( "Invalid PropertyName: %s. %s" % (child.find(util.nspath_eval("ogc:Function/ogc:PropertyName", nsmap)).text, str(err)) ) else: try: LOGGER.debug("Testing existence of ogc:PropertyName") pname = queryables[child.find(util.nspath_eval("ogc:PropertyName", nsmap)).text]["dbcol"] except Exception, err: raise RuntimeError( "Invalid PropertyName: %s. %s" % (child.find(util.nspath_eval("ogc:PropertyName", nsmap)).text, str(err)) )
def _get_comparison_operator(element): """return the SQL operator based on Filter query""" return MODEL["ComparisonOperators"]["ogc:%s" % util.xmltag_split(element.tag)]["opvalue"]
def _get_comparison_operator(element): """return the SQL operator based on Filter query""" return MODEL['ComparisonOperators']['ogc:%s' % util.xmltag_split(element.tag)]['opvalue']
def response(self, response, kvp, repository, server_url): """process OAI-PMH request""" mode = kvp.pop("mode", None) if "config" in kvp: config_val = kvp.pop("config") url = "%smode=oaipmh" % util.bind_url(server_url) node = etree.Element(util.nspath_eval("oai:OAI-PMH", self.namespaces), nsmap=self.namespaces) node.set( util.nspath_eval("xsi:schemaLocation", self.namespaces), "%s http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd" % self.namespaces["oai"], ) LOGGER.info(etree.tostring(node)) etree.SubElement(node, util.nspath_eval("oai:responseDate", self.namespaces)).text = util.get_today_and_now() etree.SubElement(node, util.nspath_eval("oai:request", self.namespaces), attrib=kvp).text = url if "verb" not in kvp: etree.SubElement( node, util.nspath_eval("oai:error", self.namespaces), code="badArgument" ).text = "Missing 'verb' parameter" return node if kvp["verb"] not in self.request_model.keys(): etree.SubElement(node, util.nspath_eval("oai:error", self.namespaces), code="badArgument").text = ( "Unknown verb '%s'" % kvp["verb"] ) return node if util.xmltag_split(response.tag) == "ExceptionReport": etree.SubElement( node, util.nspath_eval("oai:error", self.namespaces), code="badArgument" ).text = response.xpath("//ows:ExceptionText", namespaces=self.context.namespaces)[0].text return node verb = kvp.pop("verb") if verb in ["GetRecord", "ListIdentifiers", "ListRecords"]: if "metadataprefix" not in kvp: etree.SubElement( node, util.nspath_eval("oai:error", self.namespaces), code="badArgument" ).text = "Missing metadataPrefix parameter" return node elif kvp["metadataprefix"] not in self.metadata_formats.keys(): etree.SubElement( node, util.nspath_eval("oai:error", self.namespaces), code="badArgument" ).text = "Invalid metadataPrefix parameter" return node for key, value in kvp.iteritems(): if key != "mode" and key not in self.request_model[verb]: etree.SubElement(node, util.nspath_eval("oai:error", self.namespaces), code="badArgument").text = ( "Illegal parameter '%s'" % key ) return node verbnode = etree.SubElement(node, util.nspath_eval("oai:%s" % verb, self.namespaces)) if verb == "Identify": etree.SubElement(verbnode, util.nspath_eval("oai:repositoryName", self.namespaces)).text = self.config.get( "metadata:main", "identification_title" ) etree.SubElement(verbnode, util.nspath_eval("oai:baseURL", self.namespaces)).text = url etree.SubElement(verbnode, util.nspath_eval("oai:protocolVersion", self.namespaces)).text = "2.0" etree.SubElement(verbnode, util.nspath_eval("oai:adminEmail", self.namespaces)).text = self.config.get( "metadata:main", "contact_email" ) etree.SubElement( verbnode, util.nspath_eval("oai:earliestDatestamp", self.namespaces) ).text = repository.query_insert("min") etree.SubElement(verbnode, util.nspath_eval("oai:deletedRecord", self.namespaces)).text = "no" etree.SubElement( verbnode, util.nspath_eval("oai:granularity", self.namespaces) ).text = "YYYY-MM-DDThh:mm:ssZ" elif verb == "ListSets": for key, value in self.metadata_sets.iteritems(): setnode = etree.SubElement(verbnode, util.nspath_eval("oai:set", self.namespaces)) etree.SubElement(setnode, util.nspath_eval("oai:setSpec", self.namespaces)).text = key etree.SubElement(setnode, util.nspath_eval("oai:setName", self.namespaces)).text = value[0] elif verb == "ListMetadataFormats": for key, value in self.metadata_formats.iteritems(): mdfnode = etree.SubElement(verbnode, util.nspath_eval("oai:metadataFormat", self.namespaces)) etree.SubElement(mdfnode, util.nspath_eval("oai:metadataPrefix", self.namespaces)).text = key etree.SubElement(mdfnode, util.nspath_eval("oai:schema", self.namespaces)).text = value["schema"] etree.SubElement(mdfnode, util.nspath_eval("oai:metadataNamespace", self.namespaces)).text = value[ "namespace" ] elif verb in ["GetRecord", "ListIdentifiers", "ListRecords"]: if verb == "GetRecord": # GetRecordById records = response.getchildren() else: # GetRecords records = response.getchildren()[1].getchildren() for child in records: recnode = etree.SubElement(verbnode, util.nspath_eval("oai:record", self.namespaces)) header = etree.SubElement(recnode, util.nspath_eval("oai:header", self.namespaces)) self._transform_element(header, response, "oai:identifier") self._transform_element(header, response, "oai:dateStamp") self._transform_element(header, response, "oai:setSpec") if verb in ["GetRecord", "ListRecords"]: metadata = etree.SubElement(recnode, util.nspath_eval("oai:metadata", self.namespaces)) if "metadataprefix" in kvp and kvp["metadataprefix"] == "oai_dc": child.tag = util.nspath_eval("oai_dc:dc", self.namespaces) metadata.append(child) if verb != "GetRecord": complete_list_size = response.xpath("//@numberOfRecordsMatched")[0] next_record = response.xpath("//@nextRecord")[0] cursor = str(int(complete_list_size) - int(next_record) - 1) resumption_token = etree.SubElement( verbnode, util.nspath_eval("oai:resumptionToken", self.namespaces), completeListSize=complete_list_size, cursor=cursor, ).text = next_record return node
def response_csw2sru(self, element, environ): """transform a CSW response into an SRU response""" if util.xmltag_split(element.tag) == "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", 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", 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 self.mappings["csw:Record"]["index"].iteritems(): 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 util.xmltag_split(element.tag) == "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 util.xmltag_split(element.tag) == "ExceptionReport": node = self.exceptionreport2diagnostic(element) return node
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 response(self, response, kvp, repository, server_url): """process OAI-PMH request""" mode = kvp.pop('mode', None) if 'config' in kvp: config_val = kvp.pop('config') url = '%smode=oaipmh' % util.bind_url(server_url) node = etree.Element(util.nspath_eval('oai:OAI-PMH', self.namespaces), nsmap=self.namespaces) node.set( util.nspath_eval('xsi:schemaLocation', self.namespaces), '%s http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd' % self.namespaces['oai']) LOGGER.info(etree.tostring(node)) etree.SubElement(node, util.nspath_eval( 'oai:responseDate', self.namespaces)).text = util.get_today_and_now() etree.SubElement(node, util.nspath_eval('oai:request', self.namespaces), attrib=kvp).text = url if 'verb' not in kvp: etree.SubElement( node, util.nspath_eval('oai:error', self.namespaces), code='badArgument').text = 'Missing \'verb\' parameter' return node if kvp['verb'] not in self.request_model.keys(): etree.SubElement( node, util.nspath_eval('oai:error', self.namespaces), code='badArgument').text = 'Unknown verb \'%s\'' % kvp['verb'] return node if util.xmltag_split(response.tag) == 'ExceptionReport': etree.SubElement(node, util.nspath_eval('oai:error', self.namespaces), code='badArgument').text = response.xpath( '//ows:ExceptionText', namespaces=self.context.namespaces)[0].text return node verb = kvp.pop('verb') if verb in ['GetRecord', 'ListIdentifiers', 'ListRecords']: if 'metadataprefix' not in kvp: etree.SubElement(node, util.nspath_eval('oai:error', self.namespaces), code='badArgument' ).text = 'Missing metadataPrefix parameter' return node elif kvp['metadataprefix'] not in self.metadata_formats.keys(): etree.SubElement(node, util.nspath_eval('oai:error', self.namespaces), code='badArgument' ).text = 'Invalid metadataPrefix parameter' return node for key, value in kvp.iteritems(): if key != 'mode' and key not in self.request_model[verb]: etree.SubElement( node, util.nspath_eval('oai:error', self.namespaces), code='badArgument').text = 'Illegal parameter \'%s\'' % key return node verbnode = etree.SubElement( node, util.nspath_eval('oai:%s' % verb, self.namespaces)) if verb == 'Identify': etree.SubElement( verbnode, util.nspath_eval('oai:repositoryName', self.namespaces)).text = self.config.get( 'metadata:main', 'identification_title') etree.SubElement(verbnode, util.nspath_eval('oai:baseURL', self.namespaces)).text = url etree.SubElement( verbnode, util.nspath_eval('oai:protocolVersion', self.namespaces)).text = '2.0' etree.SubElement( verbnode, util.nspath_eval('oai:adminEmail', self.namespaces)).text = self.config.get( 'metadata:main', 'contact_email') etree.SubElement( verbnode, util.nspath_eval( 'oai:earliestDatestamp', self.namespaces)).text = repository.query_insert('min') etree.SubElement( verbnode, util.nspath_eval('oai:deletedRecord', self.namespaces)).text = 'no' etree.SubElement( verbnode, util.nspath_eval( 'oai:granularity', self.namespaces)).text = 'YYYY-MM-DDThh:mm:ssZ' elif verb == 'ListSets': for key, value in self.metadata_sets.iteritems(): setnode = etree.SubElement( verbnode, util.nspath_eval('oai:set', self.namespaces)) etree.SubElement( setnode, util.nspath_eval('oai:setSpec', self.namespaces)).text = key etree.SubElement( setnode, util.nspath_eval('oai:setName', self.namespaces)).text = value[0] elif verb == 'ListMetadataFormats': for key, value in self.metadata_formats.iteritems(): mdfnode = etree.SubElement( verbnode, util.nspath_eval('oai:metadataFormat', self.namespaces)) etree.SubElement( mdfnode, util.nspath_eval('oai:metadataPrefix', self.namespaces)).text = key etree.SubElement( mdfnode, util.nspath_eval('oai:schema', self.namespaces)).text = value['schema'] etree.SubElement( mdfnode, util.nspath_eval( 'oai:metadataNamespace', self.namespaces)).text = value['namespace'] elif verb in ['GetRecord', 'ListIdentifiers', 'ListRecords']: if verb == 'GetRecord': # GetRecordById records = response.getchildren() else: # GetRecords records = response.getchildren()[1].getchildren() for child in records: recnode = etree.SubElement( verbnode, util.nspath_eval('oai:record', self.namespaces)) header = etree.SubElement( recnode, util.nspath_eval('oai:header', self.namespaces)) self._transform_element(header, response, 'oai:identifier') self._transform_element(header, response, 'oai:dateStamp') self._transform_element(header, response, 'oai:setSpec') if verb in ['GetRecord', 'ListRecords']: metadata = etree.SubElement( recnode, util.nspath_eval('oai:metadata', self.namespaces)) if 'metadataprefix' in kvp and kvp[ 'metadataprefix'] == 'oai_dc': child.tag = util.nspath_eval('oai_dc:dc', self.namespaces) metadata.append(child) if verb != 'GetRecord': complete_list_size = response.xpath( '//@numberOfRecordsMatched')[0] next_record = response.xpath('//@nextRecord')[0] cursor = str(int(complete_list_size) - int(next_record) - 1) resumption_token = etree.SubElement( verbnode, util.nspath_eval('oai:resumptionToken', self.namespaces), completeListSize=complete_list_size, cursor=cursor).text = next_record return node
def _get_spatial_operator(geomattr, element, dbtype, nsmap): """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 = gml.Geometry(element, nsmap) 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': # adjust spatial query for PostGIS LOGGER.debug('Adjusting spatial query for PostgreSQL+PostGIS') 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) else: LOGGER.debug('Adjusting spatial query') spatial_query = "query_spatial('%s', %s,'%s','%s','%s')" % \ (os.path.abspath(os.path.join(os.path.dirname(__file__), '..')), geomattr, geometry.wkt, spatial_predicate, distance) return spatial_query