move to f-strings for string interpolation (#1064)

* update from format() to f-strings

* fix

* fix

* fix ref

* update headers
This commit is contained in:
Tom Kralidis
2022-12-08 16:09:19 -05:00
committed by GitHub
parent 64e63ac4c0
commit f9fb2c6005
54 changed files with 1053 additions and 1143 deletions
+1 -1
View File
@@ -3,7 +3,7 @@
"app_function": "pygeoapi.flask_app.APP",
"profile_name": null,
"project_name": "pygeoapi",
"runtime": "python3.6",
"runtime": "python3.8",
"s3_bucket": "zappa-pwpqh2twb",
"aws_region": "us-east-2",
"environment_variables": {
+139 -191
View File
@@ -86,7 +86,7 @@ LOGGER = logging.getLogger(__name__)
#: Return headers for requests (e.g:X-Powered-By)
HEADERS = {
'Content-Type': 'application/json',
'X-Powered-By': 'pygeoapi {}'.format(__version__)
'X-Powered-By': f'pygeoapi {__version__}'
}
CHARSET = ['utf-8']
@@ -198,7 +198,7 @@ def gzip(func):
content = compress(content.encode(charset))
except TypeError as err:
headers.pop('Content-Encoding')
LOGGER.error('Error in compression: {}'.format(err))
LOGGER.error(f'Error in compression: {err}')
return headers, status, content
@@ -648,7 +648,7 @@ class API:
'output_dir': None
}
LOGGER.debug('Loading process manager {}'.format(manager_def['name']))
LOGGER.debug(f"Loading process manager {manager_def['name']}")
self.manager = load_plugin('process_manager', manager_def)
LOGGER.info('Process manager plugin loaded')
@@ -685,35 +685,34 @@ class API:
'rel': request.get_linkrel(F_JSON),
'type': FORMAT_TYPES[F_JSON],
'title': 'This document as JSON',
'href': '{}?f={}'.format(self.config['server']['url'], F_JSON)
'href': f"{self.config['server']['url']}?f={F_JSON}"
}, {
'rel': request.get_linkrel(F_JSONLD),
'type': FORMAT_TYPES[F_JSONLD],
'title': 'This document as RDF (JSON-LD)',
'href': '{}?f={}'.format(self.config['server']['url'], F_JSONLD)
'href': f"{self.config['server']['url']}?f={F_JSONLD}"
}, {
'rel': request.get_linkrel(F_HTML),
'type': FORMAT_TYPES[F_HTML],
'title': 'This document as HTML',
'href': '{}?f={}'.format(self.config['server']['url'], F_HTML),
'href': f"{self.config['server']['url']}?f={F_HTML}",
'hreflang': self.default_locale
}, {
'rel': 'service-desc',
'type': 'application/vnd.oai.openapi+json;version=3.0',
'title': 'The OpenAPI definition as JSON',
'href': '{}/openapi'.format(self.config['server']['url'])
'href': f"{self.config['server']['url']}/openapi"
}, {
'rel': 'service-doc',
'type': FORMAT_TYPES[F_HTML],
'title': 'The OpenAPI definition as HTML',
'href': '{}/openapi?f={}'.format(self.config['server']['url'],
F_HTML),
'href': f"{self.config['server']['url']}/openapi?f={F_HTML}",
'hreflang': self.default_locale
}, {
'rel': 'conformance',
'type': FORMAT_TYPES[F_JSON],
'title': 'Conformance',
'href': '{}/conformance'.format(self.config['server']['url'])
'href': f"{self.config['server']['url']}/conformance"
}, {
'rel': 'data',
'type': FORMAT_TYPES[F_JSON],
@@ -723,12 +722,12 @@ class API:
'rel': 'http://www.opengis.net/def/rel/ogc/1.0/processes',
'type': FORMAT_TYPES[F_JSON],
'title': 'Processes',
'href': '{}/processes'.format(self.config['server']['url'])
'href': f"{self.config['server']['url']}/processes"
}, {
'rel': 'http://www.opengis.net/def/rel/ogc/1.0/job-list',
'type': FORMAT_TYPES[F_JSON],
'title': 'Jobs',
'href': '{}/jobs'.format(self.config['server']['url'])
'href': f"{self.config['server']['url']}/jobs"
}]
headers = request.get_response_headers()
@@ -871,7 +870,7 @@ class API:
LOGGER.debug('Creating collections')
for k, v in collections_dict.items():
if v.get('visibility', 'default') == 'hidden':
LOGGER.debug('Skipping hidden layer: {}'.format(k))
LOGGER.debug(f'Skipping hidden layer: {k}')
continue
collection_data = get_provider_default(v['providers'])
collection_data_type = collection_data['type']
@@ -932,34 +931,31 @@ class API:
'type': FORMAT_TYPES[F_JSON],
'rel': 'root',
'title': 'The landing page of this server as JSON',
'href': '{}?f={}'.format(self.config['server']['url'], F_JSON)
'href': f"{self.config['server']['url']}?f={F_JSON}"
})
collection['links'].append({
'type': FORMAT_TYPES[F_HTML],
'rel': 'root',
'title': 'The landing page of this server as HTML',
'href': '{}?f={}'.format(self.config['server']['url'], F_HTML)
'href': f"{self.config['server']['url']}?f={F_HTML}"
})
collection['links'].append({
'type': FORMAT_TYPES[F_JSON],
'rel': request.get_linkrel(F_JSON),
'title': 'This document as JSON',
'href': '{}/{}?f={}'.format(
self.get_collections_url(), k, F_JSON)
'href': f'{self.get_collections_url()}/{k}?f={F_JSON}'
})
collection['links'].append({
'type': FORMAT_TYPES[F_JSONLD],
'rel': request.get_linkrel(F_JSONLD),
'title': 'This document as RDF (JSON-LD)',
'href': '{}/{}?f={}'.format(
self.get_collections_url(), k, F_JSONLD)
'href': f'{self.get_collections_url()}/{k}?f={F_JSONLD}'
})
collection['links'].append({
'type': FORMAT_TYPES[F_HTML],
'rel': request.get_linkrel(F_HTML),
'title': 'This document as HTML',
'href': '{}/{}?f={}'.format(
self.get_collections_url(), k, F_HTML)
'href': f'{self.get_collections_url()}/{k}?f={F_HTML}'
})
if collection_data_type in ['feature', 'record', 'tile']:
@@ -970,36 +966,31 @@ class API:
'type': FORMAT_TYPES[F_JSON],
'rel': 'queryables',
'title': 'Queryables for this collection as JSON',
'href': '{}/{}/queryables?f={}'.format(
self.get_collections_url(), k, F_JSON)
'href': f'{self.get_collections_url()}/{k}/queryables?f={F_JSON}' # noqa
})
collection['links'].append({
'type': FORMAT_TYPES[F_HTML],
'rel': 'queryables',
'title': 'Queryables for this collection as HTML',
'href': '{}/{}queryables?f={}'.format(
self.get_collections_url(), k, F_HTML)
'href': f'{self.get_collections_url()}/{k}/queryables?f={F_HTML}' # noqa
})
collection['links'].append({
'type': 'application/geo+json',
'rel': 'items',
'title': 'items as GeoJSON',
'href': '{}/{}/items?f={}'.format(
self.get_collections_url(), k, F_JSON)
'href': f'{self.get_collections_url()}/{k}/items?f={F_JSON}' # noqa
})
collection['links'].append({
'type': FORMAT_TYPES[F_JSONLD],
'rel': 'items',
'title': 'items as RDF (GeoJSON-LD)',
'href': '{}/{}/items?f={}'.format(
self.get_collections_url(), k, F_JSONLD)
'href': f'{self.get_collections_url()}/{k}/items?f={F_JSONLD}' # noqa
})
collection['links'].append({
'type': FORMAT_TYPES[F_HTML],
'rel': 'items',
'title': 'Items as HTML',
'href': '{}/{}/items?f={}'.format(
self.get_collections_url(), k, F_HTML)
'href': f'{self.get_collections_url()}/{k}/items?f={F_HTML}' # noqa
})
elif collection_data_type == 'coverage':
@@ -1009,59 +1000,52 @@ class API:
'type': FORMAT_TYPES[F_JSON],
'rel': 'collection',
'title': 'Detailed Coverage metadata in JSON',
'href': '{}/{}?f={}'.format(
self.get_collections_url(), k, F_JSON)
'href': f'{self.get_collections_url()}/{k}?f={F_JSON}'
})
collection['links'].append({
'type': FORMAT_TYPES[F_HTML],
'rel': 'collection',
'title': 'Detailed Coverage metadata in HTML',
'href': '{}/{}?f={}'.format(
self.get_collections_url(), k, F_HTML)
'href': f'{self.get_collections_url()}/{k}?f={F_HTML}'
})
coverage_url = '{}/{}/coverage'.format(
self.get_collections_url(), k)
coverage_url = f'{self.get_collections_url()}/{k}/coverage'
collection['links'].append({
'type': FORMAT_TYPES[F_JSON],
'rel': '{}/coverage-domainset'.format(OGC_RELTYPES_BASE),
'rel': f'{OGC_RELTYPES_BASE}/coverage-domainset',
'title': 'Coverage domain set of collection in JSON',
'href': '{}/domainset?f={}'.format(coverage_url, F_JSON)
'href': f'{coverage_url}/domainset?f={F_JSON}'
})
collection['links'].append({
'type': FORMAT_TYPES[F_HTML],
'rel': '{}/coverage-domainset'.format(OGC_RELTYPES_BASE),
'rel': f'{OGC_RELTYPES_BASE}/coverage-domainset',
'title': 'Coverage domain set of collection in HTML',
'href': '{}/domainset?f={}'.format(coverage_url, F_HTML)
'href': f'{coverage_url}/domainset?f={F_HTML}'
})
collection['links'].append({
'type': FORMAT_TYPES[F_JSON],
'rel': '{}/coverage-rangetype'.format(OGC_RELTYPES_BASE),
'rel': f'{OGC_RELTYPES_BASE}/coverage-rangetype',
'title': 'Coverage range type of collection in JSON',
'href': '{}/rangetype?f={}'.format(coverage_url, F_JSON)
'href': f'{coverage_url}/rangetype?f={F_JSON}'
})
collection['links'].append({
'type': FORMAT_TYPES[F_HTML],
'rel': '{}/coverage-rangetype'.format(OGC_RELTYPES_BASE),
'rel': f'{OGC_RELTYPES_BASE}/coverage-rangetype',
'title': 'Coverage range type of collection in HTML',
'href': '{}/rangetype?f={}'.format(coverage_url, F_HTML)
'href': f'{coverage_url}/rangetype?f={F_HTML}'
})
collection['links'].append({
'type': 'application/prs.coverage+json',
'rel': '{}/coverage'.format(OGC_RELTYPES_BASE),
'rel': f'{OGC_RELTYPES_BASE}/coverage',
'title': 'Coverage data',
'href': '{}/{}/coverage?f={}'.format(
self.get_collections_url(), k, F_JSON)
'href': f'{self.get_collections_url()}/{k}/coverage?f={F_JSON}' # noqa
})
if collection_data_format is not None:
collection['links'].append({
'type': collection_data_format['mimetype'],
'rel': '{}/coverage'.format(OGC_RELTYPES_BASE),
'title': 'Coverage data as {}'.format(
collection_data_format['name']),
'href': '{}/{}/coverage?f={}'.format(
self.get_collections_url(), k,
collection_data_format['name'])
'rel': f'{OGC_RELTYPES_BASE}/coverage',
'title': f"Coverage data as {collection_data_format['name']}", # noqa
'href': f"{self.get_collections_url()}/{k}/coverage?f={collection_data_format['name']}" # noqa
})
if dataset is not None:
LOGGER.debug('Creating extended coverage metadata')
@@ -1093,15 +1077,13 @@ class API:
'type': FORMAT_TYPES[F_JSON],
'rel': 'tiles',
'title': 'Tiles as JSON',
'href': '{}/{}/tiles?f={}'.format(
self.get_collections_url(), k, F_JSON)
'href': f'{self.get_collections_url()}/{k}/tiles?f={F_JSON}' # noqa
})
collection['links'].append({
'type': FORMAT_TYPES[F_HTML],
'rel': 'tiles',
'title': 'Tiles as HTML',
'href': '{}/{}/tiles?f={}'.format(
self.get_collections_url(), k, F_HTML)
'href': f'{self.get_collections_url()}/{k}/tiles?f={F_HTML}' # noqa
})
try:
@@ -1118,9 +1100,8 @@ class API:
collection['links'].append({
'type': map_mimetype,
'rel': 'http://www.opengis.net/def/rel/ogc/1.0/map',
'title': 'Map as {}'.format(map_format),
'href': '{}/collections/{}/map?f={}'.format(
self.config['server']['url'], k, map_format)
'title': f'Map as {map_format}',
'href': f"{self.config['server']['url']}/collections/{k}/map?f={map_format}" # noqa
})
try:
@@ -1144,16 +1125,14 @@ class API:
collection['links'].append({
'type': 'application/json',
'rel': 'data',
'title': '{} query for this collection as JSON'.format(qt), # noqa
'href': '{}/{}/{}?f={}'.format(
self.get_collections_url(), k, qt, F_JSON)
'title': f'{qt} query for this collection as JSON',
'href': f'{self.get_collections_url()}/{k}/{qt}?f={F_JSON}' # noqa
})
collection['links'].append({
'type': FORMAT_TYPES[F_HTML],
'rel': 'data',
'title': '{} query for this collection as HTML'.format(qt), # noqa
'href': '{}/{}/{}?f={}'.format(
self.get_collections_url(), k, qt, F_HTML)
'title': f'{qt} query for this collection as HTML',
'href': f'{self.get_collections_url()}/{k}/{qt}?f={F_HTML}' # noqa
})
except ProviderConnectionError:
msg = 'connection error (check logs)'
@@ -1174,19 +1153,19 @@ class API:
'type': FORMAT_TYPES[F_JSON],
'rel': request.get_linkrel(F_JSON),
'title': 'This document as JSON',
'href': '{}?f={}'.format(self.get_collections_url(), F_JSON)
'href': f'{self.get_collections_url()}?f={F_JSON}'
})
fcm['links'].append({
'type': FORMAT_TYPES[F_JSONLD],
'rel': request.get_linkrel(F_JSONLD),
'title': 'This document as RDF (JSON-LD)',
'href': '{}?f={}'.format(self.get_collections_url(), F_JSONLD)
'href': f'{self.get_collections_url()}?f={F_JSONLD}'
})
fcm['links'].append({
'type': FORMAT_TYPES[F_HTML],
'rel': request.get_linkrel(F_HTML),
'title': 'This document as HTML',
'href': '{}?f={}'.format(self.get_collections_url(), F_HTML)
'href': f'{self.get_collections_url()}?f={F_HTML}'
})
if request.format == F_HTML: # render
@@ -1265,8 +1244,7 @@ class API:
self.config['resources'][dataset]['title'], request.locale),
'properties': {},
'$schema': 'http://json-schema.org/draft/2019-09/schema',
'$id': '{}/{}/queryables'.format(
self.get_collections_url(), dataset)
'$id': f'{self.get_collections_url()}/{dataset}/queryables'
}
if p.fields:
@@ -1431,7 +1409,7 @@ class API:
LOGGER.debug('processing property parameters')
for k, v in request.params.items():
if k not in reserved_fieldnames and k in list(p.fields.keys()):
LOGGER.debug('Adding property filter {}={}'.format(k, v))
LOGGER.debug(f'Adding property filter {k}={v}')
properties.append((k, v))
LOGGER.debug('processing sort parameter')
@@ -1504,19 +1482,19 @@ class API:
prv_locale = l10n.get_plugin_locale(provider_def, request.raw_locale)
LOGGER.debug('Querying provider')
LOGGER.debug('offset: {}'.format(offset))
LOGGER.debug('limit: {}'.format(limit))
LOGGER.debug('resulttype: {}'.format(resulttype))
LOGGER.debug('sortby: {}'.format(sortby))
LOGGER.debug('bbox: {}'.format(bbox))
LOGGER.debug('datetime: {}'.format(datetime_))
LOGGER.debug('properties: {}'.format(properties))
LOGGER.debug('select properties: {}'.format(select_properties))
LOGGER.debug('skipGeometry: {}'.format(skip_geometry))
LOGGER.debug('language: {}'.format(prv_locale))
LOGGER.debug('q: {}'.format(q))
LOGGER.debug('cql_text: {}'.format(cql_text))
LOGGER.debug('filter-lang: {}'.format(filter_lang))
LOGGER.debug(f'offset: {offset}')
LOGGER.debug(f'limit: {limit}')
LOGGER.debug(f'resulttype: {resulttype}')
LOGGER.debug(f'sortby: {sortby}')
LOGGER.debug(f'bbox: {bbox}')
LOGGER.debug(f'datetime: {datetime_}')
LOGGER.debug(f'properties: {properties}')
LOGGER.debug(f'select properties: {select_properties}')
LOGGER.debug(f'skipGeometry: {skip_geometry}')
LOGGER.debug(f'language: {prv_locale}')
LOGGER.debug(f'q: {q}')
LOGGER.debug(f'cql_text: {cql_text}')
LOGGER.debug(f'filter-lang: {filter_lang}')
try:
content = p.query(offset=offset, limit=limit,
@@ -1551,25 +1529,22 @@ class API:
serialized_query_params += urllib.parse.quote(str(v), safe=',')
# TODO: translate titles
uri = '{}/{}/items'.format(self.get_collections_url(), dataset)
uri = f'{self.get_collections_url()}/{dataset}/items'
content['links'] = [{
'type': 'application/geo+json',
'rel': request.get_linkrel(F_JSON),
'title': 'This document as GeoJSON',
'href': '{}?f={}{}'.format(
uri, F_JSON, serialized_query_params)
'href': f'{uri}?f={F_JSON}{serialized_query_params}'
}, {
'rel': request.get_linkrel(F_JSONLD),
'type': FORMAT_TYPES[F_JSONLD],
'title': 'This document as RDF (JSON-LD)',
'href': '{}?f={}{}'.format(
uri, F_JSONLD, serialized_query_params)
'href': f'{uri}?f={F_JSONLD}{serialized_query_params}'
}, {
'type': FORMAT_TYPES[F_HTML],
'rel': request.get_linkrel(F_HTML),
'title': 'This document as HTML',
'href': '{}?f={}{}'.format(
uri, F_HTML, serialized_query_params)
'href': f'{uri}?f={F_HTML}{serialized_query_params}'
}]
if offset > 0:
@@ -1579,9 +1554,7 @@ class API:
'type': 'application/geo+json',
'rel': 'prev',
'title': 'items (prev)',
'href': '{}?offset={}{}'
.format(
uri, prev, serialized_query_params)
'href': f'{uri}?offset={prev}{serialized_query_params}'
})
if len(content['features']) == limit:
@@ -1591,9 +1564,7 @@ class API:
'type': 'application/geo+json',
'rel': 'next',
'title': 'items (next)',
'href': '{}?offset={}{}'
.format(
uri, next_, serialized_query_params)
'href': f'{uri}?offset={next_}{serialized_query_params}'
})
content['links'].append(
@@ -1653,15 +1624,14 @@ class API:
return self.get_exception(
500, headers, request.format, 'NoApplicableCode', msg)
headers['Content-Type'] = '{}; charset={}'.format(
formatter.mimetype, self.config['server']['encoding'])
headers['Content-Type'] = f"{formatter.mimetype}; charset={self.config['server']['encoding']}" # noqa
if p.filename is None:
filename = '{}.csv'.format(dataset)
filename = f'{dataset}.csv'
else:
filename = '{}'.format(p.filename)
filename = f'{p.filename}'
cd = 'attachment; filename="{}"'.format(filename)
cd = f'attachment; filename="{filename}"'
headers['Content-Disposition'] = cd
return headers, 200, content
@@ -1802,11 +1772,11 @@ class API:
LOGGER.debug('processing property parameters')
for k, v in request.params.items():
if k not in reserved_fieldnames and k not in p.fields.keys():
msg = 'unknown query parameter: {}'.format(k)
msg = f'unknown query parameter: {k}'
return self.get_exception(
400, headers, request.format, 'InvalidParameterValue', msg)
elif k not in reserved_fieldnames and k in p.fields.keys():
LOGGER.debug('Add property filter {}={}'.format(k, v))
LOGGER.debug(f'Add property filter {k}={v}')
properties.append((k, v))
LOGGER.debug('processing sort parameter')
@@ -1862,16 +1832,16 @@ class API:
400, headers, request.format, 'InvalidParameterValue', msg)
LOGGER.debug('Querying provider')
LOGGER.debug('offset: {}'.format(offset))
LOGGER.debug('limit: {}'.format(limit))
LOGGER.debug('resulttype: {}'.format(resulttype))
LOGGER.debug('sortby: {}'.format(sortby))
LOGGER.debug('bbox: {}'.format(bbox))
LOGGER.debug('datetime: {}'.format(datetime_))
LOGGER.debug('properties: {}'.format(select_properties))
LOGGER.debug('skipGeometry: {}'.format(skip_geometry))
LOGGER.debug('q: {}'.format(q))
LOGGER.debug('filter-lang: {}'.format(filter_lang))
LOGGER.debug(f'offset: {offset}')
LOGGER.debug(f'limit: {limit}')
LOGGER.debug(f'resulttype: {resulttype}')
LOGGER.debug(f'sortby: {sortby}')
LOGGER.debug(f'bbox: {bbox}')
LOGGER.debug(f'datetime: {datetime_}')
LOGGER.debug(f'properties: {select_properties}')
LOGGER.debug(f'skipGeometry: {skip_geometry}')
LOGGER.debug(f'q: {q}')
LOGGER.debug(f'filter-lang: {filter_lang}')
LOGGER.debug('Processing headers')
@@ -2017,8 +1987,7 @@ class API:
return self.get_exception(
400, headers, request.format, 'InvalidParameterValue', msg)
headers['Location'] = '{}/{}/items/{}'.format(
self.get_collections_url(), dataset, identifier)
headers['Location'] = f'{self.get_collections_url()}/{dataset}/items/{identifier}' # noqa
return headers, 201, ''
@@ -2095,7 +2064,7 @@ class API:
prv_locale = l10n.get_plugin_locale(provider_def, request.raw_locale)
try:
LOGGER.debug('Fetching id {}'.format(identifier))
LOGGER.debug(f'Fetching id {identifier}')
content = p.get(identifier, language=prv_locale)
except ProviderConnectionError as err:
LOGGER.error(err)
@@ -2123,8 +2092,7 @@ class API:
'NotFound', msg)
uri = content['properties'].get(p.uri_field) if p.uri_field else \
'{}/{}/items/{}'.format(
self.get_collections_url(), dataset, identifier)
f'{self.get_collections_url()}/{dataset}/items/{identifier}'
if 'links' not in content:
content['links'] = []
@@ -2133,51 +2101,46 @@ class API:
'type': FORMAT_TYPES[F_JSON],
'rel': 'root',
'title': 'The landing page of this server as JSON',
'href': '{}?f={}'.format(self.config['server']['url'], F_JSON)
'href': f"{self.config['server']['url']}?f={F_JSON}"
}, {
'type': FORMAT_TYPES[F_HTML],
'rel': 'root',
'title': 'The landing page of this server as HTML',
'href': '{}?f={}'.format(self.config['server']['url'], F_HTML)
'href': f"{self.config['server']['url']}?f={F_HTML}"
}, {
'rel': request.get_linkrel(F_JSON),
'type': 'application/geo+json',
'title': 'This document as GeoJSON',
'href': '{}?f={}'.format(uri, F_JSON)
'href': f'{uri}?f={F_JSON}'
}, {
'rel': request.get_linkrel(F_JSONLD),
'type': FORMAT_TYPES[F_JSONLD],
'title': 'This document as RDF (JSON-LD)',
'href': '{}?f={}'.format(uri, F_JSONLD)
'href': f'{uri}?f={F_JSONLD}'
}, {
'rel': request.get_linkrel(F_HTML),
'type': FORMAT_TYPES[F_HTML],
'title': 'This document as HTML',
'href': '{}?f={}'.format(uri, F_HTML)
'href': f'{uri}?f={F_HTML}'
}, {
'rel': 'collection',
'type': FORMAT_TYPES[F_JSON],
'title': l10n.translate(collections[dataset]['title'],
request.locale),
'href': '{}/{}'.format(
self.get_collections_url(), dataset)
'href': f'{self.get_collections_url()}/{dataset}'
}])
if 'prev' in content:
content['links'].append({
'rel': 'prev',
'type': FORMAT_TYPES[request.format],
'href': '{}/{}/items/{}?f={}'.format(
self.get_collections_url(), dataset,
content['prev'], request.format)
'href': f"{self.get_collections_url()}/{dataset}/items/{content['prev']}?f={request.format}" # noqa
})
if 'next' in content:
content['links'].append({
'rel': 'next',
'type': FORMAT_TYPES[request.format],
'href': '{}/{}/items/{}?f={}'.format(
self.get_collections_url(), dataset,
content['next'], request.format)
'href': f"{self.get_collections_url()}/{dataset}/items/{content['next']}?f={request.format}" # noqa
})
# Set response language to requested provider locale
@@ -2271,7 +2234,7 @@ class API:
LOGGER.debug('Processing datetime parameter')
datetime_ = request.params.get('datetime', None)
datetime_ = request.params.get('datetime')
try:
datetime_ = validate_datetime(
@@ -2292,7 +2255,7 @@ class API:
LOGGER.debug('Processing properties parameter')
query_args['properties'] = [rs for
rs in properties.split(',') if rs]
LOGGER.debug('Fields: {}'.format(query_args['properties']))
LOGGER.debug(f"Fields: {query_args['properties']}")
for a in query_args['properties']:
if a not in p.fields:
@@ -2305,7 +2268,7 @@ class API:
try:
subsets = validate_subset(request.params['subset'] or '')
except (AttributeError, ValueError) as err:
msg = 'Invalid subset: {}'.format(err)
msg = f'Invalid subset: {err}'
LOGGER.error(msg)
return self.get_exception(
400, headers, format_, 'InvalidParameterValue', msg)
@@ -2317,13 +2280,13 @@ class API:
400, headers, format_, 'InvalidParameterValue', msg)
query_args['subsets'] = subsets
LOGGER.debug('Subsets: {}'.format(query_args['subsets']))
LOGGER.debug(f"Subsets: {query_args['subsets']}")
LOGGER.debug('Querying coverage')
try:
data = p.query(**query_args)
except ProviderInvalidQueryError as err:
msg = 'query error: {}'.format(err)
msg = f'query error: {err}'
return self.get_exception(
400, headers, format_, 'InvalidParameterValue', msg)
except ProviderNoDataError:
@@ -2338,7 +2301,7 @@ class API:
mt = collection_def['format']['name']
if format_ == mt: # native format
if p.filename is not None:
cd = 'attachment; filename="{}"'.format(p.filename)
cd = f'attachment; filename="{p.filename}"'
headers['Content-Disposition'] = cd
headers['Content-Type'] = collection_def['format']['mimetype']
@@ -2511,29 +2474,25 @@ class API:
'type': FORMAT_TYPES[F_JSON],
'rel': request.get_linkrel(F_JSON),
'title': 'This document as JSON',
'href': '{}/{}/tiles?f={}'.format(
self.get_collections_url(), dataset, F_JSON)
'href': f'{self.get_collections_url()}/{dataset}/tiles?f={F_JSON}'
})
tiles['links'].append({
'type': FORMAT_TYPES[F_JSONLD],
'rel': request.get_linkrel(F_JSONLD),
'title': 'This document as RDF (JSON-LD)',
'href': '{}/{}/tiles?f={}'.format(
self.get_collections_url(), dataset, F_JSONLD)
'href': f'{self.get_collections_url()}/{dataset}/tiles?f={F_JSONLD}' # noqa
})
tiles['links'].append({
'type': FORMAT_TYPES[F_HTML],
'rel': request.get_linkrel(F_HTML),
'title': 'This document as HTML',
'href': '{}/{}/tiles?f={}'.format(
self.get_collections_url(), dataset, F_HTML)
'href': f'{self.get_collections_url()}/{dataset}/tiles?f={F_HTML}'
})
tile_services = p.get_tiles_service(
baseurl=self.config['server']['url'],
servicepath='{}/{}/tiles/{{{}}}/{{{}}}/{{{}}}/{{{}}}?f=mvt'
.format(self.get_collections_url(), dataset, 'tileMatrixSetId',
'tileMatrix', 'tileRow', 'tileCol'))
servicepath='{self.get_collections_url()}/{dataset}/tiles/{{tileMatrixSetId}}/{{tileMatrix}}/{{tileRow}}/{{tileCol}}?f=mvt' # noqa
)
for service in tile_services['links']:
tiles['links'].append(service)
@@ -2551,20 +2510,14 @@ class API:
tile_matrix['links'].append({
'type': FORMAT_TYPES[F_JSON],
'rel': request.get_linkrel(F_JSON),
'title': '{} - {} - {}'.format(
dataset, matrix.tileMatrixSet, F_JSON),
'href': '{}/{}/tiles/{}?f={}'.format(
self.get_collections_url(), dataset,
matrix.tileMatrixSet, F_JSON)
'title': f'{dataset} - {matrix.tileMatrixSet} - {F_JSON}',
'href': f'{self.get_collections_url()}/{dataset}/tiles/{matrix.tileMatrixSet}?f={F_JSON}' # noqa
})
tile_matrix['links'].append({
'type': FORMAT_TYPES[F_HTML],
'rel': request.get_linkrel(F_HTML),
'title': '{} - {} - {}'.format(
dataset, matrix.tileMatrixSet, F_HTML),
'href': '{}/{}/tiles/{}?f={}'.format(
self.get_collections_url(), dataset,
matrix.tileMatrixSet, F_HTML)
'title': f'{dataset} - {matrix.tileMatrixSet} - {F_HTML}',
'href': f'{self.get_collections_url()}/{dataset}/tiles/{matrix.tileMatrixSet}?f={F_HTML}' # noqa
})
tiles['tilesets'].append(tile_matrix)
@@ -2635,8 +2588,7 @@ class API:
format_ = p.format_type
headers['Content-Type'] = format_
LOGGER.debug('Fetching tileset id {} and tile {}/{}/{}'.format(
matrix_id, z_idx, y_idx, x_idx))
LOGGER.debug(f'Fetching tileset id {matrix_id} and tile {z_idx}/{y_idx}/{x_idx}') # noqa
content = p.get_tiles(layer=p.get_layer(), tileset=matrix_id,
z=z_idx, y=y_idx, x=x_idx, format_=format_)
if content is None:
@@ -2886,7 +2838,7 @@ class API:
except ProviderInvalidQueryError as err:
exception = {
'code': 'NoApplicableCode',
'description': 'query error: {}'.format(err),
'description': f'query error: {err}'
}
LOGGER.error(exception)
headers['Content-type'] = 'application/json'
@@ -2977,7 +2929,7 @@ class API:
except ProviderInvalidQueryError as err:
exception = {
'code': 'NoApplicableCode',
'description': 'query error: {}'.format(err),
'description': f'query error: {err}'
}
LOGGER.error(exception)
return headers, 400, to_json(exception, self.pretty_print)
@@ -3064,15 +3016,14 @@ class API:
p2['outputTransmission'] = ['value']
p2['links'] = p2.get('links', [])
jobs_url = '{}/jobs'.format(self.config['server']['url'])
process_url = '{}/processes/{}'.format(
self.config['server']['url'], key)
jobs_url = f"{self.config['server']['url']}/jobs"
process_url = f"{self.config['server']['url']}/processes/{key}"
# TODO translation support
link = {
'type': FORMAT_TYPES[F_JSON],
'rel': request.get_linkrel(F_JSON),
'href': '{}?f={}'.format(process_url, F_JSON),
'href': f'{process_url}?f={F_JSON}',
'title': 'Process description as JSON',
'hreflang': self.default_locale
}
@@ -3081,7 +3032,7 @@ class API:
link = {
'type': FORMAT_TYPES[F_HTML],
'rel': request.get_linkrel(F_HTML),
'href': '{}?f={}'.format(process_url, F_HTML),
'href': f'{process_url}?f={F_HTML}',
'title': 'Process description as HTML',
'hreflang': self.default_locale
}
@@ -3090,7 +3041,7 @@ class API:
link = {
'type': FORMAT_TYPES[F_HTML],
'rel': 'http://www.opengis.net/def/rel/ogc/1.0/job-list',
'href': '{}?f={}'.format(jobs_url, F_HTML),
'href': f'{jobs_url}?f={F_HTML}',
'title': 'jobs for this process as HTML',
'hreflang': self.default_locale
}
@@ -3099,7 +3050,7 @@ class API:
link = {
'type': FORMAT_TYPES[F_JSON],
'rel': 'http://www.opengis.net/def/rel/ogc/1.0/job-list',
'href': '{}?f={}'.format(jobs_url, F_JSON),
'href': f'{jobs_url}?f={F_JSON}',
'title': 'jobs for this process as JSON',
'hreflang': self.default_locale
}
@@ -3108,7 +3059,7 @@ class API:
link = {
'type': FORMAT_TYPES[F_JSON],
'rel': 'http://www.opengis.net/def/rel/ogc/1.0/execute',
'href': '{}/execution?f={}'.format(process_url, F_JSON),
'href': f'{process_url}/execution?f={F_JSON}',
'title': 'Execution for this process as JSON',
'hreflang': self.default_locale
}
@@ -3168,12 +3119,12 @@ class API:
serialized_jobs = {
'jobs': [],
'links': [{
'href': '{}/jobs?f={}'.format(self.config['server']['url'], F_HTML), # noqa
'href': f"{self.config['server']['url']}/jobs?f={F_HTML}",
'rel': request.get_linkrel(F_HTML),
'type': FORMAT_TYPES[F_HTML],
'title': 'Jobs list as HTML'
}, {
'href': '{}/jobs?f={}'.format(self.config['server']['url'], F_JSON), # noqa
'href': f"{self.config['server']['url']}/jobs?f={F_JSON}",
'rel': request.get_linkrel(F_JSON),
'type': FORMAT_TYPES[F_JSON],
'title': 'Jobs list as JSON'
@@ -3195,19 +3146,18 @@ class API:
if JobStatus[job_['status']] in (
JobStatus.successful, JobStatus.running, JobStatus.accepted):
job_result_url = '{}/jobs/{}/results'.format(
self.config['server']['url'], job_['identifier'])
job_result_url = f"{ self.config['server']['url']}/jobs/{job_['identifier']}/results" # noqa
job2['links'] = [{
'href': '{}?f={}'.format(job_result_url, F_HTML),
'href': f'{job_result_url}?f={F_HTML}',
'rel': 'about',
'type': FORMAT_TYPES[F_HTML],
'title': 'results of job {} as HTML'.format(job_id)
'title': f'results of job {job_id} as HTML'
}, {
'href': '{}?f={}'.format(job_result_url, F_JSON),
'href': f'{job_result_url}?f={F_JSON}',
'rel': 'about',
'type': FORMAT_TYPES[F_JSON],
'title': 'results of job {} as JSON'.format(job_id)
'title': f'results of job {job_id} as JSON'
}]
if job_['mimetype'] not in (FORMAT_TYPES[F_JSON],
@@ -3216,8 +3166,7 @@ class API:
'href': job_result_url,
'rel': 'about',
'type': job_['mimetype'],
'title': 'results of job {} as {}'.format(
job_id, job_['mimetype'])
'title': f"results of job {job_id} as {job_['mimetype']}" # noqa
})
serialized_jobs['jobs'].append(job2)
@@ -3302,8 +3251,7 @@ class API:
LOGGER.debug(data_dict)
job_id = data.get("job_id", str(uuid.uuid1()))
url = '{}/jobs/{}'.format(
self.config['server']['url'], job_id)
url = f"{self.config['server']['url']}/jobs/{job_id}"
headers['Location'] = url
@@ -3430,7 +3378,7 @@ class API:
}
else:
http_status = 200
jobs_url = '{}/jobs'.format(self.config['server']['url'])
jobs_url = f"{self.config['server']['url']}/jobs"
response = {
'jobID': job_id,
@@ -3495,7 +3443,7 @@ class API:
parameternames = parameternames.split(',')
LOGGER.debug('Processing coords parameter')
wkt = request.params.get('coords', None)
wkt = request.params.get('coords')
if not wkt:
msg = 'missing coords parameter'
@@ -3615,12 +3563,12 @@ class API:
for key, value in stac_collections.items():
content['links'].append({
'rel': 'child',
'href': '{}/{}?f={}'.format(stac_url, key, F_JSON),
'href': f'{stac_url}/{key}?f={F_JSON}',
'type': FORMAT_TYPES[F_JSON]
})
content['links'].append({
'rel': 'child',
'href': '{}/{}'.format(stac_url, key),
'href': f'{stac_url}/{key}',
'type': FORMAT_TYPES[F_HTML]
})
@@ -3649,7 +3597,7 @@ class API:
headers = request.get_response_headers()
dataset = None
LOGGER.debug('Path: {}'.format(path))
LOGGER.debug(f'Path: {path}')
dir_tokens = path.split('/')
if dir_tokens:
dataset = dir_tokens[0]
@@ -3672,7 +3620,7 @@ class API:
return self.get_exception(
500, headers, request.format, 'NoApplicableCode', msg)
id_ = '{}-stac'.format(dataset)
id_ = f'{dataset}-stac'
stac_version = '1.0.0-rc.2'
content = {
@@ -3768,7 +3716,7 @@ class API:
400, headers, request.format, 'InvalidParameterValue', msg)
def get_collections_url(self):
return '{}/collections'.format(self.config['server']['url'])
return f"{self.config['server']['url']}/collections"
def validate_bbox(value=None) -> list:
@@ -3909,7 +3857,7 @@ def validate_subset(value: str) -> dict:
subsets = {}
for s in value.split(','):
LOGGER.debug('Processing subset {}'.format(s))
LOGGER.debug(f'Processing subset {s}')
m = re.search(r'(.*)\((.*)\)', s)
subset_name, values = m.group(1, 2)
+8 -8
View File
@@ -2,7 +2,7 @@
#
# Authors: Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2021 Tom Kralidis
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -31,15 +31,16 @@ import click
import json
from jsonschema import validate as jsonschema_validate
import logging
import os
from pathlib import Path
from pygeoapi.util import to_json, yaml_load
LOGGER = logging.getLogger(__name__)
THISDIR = os.path.dirname(os.path.realpath(__file__))
THISDIR = Path(__file__).parent.resolve()
def validate_config(instance_dict):
def validate_config(instance_dict: dict) -> bool:
"""
Validate pygeoapi configuration against pygeoapi schema
@@ -48,10 +49,9 @@ def validate_config(instance_dict):
:returns: `bool` of validation
"""
schema_file = os.path.join(THISDIR, 'schemas', 'config',
'pygeoapi-config-0.x.yml')
schema_file = THISDIR / 'schemas' / 'config' / 'pygeoapi-config-0.x.yml'
with open(schema_file) as fh2:
with schema_file.open() as fh2:
schema_dict = yaml_load(fh2)
jsonschema_validate(json.loads(to_json(instance_dict)), schema_dict)
@@ -74,7 +74,7 @@ def validate(ctx, config_file):
raise click.ClickException('--config/-c required')
with open(config_file) as ff:
click.echo('Validating {}'.format(config_file))
click.echo(f'Validating {config_file}')
instance = yaml_load(ff)
validate_config(instance)
click.echo('Valid configuration')
+1 -1
View File
@@ -67,7 +67,7 @@ APP.config['JSONIFY_PRETTYPRINT_REGULAR'] = CONFIG['server'].get(
api_ = API(CONFIG)
OGC_SCHEMAS_LOCATION = CONFIG['server'].get('ogc_schemas_location', None)
OGC_SCHEMAS_LOCATION = CONFIG['server'].get('ogc_schemas_location')
if (OGC_SCHEMAS_LOCATION is not None and
not OGC_SCHEMAS_LOCATION.startswith('http')):
+4 -4
View File
@@ -2,7 +2,7 @@
#
# Authors: Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2019 Tom Kralidis
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -35,7 +35,7 @@ LOGGER = logging.getLogger(__name__)
class BaseFormatter:
"""generic Formatter ABC"""
def __init__(self, formatter_def):
def __init__(self, formatter_def: dict):
"""
Initialize object
@@ -51,7 +51,7 @@ class BaseFormatter:
if 'geom' in formatter_def:
self.geom = formatter_def['geom']
def write(self, options={}, data=None):
def write(self, options: dict = {}, data: dict = None) -> str:
"""
Generate data in specified format
@@ -64,7 +64,7 @@ class BaseFormatter:
raise NotImplementedError()
def __repr__(self):
return '<BaseFormatter> {}'.format(self.name)
return f'<BaseFormatter> {self.name}'
class FormatterGenericError(Exception):
+5 -5
View File
@@ -2,7 +2,7 @@
#
# Authors: Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2018 Tom Kralidis
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -40,7 +40,7 @@ LOGGER = logging.getLogger(__name__)
class CSVFormatter(BaseFormatter):
"""CSV formatter"""
def __init__(self, formatter_def):
def __init__(self, formatter_def: dict):
"""
Initialize object
@@ -56,7 +56,7 @@ class CSVFormatter(BaseFormatter):
super().__init__({'name': 'csv', 'geom': geom})
self.mimetype = 'text/csv'
def write(self, options={}, data=None):
def write(self, options: dict = {}, data: dict = None) -> str:
"""
Generate data in CSV format
@@ -83,7 +83,7 @@ class CSVFormatter(BaseFormatter):
# TODO: implement wkt geometry serialization
LOGGER.debug('not a point geometry, skipping')
LOGGER.debug('CSV fields: {}'.format(fields))
LOGGER.debug(f'CSV fields: {fields}')
try:
output = io.BytesIO()
@@ -104,4 +104,4 @@ class CSVFormatter(BaseFormatter):
return output.getvalue()
def __repr__(self):
return '<CSVFormatter> {}'.format(self.mimetype)
return f'<CSVFormatter> {self.name}'
+74 -76
View File
@@ -2,7 +2,7 @@
#
# Authors: Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2019 Tom Kralidis
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -26,11 +26,13 @@
# OTHER DEALINGS IN THE SOFTWARE.
#
# =================================================================
""" Linked data capabilities
Returns content as linked data representations
"""
import logging
from typing import Callable
from pygeoapi.util import is_url
from pygeoapi import l10n
@@ -40,23 +42,23 @@ from shapely.ops import unary_union
LOGGER = logging.getLogger(__name__)
def jsonldify(func):
def jsonldify(func: Callable) -> Callable:
"""
Decorator that transforms app configuration\
to include a JSON-LD representation
Decorator that transforms app configuration\
to include a JSON-LD representation
:param func: decorated function
:param func: decorated function
:returns: `func`
:returns: `func`
"""
def inner(*args, **kwargs):
apireq = args[1]
format_ = getattr(apireq, 'format', None)
format_ = getattr(apireq, 'format')
if not format_ == 'jsonld':
return func(*args, **kwargs)
# Function args have been pre-processed, so get locale from APIRequest
locale_ = getattr(apireq, 'locale', None)
locale_ = getattr(apireq, 'locale')
LOGGER.debug('Creating JSON-LD representation')
cls = args[0]
cfg = cls.config
@@ -67,43 +69,43 @@ def jsonldify(func):
fcmld = {
"@context": "https://schema.org/docs/jsonldcontext.jsonld",
"@type": "DataCatalog",
"@id": cfg.get('server', {}).get('url', None),
"url": cfg.get('server', {}).get('url', None),
"name": l10n.translate(ident.get('title', None), locale_),
"@id": cfg.get('server', {}).get('url'),
"url": cfg.get('server', {}).get('url'),
"name": l10n.translate(ident.get('title'), locale_),
"description": l10n.translate(
ident.get('description', None), locale_),
ident.get('description'), locale_),
"keywords": l10n.translate(
ident.get('keywords', None), locale_),
ident.get('keywords'), locale_),
"termsOfService": l10n.translate(
ident.get('terms_of_service', None), locale_),
"license": meta.get('license', {}).get('url', None),
ident.get('terms_of_service'), locale_),
"license": meta.get('license', {}).get('url'),
"provider": {
"@type": "Organization",
"name": l10n.translate(provider.get('name', None), locale_),
"url": provider.get('url', None),
"name": l10n.translate(provider.get('name'), locale_),
"url": provider.get('url'),
"address": {
"@type": "PostalAddress",
"streetAddress": contact.get('address', None),
"postalCode": contact.get('postalcode', None),
"addressLocality": contact.get('city', None),
"addressRegion": contact.get('stateorprovince', None),
"addressCountry": contact.get('country', None)
"streetAddress": contact.get('address'),
"postalCode": contact.get('postalcode'),
"addressLocality": contact.get('city'),
"addressRegion": contact.get('stateorprovince'),
"addressCountry": contact.get('country')
},
"contactPoint": {
"@type": "Contactpoint",
"email": contact.get('email', None),
"telephone": contact.get('phone', None),
"faxNumber": contact.get('fax', None),
"url": contact.get('url', None),
"email": contact.get('email'),
"telephone": contact.get('phone'),
"faxNumber": contact.get('fax'),
"url": contact.get('url'),
"hoursAvailable": {
"opens": contact.get('hours', None),
"opens": contact.get('hours'),
"description": l10n.translate(
contact.get('instructions', None), locale_)
contact.get('instructions'), locale_)
},
"contactType": l10n.translate(
contact.get('role', None), locale_),
contact.get('role'), locale_),
"description": l10n.translate(
contact.get('position', None), locale_)
contact.get('position'), locale_)
}
}
}
@@ -112,46 +114,43 @@ def jsonldify(func):
return inner
def jsonldify_collection(cls, collection, locale_):
def jsonldify_collection(cls, collection: dict, locale_: str) -> dict:
"""
Transforms collection into a JSON-LD representation
Transforms collection into a JSON-LD representation
:param cls: API object
:param collection: `collection` as prepared for non-LD JSON
representation
:param locale_: The locale to use for translations (if supported)
:param cls: API object
:param collection: `collection` as prepared for non-LD JSON
representation
:param locale_: The locale to use for translations (if supported)
:returns: `collection` a dictionary, mapped into JSON-LD, of
type schema:Dataset
:returns: `collection` a dictionary, mapped into JSON-LD, of
type schema:Dataset
"""
temporal_extent = collection.get('extent', {}).get('temporal', {})
interval = temporal_extent.get('interval', [[None, None]])
interval = temporal_extent.get('interval')
if interval is not None:
interval = f'{interval[0][0]}/{interval[0][1]}'
spatial_extent = collection.get('extent', {}).get('spatial', {})
bbox = spatial_extent.get('bbox', None)
crs = spatial_extent.get('crs', None)
bbox = spatial_extent.get('bbox')
crs = spatial_extent.get('crs')
hascrs84 = crs.endswith('CRS84')
dataset = {
"@type": "Dataset",
"@id": "{}/collections/{}".format(
cls.config['server']['url'],
collection['id']
),
"@id": f"{cls.config['server']['url']}/collections/{collection['id']}",
"name": l10n.translate(collection['title'], locale_),
"description": l10n.translate(collection['description'], locale_),
"license": cls.fcmld['license'],
"keywords": l10n.translate(collection.get('keywords', None), locale_),
"keywords": l10n.translate(collection.get('keywords'), locale_),
"spatial": None if (not hascrs84 or not bbox) else [{
"@type": "Place",
"geo": {
"@type": "GeoShape",
"box": '{},{} {},{}'.format(*_bbox[0:2], *_bbox[2:4])
"box": f'{_bbox[0]},{_bbox[1]} {_bbox[2]},{_bbox[3]}'
}
} for _bbox in bbox],
"temporalCoverage": None if not interval else "{}/{}".format(
*interval[0]
)
"temporalCoverage": interval
}
dataset['url'] = dataset['@id']
@@ -173,20 +172,22 @@ def jsonldify_collection(cls, collection, locale_):
return dataset
def geojson2jsonld(config, data, dataset, identifier=None, id_field='id'):
def geojson2jsonld(config: dict, data: dict, dataset: str,
identifier: str = None, id_field: str = 'id') -> str:
"""
Render GeoJSON-LD from a GeoJSON base. Inserts a @context that can be
read from, and extended by, the pygeoapi configuration for a particular
dataset.
Render GeoJSON-LD from a GeoJSON base. Inserts a @context that can be
read from, and extended by, the pygeoapi configuration for a particular
dataset.
:param config: dict of configuration
:param data: dict of data:
:param dataset: dataset identifier
:param identifier: item identifier (optional)
:param id_field: item identifier_field (optional)
:param config: dict of configuration
:param data: dict of data:
:param dataset: dataset identifier
:param identifier: item identifier (optional)
:param id_field: item identifier_field (optional)
:returns: string of rendered JSON (GeoJSON-LD)
:returns: string of rendered JSON (GeoJSON-LD)
"""
context = config['resources'][dataset].get('context', []).copy()
defaultVocabulary = {
'schema': 'https://schema.org/',
@@ -215,17 +216,14 @@ def geojson2jsonld(config, data, dataset, identifier=None, id_field='id'):
'FeatureCollection': 'schema:itemList'
})
data['@id'] = '{}/collections/{}/items/'.format(
config['server']['url'], dataset
)
data['@id'] = f"{config['server']['url']}/collections/{dataset}"
for i, feature in enumerate(data['features']):
# Get URI for each feature
identifier = feature.get(id_field,
feature['properties'].get(id_field, ''))
if not is_url(str(identifier)):
identifier = '{}/collections/{}/items/{}'.format(
config['server']['url'], dataset, feature['id'])
identifier = f"config['server']['url']/collections/{dataset}/items/{feature['id']}" # noqa
data['features'][i] = {
id_field: identifier,
@@ -245,14 +243,14 @@ def geojson2jsonld(config, data, dataset, identifier=None, id_field='id'):
return ldjsonData
def jsonldify_geometry(feature):
def jsonldify_geometry(feature: dict) -> None:
"""
Render JSON-LD for feature with GeoJSON, Geosparql/WKT, and
schema geometry encodings.
Render JSON-LD for feature with GeoJSON, Geosparql/WKT, and
schema geometry encodings.
:param feature: feature body to with GeoJSON geometry
:param feature: feature body to with GeoJSON geometry
:returns: None
:returns: None
"""
geo = feature.get('geometry')
@@ -274,13 +272,13 @@ def jsonldify_geometry(feature):
feature['schema:geo'] = geom2schemageo(geom)
def geom2schemageo(geom):
def geom2schemageo(geom: shape) -> dict:
"""
Render Schema Geometry from a GeoJSON base.
Render Schema Geometry from a GeoJSON base.
:param geom: shapely geom of feature
:param geom: shapely geom of feature
:returns: dict of rendered schema:geo geometry
:returns: dict of rendered schema:geo geometry
"""
f = {'@type': 'schema:GeoShape'}
if geom.geom_type == 'Point':
@@ -313,9 +311,9 @@ def geom2schemageo(geom):
# MultiPolygon to Polygon (buffer of 0 helps ensure manifold polygon)
poly = unary_union(geom.buffer(0))
if poly.geom_type.startswith('Multi') or not poly.is_valid:
LOGGER.debug('Invalid Poly: {}'.format(poly.geom_type))
LOGGER.debug(f'Invalid MultiPolygon: {poly.geom_type}')
poly = poly.convex_hull
LOGGER.debug('New Poly: {}'.format(poly.geom_type))
LOGGER.debug(f'New MultiPolygon: {poly.geom_type}')
poly_geom = poly.exterior.coords[:]
else:
+138 -143
View File
@@ -61,7 +61,7 @@ THISDIR = os.path.dirname(os.path.realpath(__file__))
def get_ogc_schemas_location(server_config):
osl = server_config.get('ogc_schemas_location', None)
osl = server_config.get('ogc_schemas_location')
value = 'https://schemas.opengis.net'
@@ -86,7 +86,7 @@ def gen_media_type_object(media_type, api_type, path):
:returns: `dict` of media type object
"""
ref = '{}/{}'.format(OPENAPI_YAML[api_type], path)
ref = f'{OPENAPI_YAML[api_type]}/{path}'
content = {
media_type: {
@@ -177,9 +177,9 @@ def get_oas_30(cfg):
{'$ref': '#/components/parameters/lang'}
],
'responses': {
'200': {'$ref': '{}#/components/responses/LandingPage'.format(OPENAPI_YAML['oapif'])}, # noqa
'400': {'$ref': '{}#/components/responses/InvalidParameter'.format(OPENAPI_YAML['oapif'])}, # noqa
'500': {'$ref': '{}#/components/responses/ServerError'.format(OPENAPI_YAML['oapif'])} # noqa
'200': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/LandingPage"}, # noqa
'400': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/InvalidParameter"}, # noqa
'500': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/ServerError"} # noqa
}
}
}
@@ -209,7 +209,7 @@ def get_oas_30(cfg):
],
'responses': {
'200': {'$ref': '#/components/responses/200'},
'400': {'$ref': '{}#/components/responses/InvalidParameter'.format(OPENAPI_YAML['oapif'])}, # noqa
'400': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/InvalidParameter"}, # noqa
'default': {'$ref': '#/components/responses/default'}
}
}
@@ -226,9 +226,9 @@ def get_oas_30(cfg):
{'$ref': '#/components/parameters/lang'}
],
'responses': {
'200': {'$ref': '{}#/components/responses/ConformanceDeclaration'.format(OPENAPI_YAML['oapif'])}, # noqa
'400': {'$ref': '{}#/components/responses/InvalidParameter'.format(OPENAPI_YAML['oapif'])}, # noqa
'500': {'$ref': '{}#/components/responses/ServerError'.format(OPENAPI_YAML['oapif'])} # noqa
'200': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/LandingPage"}, # noqa
'400': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/InvalidParameter"}, # noqa
'500': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/ServerError"} # noqa
}
}
}
@@ -244,9 +244,9 @@ def get_oas_30(cfg):
{'$ref': '#/components/parameters/lang'}
],
'responses': {
'200': {'$ref': '{}#/components/responses/Collections'.format(OPENAPI_YAML['oapif'])}, # noqa
'400': {'$ref': '{}#/components/responses/InvalidParameter'.format(OPENAPI_YAML['oapif'])}, # noqa
'500': {'$ref': '{}#/components/responses/ServerError'.format(OPENAPI_YAML['oapif'])} # noqa
'200': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/LandingPage"}, # noqa
'400': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/InvalidParameter"}, # noqa
'500': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/ServerError"} # noqa
}
}
}
@@ -434,12 +434,12 @@ def get_oas_30(cfg):
for k, v in collections.items():
if v.get('visibility', 'default') == 'hidden':
LOGGER.debug('Skipping hidden layer: {}'.format(k))
LOGGER.debug(f'Skipping hidden layer: {k}')
continue
name = l10n.translate(k, locale_)
title = l10n.translate(v['title'], locale_)
desc = l10n.translate(v['description'], locale_)
collection_name_path = '/collections/{}'.format(k)
collection_name_path = f'/collections/{k}'
tag = {
'name': name,
'description': desc,
@@ -457,19 +457,19 @@ def get_oas_30(cfg):
paths[collection_name_path] = {
'get': {
'summary': 'Get {} metadata'.format(title),
'summary': f'Get {title} metadata',
'description': desc,
'tags': [name],
'operationId': 'describe{}Collection'.format(name.capitalize()), # noqa
'operationId': f'describe{name.capitalize()}Collection',
'parameters': [
{'$ref': '#/components/parameters/f'},
{'$ref': '#/components/parameters/lang'}
],
'responses': {
'200': {'$ref': '{}#/components/responses/Collection'.format(OPENAPI_YAML['oapif'])}, # noqa
'400': {'$ref': '{}#/components/responses/InvalidParameter'.format(OPENAPI_YAML['oapif'])}, # noqa
'404': {'$ref': '{}#/components/responses/NotFound'.format(OPENAPI_YAML['oapif'])}, # noqa
'500': {'$ref': '{}#/components/responses/ServerError'.format(OPENAPI_YAML['oapif'])} # noqa
'200': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/Collection"}, # noqa
'400': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/InvalidParameter"}, # noqa
'404': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/NotFound"}, # noqa
'500': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/ServerError"} # noqa
}
}
}
@@ -489,7 +489,7 @@ def get_oas_30(cfg):
p = load_plugin('provider', get_provider_by_type(
collections[k]['providers'], ptype))
items_path = '{}/items'.format(collection_name_path)
items_path = f'{collection_name_path}/items'
coll_properties = deepcopy(oas['components']['parameters']['properties']) # noqa
@@ -497,26 +497,26 @@ def get_oas_30(cfg):
paths[items_path] = {
'get': {
'summary': 'Get {} items'.format(title), # noqa
'summary': f'Get {title} items',
'description': desc,
'tags': [name],
'operationId': 'get{}Features'.format(name.capitalize()),
'operationId': f'get{name.capitalize()}Features',
'parameters': [
items_f,
items_l,
{'$ref': '{}#/components/parameters/bbox'.format(OPENAPI_YAML['oapif'])}, # noqa
{'$ref': '{}#/components/parameters/limit'.format(OPENAPI_YAML['oapif'])}, # noqa
{'$ref': f"{OPENAPI_YAML['oapif']}#/components/parameters/bbox"}, # noqa
{'$ref': f"{OPENAPI_YAML['oapif']}#/components/parameters/limit"}, # noqa
coll_properties,
{'$ref': '#/components/parameters/vendorSpecificParameters'}, # noqa
{'$ref': '#/components/parameters/skipGeometry'},
{'$ref': '{}/parameters/sortby.yaml'.format(OPENAPI_YAML['oapir'])}, # noqa
{'$ref': f"{OPENAPI_YAML['oapir']}/parameters/sortby.yaml"}, # noqa
{'$ref': '#/components/parameters/offset'},
],
'responses': {
'200': {'$ref': '{}#/components/responses/Features'.format(OPENAPI_YAML['oapif'])}, # noqa
'400': {'$ref': '{}#/components/responses/InvalidParameter'.format(OPENAPI_YAML['oapif'])}, # noqa
'404': {'$ref': '{}#/components/responses/NotFound'.format(OPENAPI_YAML['oapif'])}, # noqa
'500': {'$ref': '{}#/components/responses/ServerError'.format(OPENAPI_YAML['oapif'])} # noqa
'200': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/Features"}, # noqa
'400': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/InvalidParameter"}, # noqa
'404': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/NotFound"}, # noqa
'500': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/ServerError"} # noqa
}
}
}
@@ -525,10 +525,10 @@ def get_oas_30(cfg):
LOGGER.debug('Provider is editable; adding post')
paths[items_path]['post'] = {
'summary': 'Add {} items'.format(title), # noqa
'summary': f'Add {title} items',
'description': desc,
'tags': [name],
'operationId': 'add{}Features'.format(name.capitalize()),
'operationId': f'add{name.capitalize()}Features',
'requestBody': {
'description': 'Adds item to collection',
'content': {
@@ -540,8 +540,8 @@ def get_oas_30(cfg):
},
'responses': {
'201': {'description': 'Successful creation'},
'400': {'$ref': '{}#/components/responses/InvalidParameter'.format(OPENAPI_YAML['oapif'])}, # noqa
'500': {'$ref': '{}#/components/responses/ServerError'.format(OPENAPI_YAML['oapif'])} # noqa
'400': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/InvalidParameter"}, # noqa
'500': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/ServerError"} # noqa
}
}
@@ -555,33 +555,32 @@ def get_oas_30(cfg):
if ptype == 'record':
paths[items_path]['get']['parameters'].append(
{'$ref': '{}/parameters/q.yaml'.format(OPENAPI_YAML['oapir'])}) # noqa
{'$ref': f"{OPENAPI_YAML['oapir']}/parameters/q.yaml"})
if p.fields:
queryables_path = '{}/queryables'.format(collection_name_path)
queryables_path = f'{collection_name_path}/queryables'
paths[queryables_path] = {
'get': {
'summary': 'Get {} queryables'.format(title),
'summary': f'Get {title} queryables',
'description': desc,
'tags': [name],
'operationId': 'get{}Queryables'.format(
name.capitalize()),
'operationId': f'get{name.capitalize()}Queryables',
'parameters': [
items_f,
items_l
],
'responses': {
'200': {'$ref': '#/components/responses/Queryables'}, # noqa
'400': {'$ref': '{}#/components/responses/InvalidParameter'.format(OPENAPI_YAML['oapif'])}, # noqa
'404': {'$ref': '{}#/components/responses/NotFound'.format(OPENAPI_YAML['oapif'])}, # noqa
'500': {'$ref': '{}#/components/responses/ServerError'.format(OPENAPI_YAML['oapif'])} # noqa
'400': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/InvalidParameter"}, # noqa
'404': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/NotFound"}, # noqa
'500': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/ServerError"}, # noqa
}
}
}
if p.time_field is not None:
paths[items_path]['get']['parameters'].append(
{'$ref': '{}#/components/parameters/datetime'.format(OPENAPI_YAML['oapif'])}) # noqa
{'$ref': f"{OPENAPI_YAML['oapif']}#/components/parameters/datetime"}) # noqa
for field, type_ in p.fields.items():
@@ -611,8 +610,8 @@ def get_oas_30(cfg):
else:
schema = type_
path_ = '{}/items'.format(collection_name_path)
paths['{}'.format(path_)]['get']['parameters'].append({
path_ = f'{collection_name_path}/items'
paths[path_]['get']['parameters'].append({
'name': field,
'in': 'query',
'required': False,
@@ -621,29 +620,29 @@ def get_oas_30(cfg):
'explode': False
})
paths['{}/items/{{featureId}}'.format(collection_name_path)] = {
paths[f'{collection_name_path}/items/{{featureId}}'] = {
'get': {
'summary': 'Get {} item by id'.format(title),
'summary': f'Get {title} item by id',
'description': desc,
'tags': [name],
'operationId': 'get{}Feature'.format(name.capitalize()),
'operationId': f'get{name.capitalize()}Feature',
'parameters': [
{'$ref': '{}#/components/parameters/featureId'.format(OPENAPI_YAML['oapif'])}, # noqa
{'$ref': f"{OPENAPI_YAML['oapif']}#/components/parameters/featureId"}, # noqa
{'$ref': '#/components/parameters/f'},
{'$ref': '#/components/parameters/lang'}
],
'responses': {
'200': {'$ref': '{}#/components/responses/Feature'.format(OPENAPI_YAML['oapif'])}, # noqa
'400': {'$ref': '{}#/components/responses/InvalidParameter'.format(OPENAPI_YAML['oapif'])}, # noqa
'404': {'$ref': '{}#/components/responses/NotFound'.format(OPENAPI_YAML['oapif'])}, # noqa
'500': {'$ref': '{}#/components/responses/ServerError'.format(OPENAPI_YAML['oapif'])} # noqa
'200': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/Feature"}, # noqa
'400': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/InvalidParameter"}, # noqa
'404': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/NotFound"}, # noqa
'500': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/ServerError"} # noqa
}
}
}
try:
schema_ref = p.get_schema()
paths['{}/items/{{featureId}}'.format(collection_name_path)]['get']['responses']['200'] = { # noqa
paths[f'{collection_name_path}/items/{{featureId}}']['get']['responses']['200'] = { # noqa
'content': {
schema_ref[0]: {
'schema': schema_ref[1]
@@ -655,14 +654,14 @@ def get_oas_30(cfg):
if p.editable:
LOGGER.debug('Provider is editable; adding put/delete')
put_path = '{}/items/{{featureId}}'.format(collection_name_path) # noqa
put_path = f'{collection_name_path}/items/{{featureId}}' # noqa
paths[put_path]['put'] = { # noqa
'summary': 'Update {} items'.format(title),
'summary': f'Update {title} items',
'description': desc,
'tags': [name],
'operationId': 'update{}Features'.format(name.capitalize()), # noqa
'operationId': f'update{name.capitalize()}Features',
'parameters': [
{'$ref': '{}#/components/parameters/featureId'.format(OPENAPI_YAML['oapif'])}, # noqa
{'$ref': f"{OPENAPI_YAML['oapif']}#/components/parameters/featureId"} # noqa
],
'requestBody': {
'description': 'Updates item in collection',
@@ -675,8 +674,8 @@ def get_oas_30(cfg):
},
'responses': {
'204': {'description': 'Successful update'},
'400': {'$ref': '{}#/components/responses/InvalidParameter'.format(OPENAPI_YAML['oapif'])}, # noqa
'500': {'$ref': '{}#/components/responses/ServerError'.format(OPENAPI_YAML['oapif'])} # noqa
'400': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/InvalidParameter"}, # noqa
'500': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/ServerError"} # noqa
}
}
@@ -688,18 +687,18 @@ def get_oas_30(cfg):
except Exception as err:
LOGGER.debug(err)
paths['{}/items/{{featureId}}'.format(collection_name_path)]['delete'] = { # noqa
'summary': 'Delete {} items'.format(title),
paths[f'{collection_name_path}/items/{{featureId}}']['delete'] = { # noqa
'summary': f'Delete {title} items',
'description': desc,
'tags': [name],
'operationId': 'delete{}Features'.format(name.capitalize()), # noqa
'operationId': f'delete{name.capitalize()}Features',
'parameters': [
{'$ref': '{}#/components/parameters/featureId'.format(OPENAPI_YAML['oapif'])}, # noqa
{'$ref': f"{OPENAPI_YAML['oapif']}#/components/parameters/featureId"}, # noqa
],
'responses': {
'200': {'description': 'Successful delete'},
'400': {'$ref': '{}#/components/responses/InvalidParameter'.format(OPENAPI_YAML['oapif'])}, # noqa
'500': {'$ref': '{}#/components/responses/ServerError'.format(OPENAPI_YAML['oapif'])} # noqa
'400': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/InvalidParameter"}, # noqa
'500': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/ServerError"} # noqa
}
}
@@ -711,71 +710,67 @@ def get_oas_30(cfg):
load_plugin('provider', get_provider_by_type(
collections[k]['providers'], 'coverage'))
coverage_path = '{}/coverage'.format(collection_name_path)
coverage_path = f'{collection_name_path}/coverage'
paths[coverage_path] = {
'get': {
'summary': 'Get {} coverage'.format(title),
'summary': f'Get {title} coverage',
'description': desc,
'tags': [name],
'operationId': 'get{}Coverage'.format(name.capitalize()),
'operationId': f'get{name.capitalize()}Coverage',
'parameters': [
items_f,
items_l,
{'$ref': '{}#/components/parameters/bbox'.format(OPENAPI_YAML['oapif'])}, # noqa
{'$ref': f"{OPENAPI_YAML['oapif']}#/components/parameters/bbox"}, # noqa
{'$ref': '#/components/parameters/bbox-crs'}
],
'responses': {
'200': {'$ref': '{}#/components/responses/Features'.format(OPENAPI_YAML['oapif'])}, # noqa
'400': {'$ref': '{}#/components/responses/InvalidParameter'.format(OPENAPI_YAML['oapif'])}, # noqa
'404': {'$ref': '{}#/components/responses/NotFound'.format(OPENAPI_YAML['oapif'])}, # noqa
'500': {'$ref': '{}#/components/responses/ServerError'.format(OPENAPI_YAML['oapif'])} # noqa
'200': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/Features"}, # noqa
'400': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/InvalidParameter"}, # noqa
'404': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/NotFound"}, # noqa
'500': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/ServerError"} # noqa
}
}
}
coverage_domainset_path = '{}/coverage/domainset'.format(
collection_name_path)
coverage_domainset_path = f'{collection_name_path}/coverage/domainset' # noqa
paths[coverage_domainset_path] = {
'get': {
'summary': 'Get {} coverage domain set'.format(title),
'summary': f'Get {title} coverage domain set',
'description': desc,
'tags': [name],
'operationId': 'get{}CoverageDomainSet'.format(
name.capitalize()),
'operationId': f'get{name.capitalize()}CoverageDomainSet',
'parameters': [
items_f,
items_l
],
'responses': {
'200': {'$ref': '{}/schemas/cis_1.1/domainSet.yaml'.format(OPENAPI_YAML['oacov'])}, # noqa
'400': {'$ref': '{}#/components/responses/InvalidParameter'.format(OPENAPI_YAML['oapif'])}, # noqa
'404': {'$ref': '{}#/components/responses/NotFound'.format(OPENAPI_YAML['oapif'])}, # noqa
'500': {'$ref': '{}#/components/responses/ServerError'.format(OPENAPI_YAML['oapif'])} # noqa
'200': {'$ref': f"{OPENAPI_YAML['oacov']}/schemas/cis_1.1/domainSet.yaml"}, # noqa
'400': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/InvalidParameter"}, # noqa
'404': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/NotFound"}, # noqa
'500': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/ServerError"} # noqa
}
}
}
coverage_rangetype_path = '{}/coverage/rangetype'.format(
collection_name_path)
coverage_rangetype_path = f'{collection_name_path}/coverage/rangetype' # noqa
paths[coverage_rangetype_path] = {
'get': {
'summary': 'Get {} coverage range type'.format(title),
'summary': f'Get {title} coverage range type',
'description': desc,
'tags': [name],
'operationId': 'get{}CoverageRangeType'.format(
name.capitalize()),
'operationId': f'get{name.capitalize()}CoverageRangeType',
'parameters': [
items_f,
items_l
],
'responses': {
'200': {'$ref': '{}/schemas/cis_1.1/rangeType.yaml'.format(OPENAPI_YAML['oacov'])}, # noqa
'400': {'$ref': '{}#/components/responses/InvalidParameter'.format(OPENAPI_YAML['oapif'])}, # noqa
'404': {'$ref': '{}#/components/responses/NotFound'.format(OPENAPI_YAML['oapif'])}, # noqa
'500': {'$ref': '{}#/components/responses/ServerError'.format(OPENAPI_YAML['oapif'])} # noqa
'200': {'$ref': f"{OPENAPI_YAML['oacov']}/schemas/cis_1.1/rangeType.yaml"}, # noqa
'400': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/InvalidParameter"}, # noqa
'404': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/NotFound"}, # noqa
'500': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/ServerError"} # noqa
}
}
}
@@ -830,47 +825,47 @@ def get_oas_30(cfg):
},
'links': {
'type': 'array',
'items': {'$ref': '{}#/components/schemas/link'.format(OPENAPI_YAML['oapit'])}, # noqa
'items': {'$ref': f"{OPENAPI_YAML['oapit']}#/components/schemas/link"} # noqa
}
}
}
}
)
tiles_path = '{}/tiles'.format(collection_name_path)
tiles_path = f'{collection_name_path}/tiles'
paths[tiles_path] = {
'get': {
'summary': 'Fetch a {} tiles description'.format(title), # noqa
'summary': f'Fetch a {title} tiles description',
'description': desc,
'tags': [name],
'operationId': 'describe{}Tiles'.format(name.capitalize()),
'operationId': f'describe{name.capitalize()}Tiles',
'parameters': [
items_f,
# items_l TODO: is this useful?
],
'responses': {
'200': {'$ref': '#/components/responses/Tiles'},
'400': {'$ref': '{}#/components/responses/InvalidParameter'.format(OPENAPI_YAML['oapif'])}, # noqa
'404': {'$ref': '{}#/components/responses/NotFound'.format(OPENAPI_YAML['oapif'])}, # noqa
'500': {'$ref': '{}#/components/responses/ServerError'.format(OPENAPI_YAML['oapif'])} # noqa
'400': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/InvalidParameter"}, # noqa
'404': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/NotFound"}, # noqa
'500': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/ServerError"} # noqa
}
}
}
tiles_data_path = '{}/tiles/{{tileMatrixSetId}}/{{tileMatrix}}/{{tileRow}}/{{tileCol}}'.format(collection_name_path) # noqa
tiles_data_path = f'{collection_name_path}/tiles/{{tileMatrixSetId}}/{{tileMatrix}}/{{tileRow}}/{{tileCol}}' # noqa
paths[tiles_data_path] = {
'get': {
'summary': 'Get a {} tile'.format(title),
'summary': f'Get a {title} tile',
'description': desc,
'tags': [name],
'operationId': 'get{}Tiles'.format(name.capitalize()),
'operationId': f'get{name.capitalize()}Tiles',
'parameters': [
{'$ref': '{}#/components/parameters/tileMatrixSetId'.format(OPENAPI_YAML['oat'])}, # noqa
{'$ref': '{}#/components/parameters/tileMatrix'.format(OPENAPI_YAML['oat'])}, # noqa
{'$ref': '{}#/components/parameters/tileRow'.format(OPENAPI_YAML['oat'])}, # noqa
{'$ref': '{}#/components/parameters/tileCol'.format(OPENAPI_YAML['oat'])}, # noqa
{'$ref': f"{OPENAPI_YAML['oat']}#/components/parameters/tileMatrixSetId"}, # noqa
{'$ref': f"{OPENAPI_YAML['oat']}#/components/parameters/tileMatrix"}, # noqa
{'$ref': f"{OPENAPI_YAML['oat']}#/components/parameters/tileRow"}, # noqa
{'$ref': f"{OPENAPI_YAML['oat']}#/components/parameters/tileCol"}, # noqa
{
'name': 'f',
'in': 'query',
@@ -886,9 +881,9 @@ def get_oas_30(cfg):
}
],
'responses': {
'400': {'$ref': '{}#/components/responses/InvalidParameter'.format(OPENAPI_YAML['oapif'])}, # noqa
'404': {'$ref': '{}#/components/responses/NotFound'.format(OPENAPI_YAML['oapif'])}, # noqa
'500': {'$ref': '{}#/components/responses/ServerError'.format(OPENAPI_YAML['oapif'])} # noqa
'400': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/InvalidParameter"}, # noqa
'404': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/NotFound"}, # noqa
'500': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/ServerError"} # noqa
}
}
}
@@ -916,29 +911,29 @@ def get_oas_30(cfg):
for qt in ep.get_query_types():
edr_query_endpoints.append({
'path': '{}/{}'.format(collection_name_path, qt),
'path': f'{collection_name_path}/{qt}',
'qt': qt,
'op_id': 'query{}{}'.format(qt.capitalize(), k.capitalize()) # noqa
'op_id': f'query{qt.capitalize()}{k.capitalize()}'
})
if ep.instances:
edr_query_endpoints.append({
'path': '{}/instances/{{instanceId}}/{}'.format(collection_name_path, qt), # noqa
'path': f'{collection_name_path}/instances/{{instanceId}}/{qt}', # noqa
'qt': qt,
'op_id': 'query{}Instance{}'.format(qt.capitalize(), k.capitalize()) # noqa
'op_id': f'query{qt.capitalize()}Instance{k.capitalize()}' # noqa
})
for eqe in edr_query_endpoints:
paths[eqe['path']] = {
'get': {
'summary': 'query {} by {}'.format(v['description'], eqe['qt']), # noqa
'summary': f"query {v['description']} by {eqe['qt']}", # noqa
'description': v['description'],
'tags': [k],
'operationId': eqe['op_id'],
'parameters': [
{'$ref': '{}/parameters/{}Coords.yaml'.format(OPENAPI_YAML['oaedr'], eqe['qt'])}, # noqa
{'$ref': '{}#/components/parameters/datetime'.format(OPENAPI_YAML['oapif'])}, # noqa
{'$ref': '{}/parameters/parameter-name.yaml'.format(OPENAPI_YAML['oaedr'])}, # noqa
{'$ref': '{}/parameters/z.yaml'.format(OPENAPI_YAML['oaedr'])}, # noqa
{'$ref': f"{OPENAPI_YAML['oaedr']}/parameters/{eqe['qt']}Coords.yaml"}, # noqa
{'$ref': f"{OPENAPI_YAML['oapif']}#/components/parameters/datetime"}, # noqa
{'$ref': f"{OPENAPI_YAML['oaedr']}/parameters/parameter-name.yaml"}, # noqa
{'$ref': f"{OPENAPI_YAML['oaedr']}/parameters/z.yaml"}, # noqa
{'$ref': '#/components/parameters/f'}
],
'responses': {
@@ -947,13 +942,14 @@ def get_oas_30(cfg):
'content': {
'application/prs.coverage+json': {
'schema': {
'$ref': '{}/schemas/coverageJSON.yaml'.format(OPENAPI_YAML['oaedr'])} # noqa
'$ref': f"{OPENAPI_YAML['oaedr']}/schemas/coverageJSON.yaml" # noqa
}
}
}
}
}
}
}
LOGGER.debug('setting up maps endpoints')
map_extension = filter_providers_by_type(
@@ -966,15 +962,15 @@ def get_oas_30(cfg):
map_f['schema']['enum'] = [map_extension['format']['name']]
map_f['schema']['default'] = map_extension['format']['name']
pth = '/collections/{}/map'.format(k)
pth = f'/collections/{k}/map'
paths[pth] = {
'get': {
'summary': 'Get map',
'description': '{} map'.format(v['description']),
'description': f"{v['description']} map",
'tags': [k],
'operationId': 'getMap',
'parameters': [
{'$ref': '{}#/components/parameters/bbox'.format(OPENAPI_YAML['oapif'])}, # noqa
{'$ref': f"{OPENAPI_YAML['oapif']}#/components/parameters/bbox"}, # noqa
{
'name': 'width',
'in': 'query',
@@ -1019,14 +1015,14 @@ def get_oas_30(cfg):
'application/json': {}
}
},
'400': {'$ref': '{}#/components/responses/InvalidParameter'.format(OPENAPI_YAML['oapif'])}, # noqa
'500': {'$ref': '{}#/components/responses/ServerError'.format(OPENAPI_YAML['oapif'])} # noqa
'400': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/InvalidParameter"}, # noqa
'500': {'$ref': f"{OPENAPI_YAML['oapif']}#/components/responses/ServerError"}, # noqa
}
}
}
if mp.time_field is not None:
paths[pth]['get']['parameters'].append(
{'$ref': '{}#/components/parameters/datetime'.format(OPENAPI_YAML['oapif'])}) # noqa
{'$ref': f"{OPENAPI_YAML['oapif']}#/components/parameters/datetime"}) # noqa
LOGGER.debug('setting up STAC')
stac_collections = filter_dict_by_key_value(cfg['resources'],
@@ -1061,7 +1057,7 @@ def get_oas_30(cfg):
{'$ref': '#/components/parameters/f'}
],
'responses': {
'200': {'$ref': '{}/responses/ProcessList.yaml'.format(OPENAPI_YAML['oapip'])}, # noqa
'200': {'$ref': f"{OPENAPI_YAML['oapip']}/responses/ProcessList.yaml"}, # noqa
'default': {'$ref': '#/components/responses/default'}
}
}
@@ -1070,13 +1066,13 @@ def get_oas_30(cfg):
for k, v in processes.items():
if k.startswith('_'):
LOGGER.debug('Skipping hidden layer: {}'.format(k))
LOGGER.debug(f'Skipping hidden layer: {k}')
continue
name = l10n.translate(k, locale_)
p = load_plugin('process', v['processor'])
md_desc = l10n.translate(p.metadata['description'], locale_)
process_name_path = '/processes/{}'.format(name)
process_name_path = f'/processes/{name}'
tag = {
'name': name,
'description': md_desc, # noqa
@@ -1097,7 +1093,7 @@ def get_oas_30(cfg):
'summary': 'Get process metadata',
'description': md_desc,
'tags': [name],
'operationId': 'describe{}Process'.format(name.capitalize()), # noqa
'operationId': f'describe{name.capitalize()}Process',
'parameters': [
{'$ref': '#/components/parameters/f'}
],
@@ -1108,18 +1104,17 @@ def get_oas_30(cfg):
}
}
paths['{}/execution'.format(process_name_path)] = {
paths[f'{process_name_path}/execution'] = {
'post': {
'summary': 'Process {} execution'.format(
l10n.translate(p.metadata['title'], locale_)),
'summary': f"Process {l10n.translate(p.metadata['title'], locale_)} execution", # noqa
'description': md_desc,
'tags': [name],
'operationId': 'execute{}Job'.format(name.capitalize()),
'operationId': f'execute{name.capitalize()}Job',
'responses': {
'200': {'$ref': '#/components/responses/200'},
'201': {'$ref': '{}/responses/ExecuteAsync.yaml'.format(OPENAPI_YAML['oapip'])}, # noqa
'404': {'$ref': '{}/responses/NotFound.yaml'.format(OPENAPI_YAML['oapip'])}, # noqa
'500': {'$ref': '{}/responses/ServerError.yaml'.format(OPENAPI_YAML['oapip'])}, # noqa
'201': {'$ref': f"{OPENAPI_YAML['oapip']}/responses/ExecuteAsync.yaml"}, # noqa
'404': {'$ref': f"{OPENAPI_YAML['oapip']}/responses/NotFound.yaml"}, # noqa
'500': {'$ref': f"{OPENAPI_YAML['oapip']}/responses/ServerError.yaml"}, # noqa
'default': {'$ref': '#/components/responses/default'}
},
'requestBody': {
@@ -1128,7 +1123,7 @@ def get_oas_30(cfg):
'content': {
'application/json': {
'schema': {
'$ref': '{}/schemas/execute.yaml'.format(OPENAPI_YAML['oapip']) # noqa
'$ref': f"{OPENAPI_YAML['oapip']}/schemas/execute.yaml" # noqa
}
}
}
@@ -1136,7 +1131,7 @@ def get_oas_30(cfg):
}
}
if 'example' in p.metadata:
paths['{}/execution'.format(process_name_path)]['post']['requestBody']['content']['application/json']['example'] = p.metadata['example'] # noqa
paths[f'{process_name_path}/execution']['post']['requestBody']['content']['application/json']['example'] = p.metadata['example'] # noqa
name_in_path = {
'name': 'jobId',
@@ -1157,7 +1152,7 @@ def get_oas_30(cfg):
'operationId': 'getJobs',
'responses': {
'200': {'$ref': '#/components/responses/200'},
'404': {'$ref': '{}/responses/NotFound.yaml'.format(OPENAPI_YAML['oapip'])}, # noqa
'404': {'$ref': f"{OPENAPI_YAML['oapip']}/responses/NotFound.yaml"}, # noqa
'default': {'$ref': '#/components/responses/default'}
}
}
@@ -1175,7 +1170,7 @@ def get_oas_30(cfg):
'operationId': 'getJob',
'responses': {
'200': {'$ref': '#/components/responses/200'},
'404': {'$ref': '{}/responses/NotFound.yaml'.format(OPENAPI_YAML['oapip'])}, # noqa
'404': {'$ref': f"{OPENAPI_YAML['oapip']}/responses/NotFound.yaml"}, # noqa
'default': {'$ref': '#/components/responses/default'} # noqa
}
},
@@ -1189,7 +1184,7 @@ def get_oas_30(cfg):
'operationId': 'deleteJob',
'responses': {
'204': {'$ref': '#/components/responses/204'},
'404': {'$ref': '{}/responses/NotFound.yaml'.format(OPENAPI_YAML['oapip'])}, # noqa
'404': {'$ref': f"{OPENAPI_YAML['oapip']}/responses/NotFound.yaml"}, # noqa
'default': {'$ref': '#/components/responses/default'} # noqa
}
},
@@ -1207,7 +1202,7 @@ def get_oas_30(cfg):
'operationId': 'getJobResults',
'responses': {
'200': {'$ref': '#/components/responses/200'},
'404': {'$ref': '{}/responses/NotFound.yaml'.format(OPENAPI_YAML['oapip'])}, # noqa
'404': {'$ref': f"{OPENAPI_YAML['oapip']}/responses/NotFound.yaml"}, # noqa
'default': {'$ref': '#/components/responses/default'} # noqa
}
}
@@ -1297,7 +1292,7 @@ def validate(ctx, openapi_file):
if openapi_file is None:
raise click.ClickException('--openapi/-o required')
click.echo('Validating {}'.format(openapi_file))
click.echo(f'Validating {openapi_file}')
instance = yaml_load(openapi_file)
validate_openapi_document(instance)
click.echo('Valid OpenAPI document')
+7 -6
View File
@@ -30,6 +30,7 @@
import importlib
import logging
from typing import Any
LOGGER = logging.getLogger(__name__)
@@ -71,7 +72,7 @@ PLUGINS = {
}
def load_plugin(plugin_type, plugin_def):
def load_plugin(plugin_type: str, plugin_def: dict) -> Any:
"""
loads plugin by name
@@ -84,16 +85,16 @@ def load_plugin(plugin_type, plugin_def):
name = plugin_def['name']
if plugin_type not in PLUGINS.keys():
msg = 'Plugin type {} not found'.format(plugin_type)
msg = f'Plugin type {plugin_type} not found'
LOGGER.exception(msg)
raise InvalidPluginError(msg)
plugin_list = PLUGINS[plugin_type]
LOGGER.debug('Plugins: {}'.format(plugin_list))
LOGGER.debug(f'Plugins: {plugin_list}')
if '.' not in name and name not in plugin_list.keys():
msg = 'Plugin {} not found'.format(name)
msg = f'Plugin {name} not found'
LOGGER.exception(msg)
raise InvalidPluginError(msg)
@@ -102,8 +103,8 @@ def load_plugin(plugin_type, plugin_def):
else: # core formatter
packagename, classname = plugin_list[name].rsplit('.', 1)
LOGGER.debug('package name: {}'.format(packagename))
LOGGER.debug('class name: {}'.format(classname))
LOGGER.debug(f'package name: {packagename}')
LOGGER.debug(f'class name: {classname}')
module = importlib.import_module(packagename)
class_ = getattr(module, classname)
+5 -4
View File
@@ -2,7 +2,7 @@
#
# Authors: Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2019 Tom Kralidis
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -28,6 +28,7 @@
# =================================================================
import logging
from typing import Any, Tuple
LOGGER = logging.getLogger(__name__)
@@ -35,7 +36,7 @@ LOGGER = logging.getLogger(__name__)
class BaseProcessor:
"""generic Processor ABC. Processes are inherited from this class"""
def __init__(self, processor_def, process_metadata):
def __init__(self, processor_def: dict, process_metadata: dict):
"""
Initialize object
@@ -47,7 +48,7 @@ class BaseProcessor:
self.name = processor_def['name']
self.metadata = process_metadata
def execute(self):
def execute(self) -> Tuple[str, Any]:
"""
execute the process
@@ -57,7 +58,7 @@ class BaseProcessor:
raise NotImplementedError()
def __repr__(self):
return '<BaseProcessor> {}'.format(self.name)
return f'<BaseProcessor> {self.name}'
class ProcessorGenericError(Exception):
+5 -4
View File
@@ -2,7 +2,7 @@
#
# Authors: Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2019 Tom Kralidis
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -120,12 +120,13 @@ class HelloWorldProcessor(BaseProcessor):
def execute(self, data):
mimetype = 'application/json'
name = data.get('name', None)
name = data.get('name')
if name is None:
raise ProcessorExecuteError('Cannot process without a name')
value = 'Hello {}! {}'.format(name, data.get('message', '')).strip()
message = data.get('message', '')
value = f'Hello {name}! {message}'.strip()
outputs = {
'id': 'echo',
@@ -135,4 +136,4 @@ class HelloWorldProcessor(BaseProcessor):
return mimetype, outputs
def __repr__(self):
return '<HelloWorldProcessor> {}'.format(self.name)
return f'<HelloWorldProcessor> {self.name}'
+26 -19
View File
@@ -28,13 +28,14 @@
# =================================================================
from datetime import datetime
import io
import json
import logging
from multiprocessing import dummy
import os
from pathlib import Path
from typing import Any, Tuple
from pygeoapi.util import DATETIME_FORMAT, JobStatus
from pygeoapi.process.base import BaseProcessor
LOGGER = logging.getLogger(__name__)
@@ -42,7 +43,7 @@ LOGGER = logging.getLogger(__name__)
class BaseManager:
"""generic Manager ABC"""
def __init__(self, manager_def):
def __init__(self, manager_def: dict):
"""
Initialize object
@@ -53,10 +54,13 @@ class BaseManager:
self.name = manager_def['name']
self.is_async = False
self.connection = manager_def.get('connection', None)
self.output_dir = manager_def.get('output_dir', None)
self.connection = manager_def.get('connection')
self.output_dir = manager_def.get('output_dir')
def get_jobs(self, status=None):
if self.output_dir is not None:
self.output_dir = Path(self.output_dir)
def get_jobs(self, status: JobStatus = None) -> list:
"""
Get process jobs, optionally filtered by status
@@ -68,7 +72,7 @@ class BaseManager:
raise NotImplementedError()
def add_job(self, job_metadata):
def add_job(self, job_metadata: dict) -> str:
"""
Add a job
@@ -79,7 +83,7 @@ class BaseManager:
raise NotImplementedError()
def update_job(self, job_id, update_dict):
def update_job(self, job_id: str, update_dict: dict) -> bool:
"""
Updates a job
@@ -91,7 +95,7 @@ class BaseManager:
raise NotImplementedError()
def get_job(self, job_id):
def get_job(self, job_id: str) -> dict:
"""
Get a job (!)
@@ -102,7 +106,7 @@ class BaseManager:
raise NotImplementedError()
def get_job_result(self, job_id):
def get_job_result(self, job_id: str) -> Tuple[str, Any]:
"""
Returns the actual output from a completed process
@@ -113,7 +117,7 @@ class BaseManager:
raise NotImplementedError()
def delete_job(self, job_id):
def delete_job(self, job_id: str) -> bool:
"""
Deletes a job and associated results/outputs
@@ -124,7 +128,8 @@ class BaseManager:
raise NotImplementedError()
def _execute_handler_async(self, p, job_id, data_dict):
def _execute_handler_async(self, p: BaseProcessor, job_id: str,
data_dict: dict) -> Tuple[None, JobStatus]:
"""
This private execution handler executes a process in a background
thread using `multiprocessing.dummy`
@@ -145,7 +150,8 @@ class BaseManager:
_process.start()
return 'application/json', None, JobStatus.accepted
def _execute_handler_sync(self, p, job_id, data_dict):
def _execute_handler_sync(self, p: BaseProcessor, job_id: str,
data_dict: dict) -> Tuple[str, Any, JobStatus]:
"""
Synchronous execution handler
@@ -180,8 +186,8 @@ class BaseManager:
try:
if self.output_dir is not None:
filename = '{}-{}'.format(p.metadata['id'], job_id)
job_filename = os.path.join(self.output_dir, filename)
filename = f"{p.metadata['id']}-{job_id}"
job_filename = self.output_dir / filename
else:
job_filename = None
@@ -195,7 +201,7 @@ class BaseManager:
})
if self.output_dir is not None:
LOGGER.debug('writing output to {}'.format(job_filename))
LOGGER.debug(f'writing output to {job_filename}')
if isinstance(outputs, dict):
mode = 'w'
data = json.dumps(outputs, sort_keys=True, indent=4)
@@ -204,7 +210,7 @@ class BaseManager:
mode = 'wb'
data = outputs
encoding = None
with io.open(job_filename, mode, encoding=encoding) as fh:
with job_filename.open(mode=mode, encoding=encoding) as fh:
fh.write(data)
current_status = JobStatus.successful
@@ -213,7 +219,7 @@ class BaseManager:
'job_end_datetime': datetime.utcnow().strftime(
DATETIME_FORMAT),
'status': current_status.value,
'location': job_filename,
'location': str(job_filename),
'mimetype': jfmt,
'message': 'Job complete',
'progress': 100
@@ -230,6 +236,7 @@ class BaseManager:
# endpoint, even if the /result endpoint correctly returns the
# failure information (i.e. what one might assume is a 200
# response).
current_status = JobStatus.failed
code = 'InvalidParameterValue'
outputs = {
@@ -272,4 +279,4 @@ class BaseManager:
return self._execute_handler_async(p, job_id, data_dict)
def __repr__(self):
return '<BaseManager> {}'.format(self.name)
return f'<BaseManager> {self.name}'
+7 -4
View File
@@ -28,7 +28,9 @@
# =================================================================
import logging
from typing import Any, Tuple
from pygeoapi.process.base import BaseProcessor
from pygeoapi.process.manager.base import BaseManager
from pygeoapi.util import JobStatus
@@ -38,7 +40,7 @@ LOGGER = logging.getLogger(__name__)
class DummyManager(BaseManager):
"""generic Manager ABC"""
def __init__(self, manager_def):
def __init__(self, manager_def: dict):
"""
Initialize object
@@ -49,7 +51,7 @@ class DummyManager(BaseManager):
super().__init__(manager_def)
def get_jobs(self, status=None):
def get_jobs(self, status: JobStatus = None) -> list:
"""
Get process jobs, optionally filtered by status
@@ -61,7 +63,8 @@ class DummyManager(BaseManager):
return []
def execute_process(self, p, job_id, data_dict, is_async=False):
def execute_process(self, p: BaseProcessor, job_id: str, data_dict: dict,
is_async: bool = False) -> Tuple[str, Any, int]:
"""
Default process execution handler
@@ -93,4 +96,4 @@ class DummyManager(BaseManager):
return jfmt, outputs, current_status
def __repr__(self):
return '<DummyManager> {}'.format(self.name)
return f'<DummyManager> {self.name}'
+19 -17
View File
@@ -28,10 +28,10 @@
# =================================================================
import fcntl
import io
import json
import logging
import os
from pathlib import Path
from typing import Any, Tuple
import tinydb
@@ -44,7 +44,7 @@ LOGGER = logging.getLogger(__name__)
class TinyDBManager(BaseManager):
"""TinyDB Manager"""
def __init__(self, manager_def):
def __init__(self, manager_def: dict):
"""
Initialize object
@@ -56,7 +56,7 @@ class TinyDBManager(BaseManager):
super().__init__(manager_def)
self.is_async = True
def _connect(self, mode='r'):
def _connect(self, mode: str = 'r') -> bool:
"""
connect to manager
@@ -70,7 +70,7 @@ class TinyDBManager(BaseManager):
return True
def destroy(self):
def destroy(self) -> bool:
"""
Destroy manager
@@ -81,7 +81,7 @@ class TinyDBManager(BaseManager):
self.db.close()
return True
def get_jobs(self, status=None):
def get_jobs(self, status: JobStatus = None) -> list:
"""
Get jobs
@@ -97,7 +97,7 @@ class TinyDBManager(BaseManager):
return jobs_list
def add_job(self, job_metadata):
def add_job(self, job_metadata: dict) -> str:
"""
Add a job
@@ -112,7 +112,7 @@ class TinyDBManager(BaseManager):
return doc_id
def update_job(self, job_id, update_dict):
def update_job(self, job_id: str, update_dict: dict) -> bool:
"""
Updates a job
@@ -128,7 +128,7 @@ class TinyDBManager(BaseManager):
return True
def delete_job(self, job_id):
def delete_job(self, job_id: str) -> bool:
"""
Deletes a job
@@ -139,9 +139,9 @@ class TinyDBManager(BaseManager):
# delete result file if present
job_result = self.get_job(job_id)
if job_result:
location = job_result.get('location', None)
location = job_result.get('location')
if location and self.output_dir is not None:
os.remove(location)
Path(location).unlink()
self._connect(mode='w')
removed = bool(self.db.remove(tinydb.where('identifier') == job_id))
@@ -149,7 +149,7 @@ class TinyDBManager(BaseManager):
return removed
def get_job(self, job_id):
def get_job(self, job_id: str) -> dict:
"""
Get a single job
@@ -166,7 +166,7 @@ class TinyDBManager(BaseManager):
self.db.close()
return result
def get_job_result(self, job_id):
def get_job_result(self, job_id: str) -> Tuple[str, Any]:
"""
Get a job's status, and actual output of executing the process
@@ -180,8 +180,8 @@ class TinyDBManager(BaseManager):
# job does not exist
return None
location = job_result.get('location', None)
mimetype = job_result.get('mimetype', None)
location = job_result.get('location')
mimetype = job_result.get('mimetype')
job_status = JobStatus[job_result['status']]
if not job_status == JobStatus.successful:
@@ -191,11 +191,13 @@ class TinyDBManager(BaseManager):
# Job data was not written for some reason
# TODO log/raise exception?
return (None,)
else:
location = Path(location)
with io.open(location, 'r', encoding='utf-8') as filehandler:
with location.open('r', encoding='utf-8') as filehandler:
result = json.load(filehandler)
return mimetype, result
def __repr__(self):
return '<TinyDBManager> {}'.format(self.name)
return f'<TinyDBManager> {self.name}'
+7 -7
View File
@@ -61,11 +61,11 @@ class BaseProvider:
raise RuntimeError('name/type/data are required')
self.editable = provider_def.get('editable', False)
self.options = provider_def.get('options', None)
self.id_field = provider_def.get('id_field', None)
self.uri_field = provider_def.get('uri_field', None)
self.x_field = provider_def.get('x_field', None)
self.y_field = provider_def.get('y_field', None)
self.options = provider_def.get('options')
self.id_field = provider_def.get('id_field')
self.uri_field = provider_def.get('uri_field')
self.x_field = provider_def.get('x_field')
self.y_field = provider_def.get('y_field')
self.time_field = provider_def.get('time_field')
self.title_field = provider_def.get('title_field')
self.properties = provider_def.get('properties', [])
@@ -212,7 +212,7 @@ class BaseProvider:
msg = None
LOGGER.debug('Loading data')
LOGGER.debug('Data: {}'.format(item))
LOGGER.debug(f'Data: {item}')
try:
json_data = json.loads(item)
except TypeError as err:
@@ -262,7 +262,7 @@ class BaseProvider:
return identifier2, json_data
def __repr__(self):
return '<BaseProvider> {}'.format(self.type)
return f'<BaseProvider> {self.type}'
class ProviderGenericError(Exception):
+3 -3
View File
@@ -2,7 +2,7 @@
#
# Authors: Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2021 Tom Kralidis
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -193,9 +193,9 @@ class CSVProvider(BaseProvider):
if item:
return item
else:
err = 'item {} not found'.format(identifier)
err = f'item {identifier} not found'
LOGGER.error(err)
raise ProviderItemNotFoundError(err)
def __repr__(self):
return '<CSVProvider> {}'.format(self.data)
return f'<CSVProvider> {self.data}'
+19 -21
View File
@@ -2,7 +2,7 @@
#
# Authors: Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2021 Tom Kralidis
# Copyright (c) 2022 Tom Kralidis
# Copyright (c) 2021 Francesco Bartoli
#
# Permission is hereby granted, free of charge, to any person
@@ -67,8 +67,8 @@ class ElasticsearchProvider(BaseProvider):
LOGGER.debug('Setting Elasticsearch properties')
self.is_gdal = False
LOGGER.debug('host: {}'.format(self.es_host))
LOGGER.debug('index: {}'.format(self.index_name))
LOGGER.debug(f'host: {self.es_host}')
LOGGER.debug(f'index: {self.index_name}')
self.type_name = 'FeatureCollection'
self.url_parsed = urlparse(self.es_host)
@@ -94,7 +94,7 @@ class ElasticsearchProvider(BaseProvider):
if self.url_parsed.path:
url_settings['url_prefix'] = self.url_parsed.path
LOGGER.debug('URL settings: {}'.format(url_settings))
LOGGER.debug(f'URL settings: {url_settings}')
LOGGER.debug('Connecting to Elasticsearch')
self.es = Elasticsearch([url_settings])
if not self.es.ping():
@@ -262,14 +262,14 @@ class ElasticsearchProvider(BaseProvider):
LOGGER.debug('processing sortby')
query['sort'] = []
for sort in sortby:
LOGGER.debug('processing sort object: {}'.format(sort))
LOGGER.debug(f'processing sort object: {sort}')
sp = sort['property']
if (self.fields[sp]['type'] == 'string'
and self.fields[sp].get('format') != 'date'):
LOGGER.debug('setting ES .raw on property')
sort_property = '{}.raw'.format(self.mask_prop(sp))
sort_property = f'{self.mask_prop(sp)}.raw'
else:
sort_property = self.mask_prop(sp)
@@ -318,7 +318,7 @@ class ElasticsearchProvider(BaseProvider):
try:
LOGGER.debug('querying Elasticsearch')
if filterq:
LOGGER.debug('adding cql object: {}'.format(filterq.json()))
LOGGER.debug(f'adding cql object: {filterq.json()}')
query = update_query(input_query=query, cql=filterq)
LOGGER.debug(json.dumps(query, indent=4))
@@ -378,12 +378,12 @@ class ElasticsearchProvider(BaseProvider):
"""
try:
LOGGER.debug('Fetching identifier {}'.format(identifier))
LOGGER.debug(f'Fetching identifier {identifier}')
result = self.es.get(index=self.index_name, id=identifier)
LOGGER.debug('Serializing feature')
feature_ = self.esdoc2geojson(result)
except exceptions.NotFoundError as err:
LOGGER.debug('Not found via ES id query: {}'.format(err))
LOGGER.debug(f'Not found via ES id query: {err}')
LOGGER.debug('Trying via a real query')
query = {
@@ -425,7 +425,7 @@ class ElasticsearchProvider(BaseProvider):
identifier, json_data = self._load_and_prepare_item(item)
LOGGER.debug('Inserting data with identifier {}'.format(identifier))
LOGGER.debug(f'Inserting data with identifier {identifier}')
_ = self.es.index(index=self.index_name, id=identifier, body=json_data)
LOGGER.debug('Item added')
@@ -441,7 +441,7 @@ class ElasticsearchProvider(BaseProvider):
:returns: `bool` of update result
"""
LOGGER.debug('Updating item {}'.format(identifier))
LOGGER.debug(f'Updating item {identifier}')
identifier, json_data = self._load_and_prepare_item(
item, identifier, raise_if_exists=False)
@@ -458,7 +458,7 @@ class ElasticsearchProvider(BaseProvider):
:returns: `bool` of deletion result
"""
LOGGER.debug('Deleting item {}'.format(identifier))
LOGGER.debug(f'Deleting item {identifier}')
_ = self.es.delete(index=self.index_name, id=identifier)
return True
@@ -528,13 +528,13 @@ class ElasticsearchProvider(BaseProvider):
if self.is_gdal:
return property_name
else:
return 'properties.{}'.format(property_name)
return f'properties.{property_name}'
def get_properties(self):
all_properties = []
LOGGER.debug('configured properties: {}'.format(self.properties))
LOGGER.debug('selected properties: {}'.format(self.select_properties))
LOGGER.debug(f'configured properties: {self.properties}')
LOGGER.debug(f'selected properties: {self.select_properties}')
if not self.properties and not self.select_properties:
all_properties = self.get_fields()
@@ -543,11 +543,11 @@ class ElasticsearchProvider(BaseProvider):
else:
all_properties = set(self.properties) | set(self.select_properties)
LOGGER.debug('resulting properties: {}'.format(all_properties))
LOGGER.debug(f'resulting properties: {all_properties}')
return all_properties
def __repr__(self):
return '<ElasticsearchProvider> {}'.format(self.data)
return f'<ElasticsearchProvider> {self.data}'
class ElasticsearchCatalogueProvider(ElasticsearchProvider):
@@ -588,7 +588,7 @@ class ElasticsearchCatalogueProvider(ElasticsearchProvider):
return records
def __repr__(self):
return '<ElasticsearchCatalogueProvider> {}'.format(self.data)
return f'<ElasticsearchCatalogueProvider> {self.data}'
class ESQueryBuilder:
@@ -769,7 +769,5 @@ def update_query(input_query: Dict, cql: CQLModel):
output_query = _build_query(query, cql)
s = s.query(output_query)
LOGGER.debug('Enhanced query: {}'.format(
json.dumps(s.to_dict())
))
LOGGER.debug(f'Enhanced query: {json.dumps(s.to_dict())}')
return s.to_dict()
+3 -4
View File
@@ -82,7 +82,7 @@ class ESRIServiceProvider(BaseProvider):
resp = self.get_response(self.data, params=params)
if resp.get('error') is not None:
msg = 'Connection error: {}'.format(resp['error']['message'])
msg = f"Connection error: {resp['error']['message']}"
LOGGER.error(msg)
raise ProviderConnectionError(msg)
@@ -283,9 +283,8 @@ class ESRIServiceProvider(BaseProvider):
if datetime_ is not None:
def esri_dt(dt):
return "TIMESTAMP '{}'".format(
format_datetime(dt, '%Y-%m-%d %H:%M:%S')
)
dt_ = format_datetime(dt, '%Y-%m-%d %H:%M:%S')
return f"TIMESTAMP '{dt_}'"
tf = self.time_field
if '/' in datetime_:
+15 -29
View File
@@ -2,7 +2,7 @@
#
# Authors: Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2020 Tom Kralidis
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -56,7 +56,7 @@ class FileSystemProvider(BaseProvider):
super().__init__(provider_def)
if not os.path.exists(self.data):
msg = 'Directory does not exist: {}'.format(self.data)
msg = f'Directory does not exist: {self.data}'
LOGGER.error(msg)
raise ProviderConnectionError(msg)
@@ -86,7 +86,7 @@ class FileSystemProvider(BaseProvider):
parentpath = urljoin(thispath, '.')
child_links.append({
'rel': 'parent',
'href': '{}?f=json'.format(parentpath),
'href': f'{parentpath}?f=json',
'type': 'application/json'
})
child_links.append({
@@ -102,7 +102,7 @@ class FileSystemProvider(BaseProvider):
content = {
'links': [{
'rel': 'root',
'href': '{}?f=json'.format(root_link),
'href': f'{root_link}?f=json',
'type': 'application/json'
}, {
'rel': 'root',
@@ -110,7 +110,7 @@ class FileSystemProvider(BaseProvider):
'type': 'text/html'
}, {
'rel': 'self',
'href': '{}?f=json'.format(thispath),
'href': f'{thispath}?f=json',
'type': 'application/json',
}, {
'rel': 'self',
@@ -128,14 +128,14 @@ class FileSystemProvider(BaseProvider):
else:
LOGGER.debug('Checking if path exists as file via file_types')
for ft in self.file_types:
tmp_path = '{}{}'.format(data_path, ft)
tmp_path = f'{data_path}{ft}'
if os.path.exists(tmp_path):
resource_type = 'file'
data_path = tmp_path
break
if resource_type is None:
msg = 'Resource does not exist: {}'.format(data_path)
msg = f'Resource does not exist: {data_path}'
LOGGER.error(msg)
raise ProviderNotFoundError(msg)
@@ -158,11 +158,6 @@ class FileSystemProvider(BaseProvider):
if os.path.isdir(fullpath):
newpath = os.path.join(baseurl, urlpath, dc)
# child_links.append({
# 'rel': 'child',
# 'href': '{}?f=json'.format(newpath),
# 'type': 'application/json'
# })
child_links.append({
'rel': 'child',
'href': newpath,
@@ -173,7 +168,7 @@ class FileSystemProvider(BaseProvider):
elif os.path.isfile(fullpath):
basename, extension = os.path.splitext(dc)
newpath = os.path.join(baseurl, urlpath, basename)
newpath2 = '{}{}'.format(newpath, extension)
newpath2 = f'{newpath}{extension}'
if extension in self.file_types:
fullpath = os.path.join(data_path, dc)
child_links.append({
@@ -184,14 +179,6 @@ class FileSystemProvider(BaseProvider):
'file:size': filesize,
'entry:type': 'Item'
})
# child_links.append({
# 'rel': 'item',
# 'title': get_path_basename(newpath2),
# 'href': newpath,
# 'type': 'text/html',
# 'created': filectime,
# 'file:size': filesize
# })
elif resource_type == 'file':
filename = os.path.basename(data_path)
@@ -199,7 +186,7 @@ class FileSystemProvider(BaseProvider):
id_ = os.path.splitext(filename)[0]
if urlpath:
filename = filename.replace(id_, '')
url = '{}/{}{}'.format(baseurl, urlpath, filename)
url = f'{baseurl}/{urlpath}{filename}'
filectime = file_modified_iso8601(data_path)
filesize = os.path.getsize(data_path)
@@ -225,7 +212,7 @@ class FileSystemProvider(BaseProvider):
return content
def __repr__(self):
return '<FileSystemProvider> {}'.format(self.data)
return f'<FileSystemProvider> {self.data}'
def _describe_file(filepath):
@@ -245,7 +232,7 @@ def _describe_file(filepath):
'properties': {}
}
mcf_file = '{}.yml'.format(os.path.splitext(filepath)[0])
mcf_file = f'{os.path.splitext(filepath)[0]}.yml'
if os.path.isfile(mcf_file):
try:
@@ -260,9 +247,9 @@ def _describe_file(filepath):
except ImportError:
LOGGER.debug('pygeometa not found')
except MCFReadError as err:
LOGGER.warning('MCF error: {}'.format(err))
LOGGER.warning(f'MCF error: {err}')
else:
LOGGER.debug('No mcf found at: {}'.format(mcf_file))
LOGGER.debug(f'No mcf found at: {mcf_file}')
if content['geometry'] is None and content['bbox'] is None:
try:
@@ -350,15 +337,14 @@ def _describe_file(filepath):
id_ = os.path.splitext(os.path.basename(filepath))[0]
content['assets'] = {}
for suffix in ['shx', 'dbf', 'prj']:
fullpath = '{}.{}'.format(
os.path.splitext(filepath)[0], suffix)
fullpath = f'{os.path.splitext(filepath)[0]}.{suffix}'
if os.path.exists(fullpath):
filectime = file_modified_iso8601(fullpath)
filesize = os.path.getsize(fullpath)
content['assets'][suffix] = {
'href': './{}.{}'.format(id_, suffix),
'href': f'./{id_}.{suffix}',
'created': filectime,
'file:size': filesize
}
+5 -5
View File
@@ -3,7 +3,7 @@
# Authors: Matthew Perry <perrygeo@gmail.com>
#
# Copyright (c) 2018 Matthew Perry
# Copyright (c) 2021 Tom Kralidis
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -84,7 +84,7 @@ class GeoJSONProvider(BaseProvider):
for f in data['features'][0]['properties'].keys():
fields[f] = {'type': 'string'}
else:
LOGGER.warning('File {} does not exist.'.format(self.data))
LOGGER.warning(f'File {self.data} does not exist.')
return fields
def _load(self, skip_geometry=None, properties=[], select_properties=[]):
@@ -99,7 +99,7 @@ class GeoJSONProvider(BaseProvider):
with open(self.data) as src:
data = json.loads(src.read())
else:
LOGGER.warning('File {} does not exist.'.format(self.data))
LOGGER.warning(f'File {self.data} does not exist.')
data = {
'type': 'FeatureCollection',
'features': []}
@@ -171,7 +171,7 @@ class GeoJSONProvider(BaseProvider):
if str(feature.get('id')) == identifier:
return feature
# default, no match
err = 'item {} not found'.format(identifier)
err = f'item {identifier} not found'
LOGGER.error(err)
raise ProviderItemNotFoundError(err)
@@ -230,4 +230,4 @@ class GeoJSONProvider(BaseProvider):
dst.write(json.dumps(all_data))
def __repr__(self):
return '<GeoJSONProvider> {}'.format(self.data)
return f'<GeoJSONProvider> {self.data}'
+8 -8
View File
@@ -77,7 +77,7 @@ class HateoasProvider(BaseProvider):
parentpath = urljoin(thispath, '.')
child_links.append({
'rel': 'parent',
'href': '{}?f=json'.format(parentpath),
'href': f'{parentpath}?f=json',
'type': 'application/json'
})
child_links.append({
@@ -93,7 +93,7 @@ class HateoasProvider(BaseProvider):
content = {
'links': [{
'rel': 'root',
'href': '{}?f=json'.format(root_link),
'href': f'{root_link}?f=json',
'type': 'application/json'
}, {
'rel': 'root',
@@ -101,7 +101,7 @@ class HateoasProvider(BaseProvider):
'type': 'text/html'
}, {
'rel': 'self',
'href': '{}?f=json'.format(thispath),
'href': f'{thispath}?f=json',
'type': 'application/json',
}, {
'rel': 'self',
@@ -113,19 +113,19 @@ class HateoasProvider(BaseProvider):
LOGGER.debug('Checking if path exists as Catalog, Collection or Asset')
try:
jsondata = _get_json_data('{}/catalog.json'.format(data_path))
jsondata = _get_json_data(f'{data_path}/catalog.json')
resource_type = 'Catalog'
except Exception:
try:
jsondata = _get_json_data('{}/collection.json'.format(data_path)) # noqa
jsondata = _get_json_data(f'{data_path}/collection.json')
resource_type = 'Collection'
except Exception:
try:
filename = os.path.basename(data_path)
jsondata = _get_json_data('{}/{}.json'.format(data_path, filename)) # noqa
jsondata = _get_json_data(f'{data_path}/{filename}.json')
resource_type = 'Assets'
except Exception:
msg = 'Resource does not exist: {}'.format(data_path)
msg = f'Resource does not exist: {data_path}'
LOGGER.error(msg)
raise ProviderNotFoundError(msg)
@@ -183,7 +183,7 @@ class HateoasProvider(BaseProvider):
return content
def __repr__(self):
return '<HateoasProvider> {}'.format(self.data)
return f'<HateoasProvider> {self.data}'
def _get_json_data(jsonpath):
+7 -7
View File
@@ -64,7 +64,7 @@ class MapScriptProvider(BaseProvider):
self.styles = []
self.default_format = 'png'
LOGGER.debug('MapScript version: {}'.format(mapscript.MS_VERSION))
LOGGER.debug(f'MapScript version: {mapscript.MS_VERSION}')
try:
LOGGER.debug('Creating new mapObj and layerObj')
@@ -91,7 +91,7 @@ class MapScriptProvider(BaseProvider):
self._layer.setProjection(self._epsg2projstring(self.crs))
LOGGER.debug('Layer projection: {}'.format(self._layer.getProjection())) # noqa
LOGGER.debug(f'Layer projection: {self._layer.getProjection()}')
if 'style' in self.options:
if self.options['style'].endswith(('xml', 'sld')):
@@ -135,7 +135,7 @@ class MapScriptProvider(BaseProvider):
try:
image_obj_format = IMAGE_FORMATS[format_]
except KeyError:
LOGGER.error('Bad output format: {}'.format(image_obj_format))
LOGGER.error(f'Bad output format: {image_obj_format}')
raise ProviderQueryError('Bad image format')
LOGGER.debug('Setting output map CRS')
@@ -167,8 +167,8 @@ class MapScriptProvider(BaseProvider):
if self.time_field is None:
LOGGER.debug('collection is not time enabled')
else:
fe = '{} = "{}"'.format(self.time_field, datetime_)
LOGGER.debug('Setting temporal filter: {}'.format(fe))
fe = f'{self.time_field} = "{datetime_}"'
LOGGER.debug(f'Setting temporal filter: {fe}')
self._layer.setFilter(fe)
LOGGER.debug('Setting output image properties')
@@ -185,7 +185,7 @@ class MapScriptProvider(BaseProvider):
self._map.setProjection(map_crs)
self._map.setConfigOption('MS_NONSQUARE', 'yes')
LOGGER.debug('Mapfile: {}'.format(self._map.convertToString()))
LOGGER.debug(f'Mapfile: {self._map.convertToString()}')
try:
img = self._map.draw()
except MapServerError as err:
@@ -209,4 +209,4 @@ class MapScriptProvider(BaseProvider):
return prj.ExportToProj4().strip()
def __repr__(self):
return '<MapScriptProvider> {}'.format(self.data)
return f'<MapScriptProvider> {self.data}'
+3 -3
View File
@@ -3,7 +3,7 @@
# Authors: Timo Tuunanen <timo.tuunanen@rdvelho.com>
#
# Copyright (c) 2019 Timo Tuunanen
# Copyright (c) 2021 Tom Kralidis
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -60,7 +60,7 @@ class MongoProvider(BaseProvider):
super().__init__(provider_def)
LOGGER.info('Mongo source config: {}'.format(self.data))
LOGGER.info(f'Mongo source config: {self.data}')
dbclient = MongoClient(self.data)
self.featuredb = dbclient.get_default_database()
@@ -167,7 +167,7 @@ class MongoProvider(BaseProvider):
if featurelist:
return featurelist[0]
else:
err = 'item {} not found'.format(identifier)
err = f'item {identifier} not found'
LOGGER.error(err)
raise ProviderItemNotFoundError(err)
+38 -63
View File
@@ -1,8 +1,10 @@
# =================================================================
#
# Authors: Francesco Bartoli <xbartolone@gmail.com>
# Authors: Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2020 Francesco Bartoli
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -60,23 +62,15 @@ class MVTProvider(BaseTileProvider):
super().__init__(provider_def)
if is_url(self.data):
url = urlparse(self.data)
baseurl = '{}://{}'.format(url.scheme, url.netloc)
baseurl = f'{url.scheme}://{url.netloc}'
param_type = '?f=mvt'
layer = '/{}'.format(self.get_layer())
layer = f'/{self.get_layer()}'
LOGGER.debug('Extracting layer name from URL')
LOGGER.debug('Layer: {}'.format(layer))
LOGGER.debug(f'Layer: {layer}')
tilepath = '{}/tiles'.format(layer)
servicepath = \
'{}/{{{}}}/{{{}}}/{{{}}}/{{{}}}{}'.format(
tilepath,
'tileMatrixSetId',
'tileMatrix',
'tileRow',
'tileCol',
param_type
)
tilepath = f'{layer}/tiles'
servicepath = f'{tilepath}/{{tileMatrixSetId}}/{{tileMatrix}}/{{tileRow}}/{{tileCol}}{param_type}' # noqa
self._service_url = url_join(baseurl, servicepath)
@@ -86,19 +80,19 @@ class MVTProvider(BaseTileProvider):
else:
data_path = Path(self.data)
if not data_path.exists():
msg = 'Service does not exist: {}'.format(self.data)
msg = f'Service does not exist: {self.data}'
LOGGER.error(msg)
raise ProviderConnectionError(msg)
self._service_url = data_path
metadata_path = data_path.joinpath('metadata.json')
if not metadata_path.exists():
msg = 'Service metadata does not exist: {}'.format(
metadata_path.name)
msg = f'Service metadata does not exist: {metadata_path.name}'
LOGGER.error(msg)
LOGGER.warning(msg)
self._service_metadata_url = metadata_path
def __repr__(self):
return '<MVTProvider> {}'.format(self.data)
return f'<MVTProvider> {self.data}'
@property
def service_url(self):
@@ -117,8 +111,7 @@ class MVTProvider(BaseTileProvider):
if ('/{z}/{x}/{y}' not in url.path and
'/{z}/{y}/{x}' not in url.path):
msg = 'This url template is not supported yet: {}'.format(
url.path)
msg = f'This url template is not supported yet: {url.path}'
LOGGER.error(msg)
raise ProviderConnectionError(msg)
@@ -158,18 +151,11 @@ class MVTProvider(BaseTileProvider):
"""
url = urlparse(self.data)
baseurl = baseurl or '{}://{}'.format(url.scheme, url.netloc)
baseurl = baseurl or f'{url.scheme}://{url.netloc}'
# @TODO: support multiple types
tile_type = tile_type or self.format_type
servicepath = \
servicepath or \
'{}/tiles/{{{}}}/{{{}}}/{{{}}}/{{{}}}{}'.format(
url.path.split('/{z}/{x}/{y}')[0],
'tileMatrixSetId',
'tileMatrix',
'tileRow',
'tileCol',
tile_type)
basepath = url.path.split('/{z}/{x}/{y}')[0]
servicepath = servicepath or f'{basepath}/tiles/{{tileMatrixSetId}}/{{tileMatrix}}/{{tileRow}}/{{tileCol}}{tile_type}' # noqa
if servicepath.startswith(baseurl):
self._service_url = servicepath
@@ -185,18 +171,18 @@ class MVTProvider(BaseTileProvider):
'type': 'application/json',
'rel': 'self',
'title': 'This collection as multi vector tilesets',
'href': '{}?f=json'.format(tile_matrix_set),
'href': f'{tile_matrix_set}?f=json'
},
{
'type': self.mimetype,
'rel': 'item',
'title': 'This collection as multi vector tiles',
'href': self.service_url,
'href': self.service_url
}, {
'type': 'application/json',
'rel': 'describedby',
'title': 'Collection metadata in TileJSON format',
'href': '{}?f=json'.format(self.service_metadata_url),
'href': f'{self.service_metadata_url}?f=json'
}
]
}
@@ -220,36 +206,32 @@ class MVTProvider(BaseTileProvider):
format_ = self.format_type
if is_url(self.data):
url = urlparse(self.data)
base_url = '{}://{}'.format(url.scheme, url.netloc)
base_url = f'{url.scheme}://{url.netloc}'
if url.query:
url_query = f'?{url.query}'
else:
url_query = ''
with requests.Session() as session:
session.get(base_url)
# There is a "." in the url path
if '.' in url.path:
resp = session.get(
'{base_url}/{lyr}/{z}/{y}/{x}.{f}{q}'.format(
base_url=base_url, lyr=layer,
z=z, y=y, x=x, f=format_, q="?" + url.query
if url.query else ''))
resp = session.get(f'{base_url}/{layer}/{z}/{y}/{x}.{f}{url_query}') # noqa
# There is no "." in the url )e.g. elasticsearch)
else:
resp = session.get(
'{base_url}/{lyr}/{z}/{y}/{x}{q}'.format(
base_url=base_url, lyr=layer,
z=z, y=y, x=x, q="?" + url.query
if url.query else ''))
resp = session.get(f'{base_url}/{layer}/{z}/{y}/{x}{url_query}') # noqa
resp.raise_for_status()
return resp.content
else:
if not isinstance(self.service_url, Path):
msg = 'Wrong data path configuration: {}'.format(
self.service_url)
msg = f'Wrong data path configuration: {self.service_url}'
LOGGER.error(msg)
raise ProviderConnectionError(msg)
else:
try:
with open(self.service_url.joinpath(
'{z}/{y}/{x}.{f}'.format(
z=z, y=y, x=x, f=format_)), 'rb') as tile:
service_url_path = self.service_url.joinpath(f'{z}/{y}/{x}.{format_}') # noqa
with open(service_url_path) as tile:
return tile.read()
except FileNotFoundError as err:
raise ProviderTileNotFoundError(err)
@@ -272,28 +254,25 @@ class MVTProvider(BaseTileProvider):
if is_url(self.data):
url = urlparse(self.data)
base_url = '{}://{}'.format(url.scheme, url.netloc)
base_url = f'{url.scheme}://{url.netloc}'
with requests.Session() as session:
session.get(base_url)
resp = session.get('{base_url}/{lyr}/metadata.json'.format(
base_url=base_url, lyr=layer))
resp = session.get(f'{base_url}/{layer}/metadata.json')
resp.raise_for_status()
metadata_json_content = resp.json()
else:
if not isinstance(self.service_metadata_url, Path):
msg = 'Wrong data path configuration: {}'.format(
self.service_metadata_url)
msg = f'Wrong data path configuration: {self.service_metadata_url}' # noqa
LOGGER.error(msg)
raise ProviderConnectionError(msg)
if self.service_metadata_url.exists():
with open(self.service_metadata_url, 'r') as md_file:
metadata_json_content = json.loads(md_file.read())
service_url = urljoin(
server_url,
'collections/{}/tiles/{}/{{{}}}/{{{}}}/{{{}}}{}'.format(
dataset, tileset, 'tileMatrix',
'tileRow', 'tileCol', '?f=mvt'))
f'collections/{dataset}/tiles/{tileset}/{{tileMatrix}}/{{tileRow}}/{{tileCol}}?f=mvt') # noqa
content = {}
if metadata_format == TilesMetadataFormat.TILEJSON:
@@ -304,8 +283,7 @@ class MVTProvider(BaseTileProvider):
metadata_json_content["json"])["vector_layers"]
return content.dict()
else:
msg = 'No tiles metadata json available: {}'.format(
self.service_metadata_url)
msg = f'No tiles metadata json available: {self.service_metadata_url}' # noqa
LOGGER.error(msg)
raise ProviderConnectionError(msg)
elif metadata_format == TilesMetadataFormat.CUSTOMJSON:
@@ -315,8 +293,7 @@ class MVTProvider(BaseTileProvider):
content['json'] = json.loads(metadata_json_content['json'])
return content
else:
msg = 'No custom JSON for tiles metadata available: {}'.format(
self.service_metadata_url)
msg = f'No custom JSON for tiles metadata available: {self.service_metadata_url}' # noqa
LOGGER.error(msg)
raise ProviderConnectionError(msg)
else:
@@ -336,9 +313,7 @@ class MVTProvider(BaseTileProvider):
links = []
service_url_link_type = "application/vnd.mapbox-vector-tile"
service_url_link_title = "{} vector tiles for {}".format(
tileset, layer
)
service_url_link_title = f'{tileset} vector tiles for {layer}'
service_url_link = LinkType(href=service_url, rel="item",
type=service_url_link_type,
title=service_url_link_title)
+23 -46
View File
@@ -5,7 +5,7 @@
#
# Copyright (c) 2019 Just van den Broecke
# Copyright (c) 2020 Francesco Bartoli
# Copyright (c) 2021 Tom Kralidis
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -172,7 +172,7 @@ class OGRProvider(BaseProvider):
self._load_source_helper(self.data_def['source_type'])
# Layer name is required
self.layer_name = provider_def.get('layer', None)
self.layer_name = provider_def.get('layer')
if not self.layer_name:
msg = 'Need explicit \'layer\' attr in provider config'
LOGGER.error(msg)
@@ -193,7 +193,7 @@ class OGRProvider(BaseProvider):
source_type = self.data_def['source_type']
self.driver = self.ogr.GetDriverByName(source_type)
if not self.driver:
msg = 'No Driver for Source: {}'.format(source_type)
msg = f'No Driver for Source: {source_type}'
LOGGER.error(msg)
raise Exception(msg)
if self.open_options:
@@ -206,8 +206,7 @@ class OGRProvider(BaseProvider):
LOGGER.error(err)
raise ProviderConnectionError(err)
except Exception:
msg = 'Ignore errors during the connection for Driver \
{}'.format(source_type)
msg = f'Ignore errors during the connection for Driver {source_type}' # noqa
LOGGER.error(msg)
self.conn = _ignore_gdal_error(
self.gdal, 'OpenEx', self.data_def['source'],
@@ -220,8 +219,7 @@ class OGRProvider(BaseProvider):
LOGGER.error(err)
raise ProviderConnectionError(err)
except Exception:
msg = 'Ignore errors during the connection for Driver \
{}'.format(source_type)
msg = f'Ignore errors during the connection for Driver {source_type}' # noqa
LOGGER.error(msg)
# ignore errors for ESRIJSON not having geometry member
# see https://github.com/OSGeo/gdal/commit/38b0feed67f80ded32be6c508323d862e1a14474 # noqa
@@ -323,12 +321,10 @@ class OGRProvider(BaseProvider):
if bbox:
LOGGER.debug('processing bbox parameter')
minx, miny, maxx, maxy = bbox
minx, miny, maxx, maxy = [float(b) for b in bbox]
wkt = "POLYGON (({minx} {miny},{minx} {maxy},{maxx} {maxy}," \
"{maxx} {miny},{minx} {miny}))".format(
minx=float(minx), miny=float(miny),
maxx=float(maxx), maxy=float(maxy))
wkt = f"POLYGON (({minx} {miny},{minx} {maxy},{maxx} {maxy}," \
"{maxx} {miny},{minx} {miny}))"
polygon = self.ogr.CreateGeometryFromWkt(wkt)
if self.transform_in:
@@ -343,10 +339,7 @@ class OGRProvider(BaseProvider):
LOGGER.debug('processing properties')
attribute_filter = ' and '.join(
map(
lambda x: '{} = \'{}\''.format(x[0], x[1]),
properties
)
map(lambda x: f'{x[0]} = \'{x[1]}\'', properties)
)
LOGGER.debug(attribute_filter)
@@ -389,11 +382,10 @@ class OGRProvider(BaseProvider):
"""
result = None
try:
LOGGER.debug('Fetching identifier {}'.format(identifier))
LOGGER.debug(f'Fetching identifier {identifier}')
layer = self._get_layer()
layer.SetAttributeFilter("{field} = '{id}'".format(
field=self.id_field, id=identifier))
layer.SetAttributeFilter(f"{self.id_field} = '{identifier}'")
ogr_feature = self._get_next_feature(layer, identifier)
result = self._ogr_feature_to_json(ogr_feature)
@@ -417,7 +409,7 @@ class OGRProvider(BaseProvider):
return result
def __repr__(self):
return '<OGRProvider> {}'.format(self.data)
return f'<OGRProvider> {self.data}'
def _load_source_helper(self, source_type):
"""
@@ -442,9 +434,9 @@ class OGRProvider(BaseProvider):
def _get_next_feature(self, layer, feature_id):
try:
if layer.GetFeatureCount() == 0:
LOGGER.error("item {} is not found".format(feature_id))
raise ProviderItemNotFoundError(
"item {} not found".format(feature_id))
msg = f"item {feature_id} is not found"
LOGGER.error(msg)
raise ProviderItemNotFoundError(msg)
# Ignore gdal error
next_feature = _ignore_gdal_error(layer, 'GetNextFeature')
if next_feature:
@@ -455,8 +447,7 @@ class OGRProvider(BaseProvider):
)
else:
raise RuntimeError(
"GDAL has returned a null feature for item {}".format(
feature_id))
f"GDAL has returned a null feature for item {feature_id}")
return next_feature
except RuntimeError as gdalerr:
LOGGER.error(self.gdal.GetLastErrorMsg())
@@ -476,11 +467,7 @@ class OGRProvider(BaseProvider):
self.id_field, json_feature['id']
)
except KeyError as err:
LOGGER.error(
"Cannot use configured id_field nor fid as id, err={}".format(
err
)
)
LOGGER.error(f"Cannot use configured id_field nor fid as id, err={err}") # noqa
return json_feature
@@ -578,8 +565,7 @@ class SourceHelper:
layer = self.provider.conn.GetLayerByName(self.provider.layer_name)
if not layer:
msg = 'Cannot get Layer {} from OGR Source'.\
format(self.provider.layer_name)
msg = f'Cannot get Layer {self.provider.layer_name} from OGR Source' # noqa
LOGGER.error(msg)
raise Exception(msg)
@@ -635,8 +621,7 @@ class CommonSourceHelper(SourceHelper):
try:
self.provider.conn.ReleaseResultSet(self.result_set)
except Exception as err:
msg = 'ReleaseResultSet exception for Layer {}'.format(
self.provider.layer_name)
msg = f'ReleaseResultSet exception for Layer {self.provider.layer_name}' # noqa
LOGGER.error(msg, err)
finally:
self.result_set = None
@@ -671,10 +656,7 @@ class CommonSourceHelper(SourceHelper):
self.close()
sql = 'SELECT * FROM "{ds_name}" LIMIT {limit} OFFSET {offset}'.format(
ds_name=self.provider.layer_name,
limit=self.limit,
offset=self.offset)
sql = f'SELECT * FROM "{self.provider.layer_name}" LIMIT {self.limit} OFFSET {self.offset}' # noqa
self.result_set = self.provider.conn.ExecuteSQL(sql)
# Reset since needs to be set each time explicitly
@@ -682,8 +664,7 @@ class CommonSourceHelper(SourceHelper):
self.limit = -1
if not self.result_set:
msg = 'Cannot get Layer {} via ExecuteSQL'.format(
self.provider.layer_name)
msg = f'Cannot get Layer {self.provider.layer_name} via ExecuteSQL'
LOGGER.error(msg)
raise Exception(msg)
@@ -735,10 +716,7 @@ class ESRIJSONHelper(CommonSourceHelper):
self.close()
sql = "SELECT * FROM {ds_name} LIMIT {limit} OFFSET {offset}".format(
ds_name=self.provider.layer_name,
limit=self.limit,
offset=self.offset)
sql = f"SELECT * FROM {self.provider.layer_name} LIMIT {self.limit} OFFSET {self.offset}" # noqa
self.result_set = self.provider.conn.ExecuteSQL(sql)
# Reset since needs to be set each time explicitly
@@ -746,8 +724,7 @@ class ESRIJSONHelper(CommonSourceHelper):
self.limit = -1
if not self.result_set:
msg = 'Cannot get Layer {} via ExecuteSQL'.format(
self.provider.layer_name)
msg = f'Cannot get Layer {self.provider.layer_name} via ExecuteSQL'
LOGGER.error(msg)
raise Exception(msg)
+6 -6
View File
@@ -7,7 +7,7 @@
# Colin Blackburn <colb@bgs.ac.uk>
#
# Copyright (c) 2018 Jorge Samuel Mendes de Jesus
# Copyright (c) 2021 Tom Kralidis
# Copyright (c) 2022 Tom Kralidis
# Copyright (c) 2022 John A Stevenson and Colin Blackburn
#
# Permission is hereby granted, free of charge, to any person
@@ -90,15 +90,15 @@ class PostgreSQLProvider(BaseProvider):
self.id_field = provider_def['id_field']
self.geom = provider_def.get('geom_field', 'geom')
LOGGER.debug('Name: {}'.format(self.name))
LOGGER.debug('Table: {}'.format(self.table))
LOGGER.debug('ID field: {}'.format(self.id_field))
LOGGER.debug('Geometry field: {}'.format(self.geom))
LOGGER.debug(f'Name: {self.name}')
LOGGER.debug(f'Table: {self.table}')
LOGGER.debug(f'ID field: {self.id_field}')
LOGGER.debug(f'Geometry field: {self.geom}')
# Read table information from database
self._store_db_parameters(provider_def['data'])
self._engine, self.table_model = self._get_engine_and_table_model()
LOGGER.debug('DB connection: {}'.format(repr(self._engine.url)))
LOGGER.debug(f'DB connection: {repr(self._engine.url)}')
self.fields = self.get_fields()
def query(self, offset=0, limit=10, resulttype='results',
+9 -15
View File
@@ -2,7 +2,7 @@
#
# Authors: Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2020 Tom Kralidis
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -132,7 +132,7 @@ class RasterioProvider(BaseProvider):
for i, dtype, nodataval in zip(self._data.indexes, self._data.dtypes,
self._data.nodatavals):
LOGGER.debug('Determing rangetype for band {}'.format(i))
LOGGER.debug(f'Determing rangetype for band {i}')
name, units = None, None
if self._data.units[i-1] is None:
@@ -146,12 +146,11 @@ class RasterioProvider(BaseProvider):
'type': 'Quantity',
'name': name,
'encodingInfo': {
'dataType': 'http://www.opengis.net/def/dataType/OGC/0/{}'.format(dtype) # noqa
'dataType': f'http://www.opengis.net/def/dataType/OGC/0/{dtype}' # noqa
},
'nodata': nodataval,
'uom': {
'id': 'http://www.opengis.net/def/uom/UCUM/{}'.format(
units),
'id': f'http://www.opengis.net/def/uom/UCUM/{units}',
'type': 'UnitReference',
'code': units
},
@@ -176,7 +175,7 @@ class RasterioProvider(BaseProvider):
"""
bands = properties
LOGGER.debug('Bands: {}, subsets: {}'.format(bands, subsets))
LOGGER.debug(f'Bands: {bands}, subsets: {subsets}')
args = {
'indexes': None
@@ -227,10 +226,8 @@ class RasterioProvider(BaseProvider):
minx2, miny2 = t.transform(minx, miny)
maxx2, maxy2 = t.transform(maxx, maxy)
LOGGER.debug('Source coordinates: {}'.format(
[minx, miny, maxx, maxy]))
LOGGER.debug('Destination coordinates: {}'.format(
[minx2, miny2, maxx2, maxy2]))
LOGGER.debug(f'Source coordinates: {minx}, {miny}, {maxx}, {maxy}') # noqa
LOGGER.debug(f'Destination: {minx2}, {miny2}, {maxx2}, {maxy2}') # noqa
shapes = [{
'type': 'Polygon',
@@ -371,7 +368,7 @@ class RasterioProvider(BaseProvider):
else:
bands_select = metadata['bands']
LOGGER.debug('bands selected: {}'.format(bands_select))
LOGGER.debug(f'bands selected: {bands_select}')
for bs in bands_select:
pm = _get_parameter_metadata(
self._data.profile['driver'], self._data.tags(bs))
@@ -437,10 +434,7 @@ class RasterioProvider(BaseProvider):
if self._data.crs is not None:
if self._data.crs.is_projected:
properties['bbox_crs'] = '{}/{}'.format(
'http://www.opengis.net/def/crs/OGC/1.3/',
self._data.crs.to_epsg())
properties['bbox_crs'] = f'http://www.opengis.net/def/crs/OGC/1.3/{self._data.crs.to_epsg()}' # noqa
properties['x_axis_label'] = 'x'
properties['y_axis_label'] = 'y'
properties['bbox_units'] = self._data.crs.linear_units
+11 -18
View File
@@ -1,8 +1,10 @@
# =================================================================
#
# Authors: Benjamin Webb <benjamin.miller.webb@gmail.com>
# Authors: Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2021 Benjamin Webb
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -119,8 +121,8 @@ class SensorThingsProvider(BaseProvider):
try:
results = r.json()['value'][0]
except JSONDecodeError as err:
LOGGER.error('Entity {} error: {}'.format(self.entity, err))
LOGGER.error('Bad url response at {}'.format(r.url))
LOGGER.error(f'Entity {self.entity} error: {err}')
LOGGER.error(f'Bad url response at {r.url}')
raise ProviderQueryError(err)
for (n, v) in results.items():
@@ -262,7 +264,7 @@ class SensorThingsProvider(BaseProvider):
hits_ = 1 if identifier else min(limit, response.get('@iot.count'))
while len(v) < hits_:
LOGGER.debug('Fetching next set of values')
next_ = response.get('@iot.nextLink', None)
next_ = response.get('@iot.nextLink')
if next_ is None:
break
else:
@@ -415,7 +417,6 @@ class SensorThingsProvider(BaseProvider):
for k, v in entity.items():
# Create intra links
path_ = 'collections/{}/items/{}'
ks = f'{k}s'
if self.uri_field is not None and k in ['properties']:
uri = v.get(self.uri_field, '')
@@ -426,14 +427,9 @@ class SensorThingsProvider(BaseProvider):
v[i] = _v['properties'][self._linkables[k]['u']]
continue
for i, _v in enumerate(v):
id = _v[self.id_field]
id = f"'{id}'" if isinstance(id, str) else str(id)
v[i] = url_join(
self._rel_link,
path_.format(
self._linkables[k]['n'], id
)
)
id_ = _v[self.id_field]
id_ = f"'{id_}'" if isinstance(id_, str) else str(id_)
v[i] = url_join(self._rel_link, f"collections/{self._linkables[k]['n']}/items/{id_}") # noqa
elif ks in self._linkables.keys():
if self._linkables[ks]['u'] != '':
@@ -443,13 +439,10 @@ class SensorThingsProvider(BaseProvider):
id = f"'{id}'" if isinstance(id, str) else str(id)
entity[k] = url_join(
self._rel_link,
path_.format(
self._linkables[ks]['n'], id
)
)
f"collections/{self._linkables[ks]['n']}/items/{id_}")
# Make properties block
if entity.get('properties', None):
if entity.get('properties'):
entity.update(entity.pop('properties'))
if keys:
@@ -465,4 +458,4 @@ class SensorThingsProvider(BaseProvider):
return entity
def __repr__(self):
return '<SensorThingsProvider> {}, {}'.format(self.data, self.entity)
return f'<SensorThingsProvider> {self.data}, {self.entity}'
+4 -2
View File
@@ -1,8 +1,10 @@
# =================================================================
#
# Authors: Benjamin Webb <bwebb@lincolninst.edu>
# Authors: Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2022 Benjamin Webb
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -79,7 +81,7 @@ class SODAServiceProvider(BaseProvider):
[dataset] = self.client.datasets(ids=[self.resource_id])
resource = dataset['resource']
except json.decoder.JSONDecodeError as err:
LOGGER.error('Bad response at {}'.format(self.data))
LOGGER.error(f'Bad response at {self.data}')
raise ProviderConnectionError(err)
fields = self.properties or resource[FIELD_NAME]
@@ -269,4 +271,4 @@ class SODAServiceProvider(BaseProvider):
return int(response['count'])
def __repr__(self):
return '<SODAServiceProvider> {}'.format(self.data)
return f'<SODAServiceProvider> {self.data}'
+28 -34
View File
@@ -5,7 +5,7 @@
# Francesco Bartoli <xbartolone@gmail.com>
#
# Copyright (c) 2018 Jorge Samuel Mendes de Jesus
# Copyright (c) 2021 Tom Kralidis
# Copyright (c) 2022 Tom Kralidis
# Copyright (c) 2020 Francesco Bartoli
#
# Permission is hereby granted, free of charge, to any person
@@ -68,10 +68,10 @@ class SQLiteGPKGProvider(BaseProvider):
self.geom_col = None
LOGGER.debug('Setting SQLite properties:')
LOGGER.debug('Data source: {}'.format(self.data))
LOGGER.debug('Name: {}'.format(self.name))
LOGGER.debug('ID_field: {}'.format(self.id_field))
LOGGER.debug('Table: {}'.format(self.table))
LOGGER.debug(f'Data source: {self.data}')
LOGGER.debug(f'Name: {self.name}')
LOGGER.debug(f'ID_field: {self.id_field}')
LOGGER.debug(f'Table: {self.table}')
self.cursor = self.__load()
@@ -89,7 +89,7 @@ class SQLiteGPKGProvider(BaseProvider):
if not self.fields:
results = self.cursor.execute(
'PRAGMA table_info({})'.format(self.table)).fetchall()
f'PRAGMA table_info({self.table})').fetchall()
for item in results:
self.fields[item['name']] = {'type': item['type']}
@@ -116,14 +116,13 @@ class SQLiteGPKGProvider(BaseProvider):
if properties:
where_clause += " AND ".join(
["{}=?".format(k) for k, v in properties])
[f"{k}=?" for k, v in properties])
where_values += where_values + tuple((v for k, v in properties))
if bbox:
if properties:
where_clause += " AND "
where_clause += " Intersects({}, \
BuildMbr(?,?,?,?)) ".format(self.geom_col)
where_clause += f" Intersects({self.geom_col}, BuildMbr(?,?,?,?)) "
where_values += tuple(bbox)
# WHERE continent=? <class 'tuple'>: ('Europe',)
return where_clause, where_values
@@ -144,7 +143,7 @@ class SQLiteGPKGProvider(BaseProvider):
'type': 'Feature'
}
feature["geometry"] = json.loads(
rd.pop('AsGeoJSON({})'.format(self.geom_col))
rd.pop(f'AsGeoJSON({self.geom_col})')
)
if skip_geometry:
feature["geometry"] = None
@@ -185,7 +184,7 @@ class SQLiteGPKGProvider(BaseProvider):
try:
conn.enable_load_extension(True)
except AttributeError as err:
LOGGER.error('Extension loading not enabled: {}'.format(err))
LOGGER.error(f'Extension loading not enabled: {err}')
raise ProviderConnectionError()
conn.row_factory = sqlite3.Row
@@ -193,10 +192,9 @@ class SQLiteGPKGProvider(BaseProvider):
# conn.set_trace_callback(LOGGER.debug)
cursor = conn.cursor()
try:
cursor.execute("SELECT load_extension('{}')".format(
SPATIALITE_EXTENSION))
cursor.execute(f"SELECT load_extension('{SPATIALITE_EXTENSION}')")
except sqlite3.OperationalError as err:
LOGGER.error('Extension loading error: {}'.format(err))
LOGGER.error(f'Extension loading error: {err}')
raise ProviderConnectionError()
result = cursor.fetchall()
@@ -229,10 +227,10 @@ class SQLiteGPKGProvider(BaseProvider):
self.geom_col = "geometry"
try:
cursor.execute('PRAGMA table_info({})'.format(self.table))
cursor.execute(f'PRAGMA table_info({self.table})')
result = cursor.fetchall()
except sqlite3.OperationalError:
LOGGER.error('Couldnt find table: {}'.format(self.table))
LOGGER.error(f'Couldnt find table: {self.table}')
raise ProviderConnectionError()
try:
@@ -245,11 +243,10 @@ class SQLiteGPKGProvider(BaseProvider):
self.columns = [item[1] for item in result if item[1]
not in [self.geom_col, self.geom_col.upper()]]
self.columns = ','.join(self.columns)+',AsGeoJSON({})'.format(
self.geom_col)
self.columns = ','.join(self.columns)+f',AsGeoJSON({self.geom_col})'
if self.application_id:
self.table = "vgpkg_{}".format(self.table)
self.table = f"vgpkg_{self.table}"
return cursor
@@ -282,23 +279,21 @@ class SQLiteGPKGProvider(BaseProvider):
if resulttype == 'hits':
sql_query = "SELECT COUNT(*) as hits FROM {} {} ".format(
self.table, where_clause)
sql_query = f"SELECT COUNT(*) as hits FROM {self.table} {where_clause} " # noqa
res = self.cursor.execute(sql_query, where_values)
hits = res.fetchone()["hits"]
return self.__response_feature_hits(hits)
sql_query = "SELECT DISTINCT {} from \
{} {} limit ? offset ?".format(
self.columns, self.table, where_clause)
sql_query = f"SELECT DISTINCT {self.columns} from \
{self.table} {where_clause} limit ? offset ?"
end_index = offset + limit
LOGGER.debug('SQL Query: {}'.format(sql_query))
LOGGER.debug('Start Index: {}'.format(offset))
LOGGER.debug('End Index: {}'.format(end_index))
LOGGER.debug(f'SQL Query: {sql_query}')
LOGGER.debug(f'Start Index: {offset}')
LOGGER.debug(f'End Index: {end_index}')
row_data = self.cursor.execute(
sql_query, where_values + (limit, offset))
@@ -326,12 +321,11 @@ class SQLiteGPKGProvider(BaseProvider):
LOGGER.debug('Get item from SQLite/GPKG')
sql_query = 'SELECT {} FROM \
{} WHERE {}==?;'.format(
self.columns, self.table, self.id_field)
sql_query = f'SELECT {self.columns} FROM \
{self.table} WHERE {self.id_field}==?;'
LOGGER.debug('SQL Query: {}'.format(sql_query))
LOGGER.debug('Identifier: {}'.format(identifier))
LOGGER.debug(f'SQL Query: {sql_query}')
LOGGER.debug(f'Identifier: {identifier}')
row_data = self.cursor.execute(sql_query, (identifier, )).fetchone()
@@ -339,9 +333,9 @@ class SQLiteGPKGProvider(BaseProvider):
if feature:
return feature
else:
err = 'item {} not found'.format(identifier)
err = f'item {identifier} not found'
LOGGER.error(err)
raise ProviderItemNotFoundError(err)
def __repr__(self):
return '<SQLiteGPKGProvider> {}, {}'.format(self.data, self.table)
return f'<SQLiteGPKGProvider> {self.data}, {self.table}'
+3 -1
View File
@@ -1,8 +1,10 @@
# =================================================================
#
# Authors: Francesco Bartoli <xbartolone@gmail.com>
# Authors: Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2020 Francesco Bartoli
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -50,7 +52,7 @@ class BaseTileProvider:
self.data = provider_def['data']
self.format_type = provider_def['format']['name']
self.mimetype = provider_def['format']['mimetype']
self.options = provider_def.get('options', None)
self.options = provider_def.get('options')
self.fields = {}
def get_layer(self):
+23 -23
View File
@@ -58,7 +58,7 @@ class TinyDBCatalogueProvider(BaseProvider):
super().__init__(provider_def)
LOGGER.debug('Connecting to TinyDB db at {}'.format(self.data))
LOGGER.debug(f'Connecting to TinyDB db at {self.data}')
if not os.path.exists(self.data):
msg = 'TinyDB does not exist'
@@ -117,7 +117,7 @@ class TinyDBCatalogueProvider(BaseProvider):
"""
Q = Query()
LOGGER.debug('Query initiated: {}'.format(Q))
LOGGER.debug(f'Query initiated: {Q}')
QUERY = []
@@ -133,7 +133,7 @@ class TinyDBCatalogueProvider(BaseProvider):
if bbox:
LOGGER.debug('processing bbox parameter')
bbox_as_string = ','.join(str(s) for s in bbox)
QUERY.append("Q.properties.extent.spatial.bbox.test(bbox_intersects, '{}')".format(bbox_as_string)) # noqa
QUERY.append(f"Q.properties.extent.spatial.bbox.test(bbox_intersects, '{bbox_as_string}')") # noqa
if datetime_ is not None:
LOGGER.debug('processing datetime parameter')
@@ -146,31 +146,31 @@ class TinyDBCatalogueProvider(BaseProvider):
time_begin, time_end = datetime_.split('/')
if time_begin != '..':
QUERY.append("(Q.properties[self.time_field]>='{}')".format(time_begin)) # noqa
QUERY.append(f"(Q.properties[self.time_field]>='{time_begin}')") # noqa
if time_end != '..':
QUERY.append("(Q.properties[self.time_field]<='{}')".format(time_end)) # noqa
QUERY.append(f"(Q.properties[self.time_field]<='{time_end}')") # noqa
else: # time instant
LOGGER.debug('detected time instant')
QUERY.append("(Q.properties[self.time_field]=='{}')".format(datetime_)) # noqa
QUERY.append(f"(Q.properties[self.time_field]=='{datetime_}')") # noqa
if properties:
LOGGER.debug('processing properties')
for prop in properties:
QUERY.append("(Q.properties['{}']=='{}')".format(*prop))
QUERY.append(f"(Q.properties['{prop[0]}']=='{prop[1]}')")
if q is not None:
for t in q.split():
QUERY.append("(Q.properties['_metadata-anytext'].search('{}', flags=re.IGNORECASE))".format(t)) # noqa
QUERY.append(f"(Q.properties['_metadata-anytext'].search('{t}', flags=re.IGNORECASE))") # noqa
QUERY_STRING = '&'.join(QUERY)
LOGGER.debug('QUERY_STRING: {}'.format(QUERY_STRING))
SEARCH_STRING = 'self.db.search({})'.format(QUERY_STRING)
LOGGER.debug('SEARCH_STRING: {}'.format(SEARCH_STRING))
LOGGER.debug(f'QUERY_STRING: {QUERY_STRING}')
SEARCH_STRING = f'self.db.search({QUERY_STRING})'
LOGGER.debug(f'SEARCH_STRING: {SEARCH_STRING}')
LOGGER.debug('querying database')
if len(QUERY) > 0:
LOGGER.debug('running eval on {}'.format(SEARCH_STRING))
LOGGER.debug(f'running eval on {SEARCH_STRING}')
results = eval(SEARCH_STRING)
else:
results = self.db.all()
@@ -185,11 +185,11 @@ class TinyDBCatalogueProvider(BaseProvider):
try:
del r['properties'][e]
except KeyError:
LOGGER.debug('Missing excluded property {}'.format(e))
LOGGER.debug(f'Missing excluded property {e}')
len_results = len(results)
LOGGER.debug('Results found: {}'.format(len_results))
LOGGER.debug(f'Results found: {len_results}')
if len_results > limit:
returned = limit
@@ -221,7 +221,7 @@ class TinyDBCatalogueProvider(BaseProvider):
:returns: `dict` of single record
"""
LOGGER.debug('Fetching identifier {}'.format(identifier))
LOGGER.debug(f'Fetching identifier {identifier}')
record = self.db.get(Query().id == identifier)
@@ -232,7 +232,7 @@ class TinyDBCatalogueProvider(BaseProvider):
try:
del record['properties'][e]
except KeyError:
LOGGER.debug('Missing excluded property {}'.format(e))
LOGGER.debug(f'Missing excluded property {e}')
return record
@@ -256,10 +256,10 @@ class TinyDBCatalogueProvider(BaseProvider):
LOGGER.debug('Missing title and description')
json_data['properties']['_metadata_anytext'] = ''
LOGGER.debug('Inserting data with identifier {}'.format(identifier))
LOGGER.debug(f'Inserting data with identifier {identifier}')
result = self.db.insert(json_data)
LOGGER.debug('Item added with internal id {}'.format(result))
LOGGER.debug(f'Item added with internal id {result}')
return identifier
@@ -273,7 +273,7 @@ class TinyDBCatalogueProvider(BaseProvider):
:returns: `bool` of update result
"""
LOGGER.debug('Updating item {}'.format(identifier))
LOGGER.debug(f'Updating item {identifier}')
identifier, json_data = self._load_and_prepare_item(
item, identifier, raise_if_exists=False)
self.db.update(json_data, where('id') == identifier)
@@ -289,7 +289,7 @@ class TinyDBCatalogueProvider(BaseProvider):
:returns: `bool` of deletion result
"""
LOGGER.debug('Deleting item {}'.format(identifier))
LOGGER.debug(f'Deleting item {identifier}')
self.db.remove(where('id') == identifier)
return True
@@ -307,7 +307,7 @@ class TinyDBCatalogueProvider(BaseProvider):
return True
def __repr__(self):
return '<TinyDBCatalogueProvider> {}'.format(self.data)
return f'<TinyDBCatalogueProvider> {self.data}'
def bbox_intersects(record_bbox, input_bbox):
@@ -323,8 +323,8 @@ def bbox_intersects(record_bbox, input_bbox):
bbox1 = record_bbox[0]
bbox2 = [float(c) for c in input_bbox.split(',')]
LOGGER.debug('Record bbox: {}'.format(bbox1))
LOGGER.debug('Input bbox: {}'.format(bbox2))
LOGGER.debug(f'Record bbox: {bbox1}')
LOGGER.debug(f'Input bbox: {bbox2}')
# any point in bbox1 should be in bbox2
bbox_tests = [
+4 -4
View File
@@ -61,7 +61,7 @@ class WMSFacadeProvider(BaseProvider):
BaseProvider.__init__(self, provider_def)
LOGGER.debug('pyproj version: {}'.format(pyproj.__version__))
LOGGER.debug(f'pyproj version: {pyproj.__version__}')
def query(self, style=None, bbox=[-180, -90, 180, 90], width=500,
height=300, crs=4326, datetime_=None, transparent=True,
@@ -90,7 +90,7 @@ class WMSFacadeProvider(BaseProvider):
[bbox[1], bbox[0], bbox[3], bbox[2]])
else:
LOGGER.debug('Reprojecting coordinates')
LOGGER.debug('Output CRS: {}'.format(CRS_CODES[crs]))
LOGGER.debug(f'Output CRS: {CRS_CODES[crs]}')
src_crs = pyproj.CRS.from_string('epsg:4326')
dest_crs = pyproj.CRS.from_string(CRS_CODES[crs])
@@ -128,7 +128,7 @@ class WMSFacadeProvider(BaseProvider):
else:
request_url = '?'.join([self.data, urlencode(params)])
LOGGER.debug('WMS 1.3.0 request url: {}'.format(request_url))
LOGGER.debug(f'WMS 1.3.0 request url: {request_url}')
response = requests.get(request_url)
@@ -140,4 +140,4 @@ class WMSFacadeProvider(BaseProvider):
return response.content
def __repr__(self):
return '<MapScriptProvider> {}'.format(self.data)
return f'<MapScriptProvider> {self.data}'
+25 -21
View File
@@ -1,8 +1,10 @@
# =================================================================
#
# Authors: Gregory Petrochenkov <gpetrochenkov@usgs.gov>
# Authors: Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2020 Gregory Petrochenkov
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -153,7 +155,7 @@ class XarrayProvider(BaseProvider):
}
for name, var in self._data.variables.items():
LOGGER.debug('Determining rangetype for {}'.format(name))
LOGGER.debug(f'Determining rangetype for {name}')
desc, units = None, None
if len(var.shape) >= 3:
@@ -167,12 +169,11 @@ class XarrayProvider(BaseProvider):
'type': 'Quantity',
'name': var.attrs.get('long_name') or desc,
'encodingInfo': {
'dataType': 'http://www.opengis.net/def/dataType/OGC/0/{}'.format(str(var.dtype)) # noqa
'dataType': f'http://www.opengis.net/def/dataType/OGC/0/{var.dtype}' # noqa
},
'nodata': 'null',
'uom': {
'id': 'http://www.opengis.net/def/uom/UCUM/{}'.format(
units),
'id': f'http://www.opengis.net/def/uom/UCUM/{units}',
'type': 'UnitReference',
'code': units
},
@@ -219,7 +220,7 @@ class XarrayProvider(BaseProvider):
query_params = {}
for key, val in subsets.items():
LOGGER.debug('Processing subset: {}'.format(key))
LOGGER.debug(f'Processing subset: {key}')
if data.coords[key].values[0] > data.coords[key].values[-1]:
LOGGER.debug('Reversing slicing from high to low')
query_params[key] = slice(val[1], val[0])
@@ -257,7 +258,7 @@ class XarrayProvider(BaseProvider):
else:
query_params[self.time_field] = datetime_
LOGGER.debug('Query parameters: {}'.format(query_params))
LOGGER.debug(f'Query parameters: {query_params}')
try:
data = data.sel(query_params)
except Exception as err:
@@ -329,7 +330,7 @@ class XarrayProvider(BaseProvider):
tmp_max = data.coords[self.y_field].values
if tmp_min > tmp_max:
LOGGER.debug('Reversing direction of {}'.format(self.y_field))
LOGGER.debug(f'Reversing direction of {self.y_field}')
miny = tmp_max
maxy = tmp_min
@@ -471,9 +472,7 @@ class XarrayProvider(BaseProvider):
}
if 'crs' in self._data.variables.keys():
properties['bbox_crs'] = '{}/{}'.format(
'http://www.opengis.net/def/crs/OGC/1.3/',
self._data.crs.epsg_code)
properties['bbox_crs'] = f'http://www.opengis.net/def/crs/OGC/1.3/{self._data.crs.epsg_code}' # noqa
properties['inverse_flattening'] = self._data.crs.\
inverse_flattening
@@ -502,11 +501,11 @@ class XarrayProvider(BaseProvider):
return {
'id': name,
'description': attrs.get('long_name', None),
'unit_label': attrs.get('units', None),
'unit_symbol': attrs.get('units', None),
'description': attrs.get('long_name'),
'unit_label': attrs.get('units'),
'unit_symbol': attrs.get('units'),
'observed_property_id': name,
'observed_property_name': attrs.get('long_name', None)
'observed_property_name': attrs.get('long_name')
}
def get_time_resolution(self):
@@ -542,7 +541,7 @@ class XarrayProvider(BaseProvider):
'seconds': int(ms_difference / 1000) % 60
}
times = ['{} {}'.format(val, key) for key, val
times = [f'{val} {key}' for key, val
in time_dict.items() if val > 0]
return ', '.join(times)
@@ -605,12 +604,17 @@ def _get_zarr_data(data):
"""
tmp_dir = tempfile.TemporaryDirectory().name
data.to_zarr('{}zarr.zarr'.format(tmp_dir), mode='w')
with zipfile.ZipFile('{}zarr.zarr.zip'.format(tmp_dir),
'w', zipfile.ZIP_DEFLATED) as zipf:
_zip_dir('{}zarr.zarr'.format(tmp_dir), zipf, os.getcwd())
zip_file = open('{}zarr.zarr.zip'.format(tmp_dir), 'rb')
return zip_file.read()
zarr_data_filename = f'{tmp_dir}zarr.zarr'
zarr_zip_filename = f'{tmp_dir}zarr.zarr.zip'
data.to_zarr(zarr_data_filename, mode='w')
with zipfile.ZipFile(zarr_zip_filename, 'w', zipfile.ZIP_DEFLATED) as zipf: # noqa
_zip_dir(zarr_data_filename, zipf, os.getcwd())
with open(zarr_zip_filename, 'rb') as fh:
return fh.read()
def _convert_float32_to_float64(data):
+6 -6
View File
@@ -2,7 +2,7 @@
#
# Authors: Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2020 Tom Kralidis
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -77,14 +77,14 @@ class XarrayEDRProvider(BaseEDRProvider, XarrayProvider):
query_params = {}
LOGGER.debug('Query parameters: {}'.format(kwargs))
LOGGER.debug(f'Query parameters: {kwargs}')
LOGGER.debug('Query type: {}'.format(kwargs.get('query_type')))
LOGGER.debug(f"Query type: {kwargs.get('query_type')}")
wkt = kwargs.get('wkt')
if wkt is not None:
LOGGER.debug('Processing WKT')
LOGGER.debug('Geometry type: {}'.format(wkt.type))
LOGGER.debug(f'Geometry type: {wkt.type}')
if wkt.type == 'Point':
query_params[self._coverage_properties['x_axis_label']] = wkt.x
query_params[self._coverage_properties['y_axis_label']] = wkt.y
@@ -102,13 +102,13 @@ class XarrayEDRProvider(BaseEDRProvider, XarrayProvider):
# example of fetching instance passed
# TODO: apply accordingly
instance = kwargs.get('instance')
LOGGER.debug('instance: {}'.format(instance))
LOGGER.debug(f'instance: {instance}')
datetime_ = kwargs.get('datetime_')
if datetime_ is not None:
query_params[self._coverage_properties['time_axis_label']] = datetime_ # noqa
LOGGER.debug('query parameters: {}'.format(query_params))
LOGGER.debug(f'query parameters: {query_params}')
try:
if select_properties:
+15 -7
View File
@@ -33,6 +33,7 @@
""" Starlette module providing the route paths to the api"""
import os
from pathlib import Path
import click
@@ -53,10 +54,14 @@ if 'PYGEOAPI_CONFIG' not in os.environ:
with open(os.environ.get('PYGEOAPI_CONFIG'), encoding='utf8') as fh:
CONFIG = yaml_load(fh)
STATIC_DIR = '{}{}static'.format(os.path.dirname(os.path.realpath(__file__)),
os.sep)
if 'templates' in CONFIG['server']:
STATIC_DIR = CONFIG['server']['templates'].get('static', STATIC_DIR)
p = Path(__file__)
STATIC_DIR = Path(p).parent.resolve() / 'static'
try:
STATIC_DIR = Path(CONFIG['server']['templates']['static'])
except KeyError:
pass
app = Starlette()
app.mount('/static', StaticFiles(directory=STATIC_DIR))
@@ -66,11 +71,14 @@ if CONFIG['server'].get('cors', False):
from starlette.middleware.cors import CORSMiddleware
app.add_middleware(CORSMiddleware, allow_origins=['*'])
OGC_SCHEMAS_LOCATION = CONFIG['server'].get('ogc_schemas_location', None)
try:
OGC_SCHEMAS_LOCATION = Path(CONFIG['server']['ogc_schemas_location'])
except KeyError:
OGC_SCHEMAS_LOCATION = None
if (OGC_SCHEMAS_LOCATION is not None and
not OGC_SCHEMAS_LOCATION.startswith('http')):
if not os.path.exists(OGC_SCHEMAS_LOCATION):
not OGC_SCHEMAS_LOCATION.name.startswith('http')):
if not OGC_SCHEMAS_LOCATION.exists():
raise RuntimeError('OGC schemas misconfigured')
app.mount('/schemas', StaticFiles(directory=OGC_SCHEMAS_LOCATION))
+2 -2
View File
@@ -13,8 +13,8 @@
<!--[if lt IE 9]>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.js"></script>
<![endif]-->
<meta name="twitter:image:src" content="{% block thumb %}{% endblock %}{% if not self.thumb() %}{{'{}/static/img/logo.png'.format(config['server']['url']) }}{% endif %}" />
<meta property="og:image" content="{{ self.thumb() }}{% if not self.thumb() %}{{'{}/static/img/logo.png'.format(config['server']['url']) }}{% endif %}" />
<meta name="twitter:image:src" content="{% block thumb %}{% endblock %}{% if not self.thumb() %}{{ config['server']['url'] }}/static/img/logo.png{% endif %}" />
<meta property="og:image" content="{{ self.thumb() }}{% if not self.thumb() %}{{ config['server']['url'] }}/static/img/logo.png{% endif %}" />
<meta name="twitter:site" content="{{ config['metadata']['identification']['title'] }}" />
<meta property="og:site_name" content="{{ config['metadata']['identification']['title'] }}" />
<meta name="twitter:card" content="summary_large_image" />
@@ -1,5 +1,5 @@
{% extends "_base.html" %}
{% set ptitle = data['properties'][data['title_field']] or ("Item {}".format(data['id'])) %}
{% set ptitle = data['properties'][data['title_field']] or "Item " + data['id'] %}
{% block desc %}{{ data.get('properties',{}).get('description', {}) | string | truncate(250) }}{% endblock %}
{% block tags %}{{ data['properties'].get('themes', [{}])[0].get('concepts', []) | join(',') }}{% endblock %}
{# Optionally renders an img element, otherwise standard value or link rendering #}
+47 -42
View File
@@ -2,7 +2,7 @@
#
# Authors: Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2020 Tom Kralidis
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -34,12 +34,13 @@ from typing import List
from datetime import date, datetime, time
from decimal import Decimal
from enum import Enum
import io
import json
import logging
import mimetypes
import os
from pathlib import Path
import re
from typing import Any, IO, Union
from urllib.request import urlopen
from urllib.parse import urlparse
@@ -59,14 +60,13 @@ LOGGER = logging.getLogger(__name__)
DATETIME_FORMAT = '%Y-%m-%dT%H:%M:%S.%fZ'
TEMPLATES = '{}{}templates'.format(os.path.dirname(
os.path.realpath(__file__)), os.sep)
TEMPLATES = Path(__file__).parent.resolve() / 'templates'
mimetypes.add_type('text/plain', '.yaml')
mimetypes.add_type('text/plain', '.yml')
def dategetter(date_property, collection):
def dategetter(date_property: str, collection: dict) -> str:
"""
Attempts to obtain a date value from a collection.
@@ -77,7 +77,7 @@ def dategetter(date_property, collection):
for an open interval using null)
"""
value = collection.get(date_property, None)
value = collection.get(date_property)
if value is None:
return None
@@ -85,7 +85,7 @@ def dategetter(date_property, collection):
return value.isoformat()
def get_typed_value(value):
def get_typed_value(value: str) -> Union[float, int, str]:
"""
Derive true type from data value
@@ -107,7 +107,7 @@ def get_typed_value(value):
return value2
def yaml_load(fh):
def yaml_load(fh: IO) -> dict:
"""
serializes a YAML files into a pyyaml object
@@ -123,7 +123,7 @@ def yaml_load(fh):
def path_constructor(loader, node):
env_var = path_matcher.match(node.value).group(1)
if env_var not in os.environ:
msg = 'Undefined environment variable {} in config'.format(env_var)
msg = f'Undefined environment variable {env_var} in config'
raise EnvironmentError(msg)
return get_typed_value(os.path.expandvars(node.value))
@@ -136,7 +136,7 @@ def yaml_load(fh):
return yaml.load(fh, Loader=EnvVarLoader)
def str2bool(value):
def str2bool(value: Union[bool, str]) -> bool:
"""
helper function to return Python boolean
type (source: https://stackoverflow.com/a/715468)
@@ -156,7 +156,7 @@ def str2bool(value):
return value2
def to_json(dict_, pretty=False):
def to_json(dict_: dict, pretty: bool = False) -> str:
"""
Serialize dict to json
@@ -175,7 +175,7 @@ def to_json(dict_, pretty=False):
indent=indent)
def format_datetime(value, format_=DATETIME_FORMAT):
def format_datetime(value: str, format_: str = DATETIME_FORMAT) -> str:
"""
Parse datetime as ISO 8601 string; re-present it in particular format
for display in HTML
@@ -192,7 +192,7 @@ def format_datetime(value, format_=DATETIME_FORMAT):
return dateutil.parser.isoparse(value).strftime(format_)
def file_modified_iso8601(filepath):
def file_modified_iso8601(filepath: Path) -> str:
"""
Provide a file's ctime in ISO8601
@@ -205,7 +205,7 @@ def file_modified_iso8601(filepath):
os.path.getctime(filepath)).strftime('%Y-%m-%dT%H:%M:%SZ')
def human_size(nbytes):
def human_size(nbytes: int) -> str:
"""
Provides human readable file size
@@ -230,12 +230,12 @@ def human_size(nbytes):
elif suffixes[i] == 'B':
return nbytes
else:
f = '{:.1f}'.format(nbytes).rstrip('0').rstrip('.')
f = f'{nbytes:.1f}'.rstrip('0').rstrip('.')
return '{}{}'.format(f, suffixes[i])
return f'{f}{suffixes[i]}'
def format_duration(start, end=None):
def format_duration(start: str, end: str = None) -> str:
"""
Parse a start and (optional) end datetime as ISO 8601 strings, calculate
the difference, and return that duration as a string.
@@ -245,6 +245,7 @@ def format_duration(start, end=None):
:returns: string
"""
if not isinstance(start, str) or not start.strip():
return ''
end = end or start
@@ -252,7 +253,7 @@ def format_duration(start, end=None):
return str(duration)
def get_path_basename(urlpath):
def get_path_basename(urlpath: str) -> str:
"""
Helper function to derive file basename
@@ -261,14 +262,16 @@ def get_path_basename(urlpath):
:returns: string of basename of URL path
"""
return os.path.basename(urlpath)
return Path(urlpath).name
def json_serial(obj):
def json_serial(obj: Any) -> str:
"""
helper function to convert to JSON non-default
types (source: https://stackoverflow.com/a/22238613)
:param obj: `object` to be evaluated
:returns: JSON non-default type to `str`
"""
@@ -290,17 +293,19 @@ def json_serial(obj):
elif isinstance(obj, l10n.Locale):
return l10n.locale2str(obj)
msg = '{} type {} not serializable'.format(obj, type(obj))
msg = f'{obj} type {type(obj)} not serializable'
LOGGER.error(msg)
raise TypeError(msg)
def is_url(urlstring):
def is_url(urlstring: str) -> bool:
"""
Validation function that determines whether a candidate URL should be
considered a URI. No remote resource is obtained; this does not check
the existence of any remote resource.
:param urlstring: `str` to be evaluated as candidate URL.
:returns: `bool` of whether the URL looks like a URL.
"""
try:
@@ -310,7 +315,8 @@ def is_url(urlstring):
return False
def render_j2_template(config, template, data, locale_=None):
def render_j2_template(config: dict, template: Path,
data: dict, locale_: str = None) -> str:
"""
render Jinja2 template
@@ -330,13 +336,13 @@ def render_j2_template(config, template, data, locale_=None):
'jinja2.ext.autoescape'],
autoescape=select_autoescape(['html', 'xml']))
custom_templates = True
LOGGER.debug('using custom templates: {}'.format(templates_path))
LOGGER.debug(f'using custom templates: {templates_path}')
except (KeyError, TypeError):
env = Environment(loader=FileSystemLoader(TEMPLATES),
extensions=['jinja2.ext.i18n',
'jinja2.ext.autoescape'],
autoescape=select_autoescape(['html', 'xml']))
LOGGER.debug('using default templates: {}'.format(TEMPLATES))
LOGGER.debug(f'using default templates: {TEMPLATES}')
env.filters['to_json'] = to_json
env.filters['format_datetime'] = format_datetime
@@ -373,7 +379,7 @@ def render_j2_template(config, template, data, locale_=None):
data=data, locale=locale_, version=__version__)
def get_mimetype(filename):
def get_mimetype(filename: str) -> str:
"""
helper function to return MIME type of a given file
@@ -385,7 +391,7 @@ def get_mimetype(filename):
return mimetypes.guess_type(filename)[0]
def get_breadcrumbs(urlpath):
def get_breadcrumbs(urlpath: str) -> list:
"""
helper function to make breadcrumbs from a URL path
@@ -412,7 +418,7 @@ def get_breadcrumbs(urlpath):
return links
def filter_dict_by_key_value(dict_, key, value):
def filter_dict_by_key_value(dict_: dict, key: str, value: str) -> dict:
"""
helper function to filter a dict by a dict key
@@ -426,7 +432,7 @@ def filter_dict_by_key_value(dict_, key, value):
return {k: v for (k, v) in dict_.items() if v[key] == value}
def filter_providers_by_type(providers, type):
def filter_providers_by_type(providers: list, type: str) -> dict:
"""
helper function to filter a list of providers by type
@@ -437,20 +443,20 @@ def filter_providers_by_type(providers, type):
"""
providers_ = {provider['type']: provider for provider in providers}
return providers_.get(type, None)
return providers_.get(type)
def get_provider_by_type(providers, provider_type):
def get_provider_by_type(providers: list, provider_type: str) -> dict:
"""
helper function to load a provider by a provider type
:param providers: ``list`` of providers
:param provider_type: type of provider (feature)
:param provider_type: type of provider (e.g. feature)
:returns: provider based on type
"""
LOGGER.debug('Searching for provider type {}'.format(provider_type))
LOGGER.debug(f'Searching for provider type {provider_type}')
try:
p = (next(d for i, d in enumerate(providers)
if d['type'] == provider_type))
@@ -460,7 +466,7 @@ def get_provider_by_type(providers, provider_type):
return p
def get_provider_default(providers):
def get_provider_default(providers: list) -> dict:
"""
helper function to get a resource's default provider
@@ -477,7 +483,7 @@ def get_provider_default(providers):
LOGGER.debug('no default provider type. Returning first provider')
default = providers[0]
LOGGER.debug('Default provider: {}'.format(default['type']))
LOGGER.debug(f"Default provider: {default['type']}")
return default
@@ -494,17 +500,16 @@ class JobStatus(Enum):
dismissed = 'dismissed'
def read_data(path):
def read_data(path: Union[Path, str]) -> Union[bytes, str]:
"""
helper function to read data (file or networrk)
"""
LOGGER.debug('Attempting to read {}'.format(path))
scheme = urlparse(path).scheme
LOGGER.debug(f'Attempting to read {path}')
if scheme in ['', 'file']:
if isinstance(path, Path) or not path.startswith(('http', 's3')):
LOGGER.debug('local file on disk')
with io.open(path, 'rb') as fh:
with Path(path).open('rb') as fh:
return fh.read()
else:
LOGGER.debug('network file')
@@ -512,7 +517,7 @@ def read_data(path):
return r.read()
def url_join(*parts):
def url_join(*parts: list) -> str:
"""
helper function to join a URL from a number of parts/fragments.
Implemented because urllib.parse.urljoin strips subpaths from
@@ -528,7 +533,7 @@ def url_join(*parts):
return '/'.join([p.strip().strip('/') for p in parts])
def get_envelope(coords_list: List[List[float]]):
def get_envelope(coords_list: List[List[float]]) -> list:
"""
helper function to get the envelope for a given coordinates
list through the Shapely API.
+16 -11
View File
@@ -2,7 +2,7 @@
#
# Authors: Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2018 Tom Kralidis
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -27,8 +27,8 @@
#
# =================================================================
import io
import os
from pathlib import Path
import re
from setuptools import Command, find_packages, setup
import shutil
@@ -58,7 +58,7 @@ class PyCleanBuild(Command):
for file_ in remove_files:
try:
os.remove(file_)
Path(file_).unlink()
except OSError:
pass
@@ -68,9 +68,9 @@ class PyCleanBuild(Command):
except OSError:
pass
for file_ in os.listdir('..'):
if file_.endswith(('.deb', '.build', '.changes')):
os.remove('../{}'.format(file_))
for file_ in [Path(p) for p in os.listdir('..')]:
if file_.stem in ['.deb', '.build', '.changes']:
os.unlink(Path('..', file_))
class PyTest(Command):
@@ -107,16 +107,20 @@ class PyCoverage(Command):
raise SystemExit(errno)
def read(filename, encoding='utf-8'):
def read(filename):
"""read file contents"""
full_path = os.path.join(os.path.dirname(__file__), filename)
with io.open(full_path, encoding=encoding) as fh:
fullpath = Path(__file__).resolve().parent / filename
with fullpath.open() as fh:
contents = fh.read().strip()
return contents
def get_package_version():
"""get version from top-level package init"""
version_file = read('pygeoapi/__init__.py')
version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]",
version_file, re.M)
@@ -129,8 +133,9 @@ LONG_DESCRIPTION = read('README.md')
DESCRIPTION = 'pygeoapi provides an API to geospatial data'
if os.path.exists('MANIFEST'):
os.unlink('MANIFEST')
MANIFEST = Path('MANIFEST')
if MANIFEST.exists():
MANIFEST.unlink()
setup(
name='pygeoapi',
+4 -4
View File
@@ -2,7 +2,7 @@
#
# Authors: Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2020 Tom Kralidis
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -28,17 +28,17 @@
# =================================================================
import json
import os
from pathlib import Path
import sys
from elasticsearch import Elasticsearch, helpers
es = Elasticsearch()
if len(sys.argv) < 3:
print('Usage: {} <path/to/data.geojson> <id-field>'.format(sys.argv[0]))
print(f'Usage: {sys.argv[0]} <path/to/data.geojson> <id-field>')
sys.exit(1)
index_name = os.path.splitext(os.path.basename(sys.argv[1]))[0].lower()
index_name = Path(sys.argv[1]).stem.lower()
id_field = sys.argv[2]
if es.indices.exists(index_name):
+3 -1
View File
@@ -1,8 +1,10 @@
# =================================================================
#
# Authors: Timo Tuunanen <timo.tuunanen@rdvelho.com>
# Authors: Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2019 Timo Tuunanen
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -37,7 +39,7 @@ mongodb = 'testdb'
mongocollection = 'testplaces'
if len(sys.argv) == 1:
print('Usage: {} <path/to/data.geojson>'.format(sys.argv[0]))
print(f'Usage: {sys.argv[0]} <path/to/data.geojson>')
sys.exit(1)
myclient = MongoClient(monogourl)
+7 -5
View File
@@ -1,8 +1,10 @@
# =================================================================
#
# Authors: Benjamin Webb <benjamin.miller.webb@gmail.com>
# Authors: Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2021 Benjamin Webb
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -27,17 +29,17 @@
#
# =================================================================
from pathlib import Path
import requests
import sys
import json
import os.path
url = 'http://localhost:8080/FROST-Server/v1.1/Datastreams'
data_url = 'https://raw.githubusercontent.com/webb-ben/data/main/'
def main(path_):
filename = os.path.basename(path_)
def main(path_: Path):
filename = path_.name
r = requests.get(f'{data_url}{filename}')
data = r.json().get('value')
for v in data:
@@ -65,7 +67,7 @@ def clean(dirty_dict):
if __name__ == "__main__":
if len(sys.argv) == 1:
print('Usage: {} <path/to/data.geojson>'.format(sys.argv[0]))
print(f'Usage: {sys.argv[0]} <path/to/data.geojson>')
sys.exit(1)
main(sys.argv[1])
main(Path(sys.argv[1]))
+8 -10
View File
@@ -28,8 +28,7 @@
# =================================================================
from datetime import datetime
from glob import glob
import os
from pathlib import Path
import sys
from typing import Union
@@ -39,14 +38,14 @@ from tinydb import TinyDB
if len(sys.argv) < 3:
print('Usage: {} <path/to/xml-files> <output.db>'.format(sys.argv[0]))
print(f'Usage: {sys.argv[0]} <path/to/xml-files> <output.db>')
sys.exit(1)
xml_dir = sys.argv[1]
index_name = sys.argv[2]
xml_dir = Path(sys.argv[1])
index_name = Path(sys.argv[2])
if os.path.exists(index_name):
os.remove(index_name)
if index_name.exists():
index_name.unlink()
db = TinyDB(index_name)
@@ -124,7 +123,7 @@ def get_anytext(bag: Union[list, str]) -> str:
return ' '.join(text_bag)
for xml_file in glob('{}/*.xml'.format(xml_dir)):
for xml_file in xml_dir.glob('*.xml'):
m = MD_Metadata(etree.parse(xml_file))
_raw_metadata = m.xml.decode('utf-8')
@@ -231,7 +230,6 @@ for xml_file in glob('{}/*.xml'.format(xml_dir)):
try:
res = db.insert(json_record)
print('Metadata record {} loaded with internal id {}'.format(
xml_file, res))
print(f'Metadata record {xml_file} loaded with internal id {res}')
except Exception as err:
print(err)
+20 -18
View File
@@ -1,8 +1,10 @@
# =================================================================
#
# Authors: Francesco Bartoli <xbartolone@gmail.com>
# Authors: Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2020 Francesco Bartoli
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -103,10 +105,10 @@ def test_query_hits_vsicurl(config_vsicurl_csv):
p = OGRProvider(config_vsicurl_csv)
feature_collection = p.query(resulttype='hits')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 0
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is not None
assert hits > 100
@@ -118,10 +120,10 @@ def test_query_bbox_hits_vsicurl(config_vsicurl_csv):
feature_collection = p.query(
bbox=[10.497565, 41.520355, 15.111823, 43.308645],
resulttype='hits')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 0
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is not None
assert hits > 1
@@ -131,15 +133,15 @@ def test_query_with_limit_vsicurl(config_vsicurl_csv):
p = OGRProvider(config_vsicurl_csv)
feature_collection = p.query(limit=2, resulttype='results')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 2
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
@@ -148,17 +150,17 @@ def test_query_with_offset_vsicurl(config_vsicurl_csv):
p = OGRProvider(config_vsicurl_csv)
feature_collection = p.query(offset=20, limit=10, resulttype='results')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 10
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
assert feature['id'] == 21
assert 'Veneto' in properties['denominazione_regione']
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
@@ -169,8 +171,8 @@ def test_query_with_property_vsicurl(config_vsicurl_csv):
feature_collection = p.query(
offset=20, limit=10, resulttype='results',
properties=[('denominazione_regione', 'Lazio')])
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 10
for feature in features:
assert 'Lazio' in feature['properties']['denominazione_regione']
+19 -17
View File
@@ -1,8 +1,10 @@
# =================================================================
#
# Authors: Francesco Bartoli <xbartolone@gmail.com>
# Authors: Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2020 Francesco Bartoli
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -75,7 +77,7 @@ def config_random_id(config_ArcGIS_ESRIJSON):
p = OGRProvider(config_ArcGIS_ESRIJSON)
# Get bunch of features to randomly have an id
feature_collection = p.query(offset=0, limit=10, resulttype='results')
features = feature_collection.get('features', None)
features = feature_collection.get('features')
features_list = []
for feature in features:
features_list.append(feature['id'])
@@ -117,10 +119,10 @@ def test_query_hits_agol(config_ArcGIS_ESRIJSON):
p = OGRProvider(config_ArcGIS_ESRIJSON)
feature_collection = p.query(resulttype='hits')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 0
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is not None
assert hits > 100
@@ -133,10 +135,10 @@ def test_query_bbox_hits_agol(config_ArcGIS_ESRIJSON):
bbox=[-9822165.181154, 5112669.004249,
-9807305.104750, 5133712.297986],
resulttype='hits')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 0
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is not None
assert hits > 1
@@ -146,15 +148,15 @@ def test_query_with_limit_agol(config_ArcGIS_ESRIJSON):
p = OGRProvider(config_ArcGIS_ESRIJSON)
feature_collection = p.query(limit=2, resulttype='results')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 2
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
@@ -163,14 +165,14 @@ def test_query_with_offset(config_ArcGIS_ESRIJSON):
p = OGRProvider(config_ArcGIS_ESRIJSON)
feature_collection = p.query(offset=10, limit=10, resulttype='results')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 10
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
assert properties['fulladdr'] is not None
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
+60 -58
View File
@@ -1,8 +1,10 @@
# =================================================================
#
# Authors: Just van den Broecke <justb4@gmail.com>
# Authors: Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2019 Just van den Broecke
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -64,13 +66,13 @@ def test_query(config_poi_portugal):
p = OGRProvider(config_poi_portugal)
feature_collection = p.query()
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert features is not None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
@@ -164,10 +166,10 @@ def test_query_hits_28992(config_gpkg_28992):
p = OGRProvider(config_gpkg_28992)
feature_collection = p.query(resulttype='hits')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 0
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is not None
assert hits == 2481
@@ -177,10 +179,10 @@ def test_query_hits_4326(config_gpkg_4326):
p = OGRProvider(config_gpkg_4326)
feature_collection = p.query(resulttype='hits')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 0
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is not None
assert hits == 2481
@@ -193,10 +195,10 @@ def test_query_bbox_hits_4326(config_gpkg_4326):
# bbox=[120000, 480000, 124000, 487000], resulttype='hits')
feature_collection = p.query(
bbox=[5.763409, 52.060197, 5.769256, 52.061976], resulttype='hits')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 0
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is not None
assert hits == 1
@@ -210,10 +212,10 @@ def test_query_bbox_hits_28992(config_gpkg_28992):
feature_collection = p.query(
bbox=[5.763409, 52.060197, 5.769256, 52.061976], resulttype='hits')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 0
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is not None
assert hits == 1
@@ -226,15 +228,15 @@ def test_query_bbox_28992(config_gpkg_28992):
# bbox=[180800, 452500, 181200, 452700], resulttype='results')
feature_collection = p.query(
bbox=(5.763409, 52.060197, 5.769256, 52.061976), resulttype='results')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 1
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
assert properties['straatnaam'] == 'Planken Wambuisweg'
@@ -247,15 +249,15 @@ def test_query_bbox_4326(config_gpkg_4326):
# bbox=[180800, 452500, 181200, 452700], resulttype='results')
feature_collection = p.query(
bbox=(5.763409, 52.060197, 5.769256, 52.061976), resulttype='results')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 1
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
assert properties['straatnaam'] == 'Planken Wambuisweg'
@@ -265,15 +267,15 @@ def test_query_with_limit_28992(config_gpkg_28992):
p = OGRProvider(config_gpkg_28992)
feature_collection = p.query(limit=2, resulttype='results')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 2
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
@@ -282,15 +284,15 @@ def test_query_with_limit_4326(config_gpkg_4326):
p = OGRProvider(config_gpkg_4326)
feature_collection = p.query(limit=5, resulttype='results')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 5
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
@@ -299,17 +301,17 @@ def test_query_with_offset_28992(config_gpkg_28992):
p = OGRProvider(config_gpkg_28992)
feature_collection = p.query(offset=20, limit=5, resulttype='results')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 5
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
assert feature['id'] == 'inspireadressen.1744969'
assert 'Egypte' in properties['straatnaam']
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
@@ -318,17 +320,17 @@ def test_query_with_offset_4326(config_gpkg_4326):
p = OGRProvider(config_gpkg_4326)
feature_collection = p.query(offset=20, limit=5, resulttype='results')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 5
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
assert feature['id'] == 'inspireadressen.1744969'
assert 'Egypte' in properties['straatnaam']
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
@@ -340,15 +342,15 @@ def test_query_bbox_with_offset_28992(config_gpkg_28992):
offset=10, limit=5,
bbox=(5.742, 52.053, 5.773, 52.098),
resulttype='results')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 5
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
assert properties['straatnaam'] == 'Buurtweg'
assert properties['huisnummer'] == '4'
@@ -362,15 +364,15 @@ def test_query_bbox_with_offset_4326(config_gpkg_4326):
offset=1, limit=5,
bbox=(5.742, 52.053, 5.773, 52.098),
resulttype='results')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 5
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
assert properties['straatnaam'] == 'Egypte'
assert properties['huisnummer'] == '6'
@@ -387,8 +389,8 @@ def test_query_with_property_filtering(config_gpkg_4326):
]
)
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) > 1
for feature in features:
+56 -54
View File
@@ -1,8 +1,10 @@
# =================================================================
#
# Authors: Just van den Broecke <justb4@gmail.com>
# Authors: Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2019 Just van den Broecke
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -120,10 +122,10 @@ def test_query_hits_28992(config_shapefile_28992):
p = OGRProvider(config_shapefile_28992)
feature_collection = p.query(resulttype='hits')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 0
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is not None
assert hits == 2481
@@ -133,10 +135,10 @@ def test_query_hits_4326(config_shapefile_4326):
p = OGRProvider(config_shapefile_4326)
feature_collection = p.query(resulttype='hits')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 0
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is not None
assert hits == 2481
@@ -149,10 +151,10 @@ def test_query_bbox_hits_4326(config_shapefile_4326):
# bbox=[120000, 480000, 124000, 487000], resulttype='hits')
feature_collection = p.query(
bbox=[5.763409, 52.060197, 5.769256, 52.061976], resulttype='hits')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 0
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is not None
assert hits == 1
@@ -166,10 +168,10 @@ def test_query_bbox_hits_28992(config_shapefile_28992):
feature_collection = p.query(
bbox=[5.763409, 52.060197, 5.769256, 52.061976], resulttype='hits')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 0
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is not None
assert hits == 1
@@ -182,15 +184,15 @@ def test_query_bbox_28992(config_shapefile_28992):
# bbox=[180800, 452500, 181200, 452700], resulttype='results')
feature_collection = p.query(
bbox=(5.763409, 52.060197, 5.769256, 52.061976), resulttype='results')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 1
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
assert properties['straatnaam'] == 'Planken Wambuisweg'
@@ -203,15 +205,15 @@ def test_query_bbox_4326(config_shapefile_4326):
# bbox=[180800, 452500, 181200, 452700], resulttype='results')
feature_collection = p.query(
bbox=(5.763409, 52.060197, 5.769256, 52.061976), resulttype='results')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 1
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
assert properties['straatnaam'] == 'Planken Wambuisweg'
@@ -221,15 +223,15 @@ def test_query_with_limit_28992(config_shapefile_28992):
p = OGRProvider(config_shapefile_28992)
feature_collection = p.query(limit=2, resulttype='results')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 2
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
@@ -238,15 +240,15 @@ def test_query_with_limit_4326(config_shapefile_4326):
p = OGRProvider(config_shapefile_4326)
feature_collection = p.query(limit=5, resulttype='results')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 5
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
@@ -255,17 +257,17 @@ def test_query_with_offset_28992(config_shapefile_28992):
p = OGRProvider(config_shapefile_28992)
feature_collection = p.query(offset=20, limit=5, resulttype='results')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 5
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
assert feature['id'] == 'inspireadressen.1744969'
assert 'Egypte' in properties['straatnaam']
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
@@ -274,17 +276,17 @@ def test_query_with_offset_4326(config_shapefile_4326):
p = OGRProvider(config_shapefile_4326)
feature_collection = p.query(offset=20, limit=5, resulttype='results')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 5
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
assert feature['id'] == 'inspireadressen.1744969'
assert 'Egypte' in properties['straatnaam']
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
@@ -296,15 +298,15 @@ def test_query_bbox_with_offset_28992(config_shapefile_28992):
offset=10, limit=5,
bbox=(5.742, 52.053, 5.773, 52.098),
resulttype='results')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 5
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
assert properties['straatnaam'] == 'Buurtweg'
assert properties['huisnummer'] == '4'
@@ -318,15 +320,15 @@ def test_query_bbox_with_offset_4326(config_shapefile_4326):
offset=1, limit=5,
bbox=(5.742, 52.053, 5.773, 52.098),
resulttype='results')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 5
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
assert properties['straatnaam'] == 'Egypte'
assert properties['huisnummer'] == '6'
@@ -343,8 +345,8 @@ def test_query_with_property_filtering(config_shapefile_4326):
]
)
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) > 1
for feature in features:
+30 -28
View File
@@ -1,8 +1,10 @@
# =================================================================
#
# Authors: Just van den Broecke <justb4@gmail.com>
# Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2019 Just van den Broecke
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -92,10 +94,10 @@ def test_query_hits_4326(config_sqlite_4326):
p = OGRProvider(config_sqlite_4326)
feature_collection = p.query(resulttype='hits')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 0
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is not None
assert hits == 2481
@@ -108,10 +110,10 @@ def test_query_bbox_hits_4326(config_sqlite_4326):
# bbox=[120000, 480000, 124000, 487000], resulttype='hits')
feature_collection = p.query(
bbox=[5.763409, 52.060197, 5.769256, 52.061976], resulttype='hits')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 0
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is not None
assert hits == 1
@@ -124,15 +126,15 @@ def test_query_bbox_4326(config_sqlite_4326):
# bbox=[180800, 452500, 181200, 452700], resulttype='results')
feature_collection = p.query(
bbox=(5.763409, 52.060197, 5.769256, 52.061976), resulttype='results')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 1
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
assert properties['straatnaam'] == 'Planken Wambuisweg'
@@ -142,15 +144,15 @@ def test_query_with_limit_4326(config_sqlite_4326):
p = OGRProvider(config_sqlite_4326)
feature_collection = p.query(limit=5, resulttype='results')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 5
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
@@ -159,17 +161,17 @@ def test_query_with_offset_4326(config_sqlite_4326):
p = OGRProvider(config_sqlite_4326)
feature_collection = p.query(offset=20, limit=5, resulttype='results')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 5
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
assert feature['id'] == 'inspireadressen.1744969'
assert 'Egypte' in properties['straatnaam']
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
@@ -181,15 +183,15 @@ def test_query_bbox_with_offset_4326(config_sqlite_4326):
offset=1, limit=50,
bbox=(5.742, 52.053, 5.773, 52.098),
resulttype='results')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 3
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
assert properties['straatnaam'] == 'Egypte'
assert properties['huisnummer'] == '4'
@@ -206,8 +208,8 @@ def test_query_with_property_filtering(config_sqlite_4326):
]
)
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) > 1
for feature in features:
+44 -44
View File
@@ -6,7 +6,7 @@
#
# Copyright (c) 2019 Just van den Broecke
# Copyright (c) 2020 Francesco Bartoli
# Copyright (c) 2020 Tom Kralidis
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -259,10 +259,10 @@ def test_query_hits_ms(config_MapServer_WFS_cities):
p = OGRProvider(config_MapServer_WFS_cities)
feature_collection = p.query(resulttype='hits')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 0
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is not None
assert hits > 5000
@@ -272,10 +272,10 @@ def test_query_hits_geosol_gs(config_geosol_gs_WFS):
p = OGRProvider(config_geosol_gs_WFS)
feature_collection = p.query(resulttype='hits')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 0
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is not None
assert hits == 186
@@ -286,10 +286,10 @@ def test_query_hits_geosol_gs(config_geosol_gs_WFS):
#
# p = OGRProvider(config_MapServer_WFS_continents)
# feature_collection = p.query(resulttype='hits')
# assert feature_collection.get('type', None) == 'FeatureCollection'
# features = feature_collection.get('features', None)
# assert feature_collection.get('type') == 'FeatureCollection'
# features = feature_collection.get('features')
# assert len(features) == 0
# hits = feature_collection.get('numberMatched', None)
# hits = feature_collection.get('numberMatched')
# assert hits is not None
# assert hits > 8000000
@@ -301,10 +301,10 @@ def test_query_bbox_hits_ms(config_MapServer_WFS_cities):
feature_collection = p.query(
bbox=[-47, -24, -45, -22], resulttype='hits')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 0
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is not None
assert hits > 1
@@ -315,10 +315,10 @@ def test_query_bbox_hits_gs(config_MapServer_WFS_continents):
p = OGRProvider(config_MapServer_WFS_continents)
feature_collection = p.query(bbox=[-61, 46, -60, 47], resulttype='hits')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 0
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is not None
assert hits == 3
@@ -332,10 +332,10 @@ def test_query_bbox_hits_geosol_gs(config_geosol_gs_WFS):
# feature_collection = p.query(bbox=(
# 5.763409, 52.060197, 5.769256, 52.061976), resulttype='hits')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 0
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is not None
assert hits == 1
@@ -348,15 +348,15 @@ def test_query_bbox_ms(config_MapServer_WFS_cities):
# bbox=[120000, 480000, 124000, 487000], resulttype='results')
feature_collection = p.query(
bbox=[4.874016, 52.306852, 4.932020, 52.370004], resulttype='results')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) > 0
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
@@ -365,15 +365,15 @@ def test_query_bbox_gs(config_MapServer_WFS_continents):
p = OGRProvider(config_MapServer_WFS_continents)
feature_collection = p.query(bbox=(5, 52, 6, 53), resulttype='results')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 4
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
assert properties['NA2DESC'] == 'Netherlands'
assert properties['NA3DESC'] == 'Europe'
@@ -388,16 +388,16 @@ def test_query_bbox_geosol_gs(config_geosol_gs_WFS):
feature_collection = p.query(
bbox=(681417.0, 4849032.0, 681417.3, 4849032.3),
resulttype='results')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 1
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
assert properties['sito'] == 'Centro storico di Firenze'
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
@@ -406,15 +406,15 @@ def test_query_with_limit_ms(config_MapServer_WFS_cities):
p = OGRProvider(config_MapServer_WFS_cities)
feature_collection = p.query(limit=2, resulttype='results')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 2
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
@@ -423,17 +423,17 @@ def test_query_with_offset(config_MapServer_WFS_cities):
p = OGRProvider(config_MapServer_WFS_cities)
feature_collection = p.query(offset=20, limit=5, resulttype='results')
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert len(features) == 5
hits = feature_collection.get('numberMatched', None)
hits = feature_collection.get('numberMatched')
assert hits is None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
assert feature['id'] == 'cities.411'
assert '6610764' in properties['POPULATION']
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
+10 -10
View File
@@ -6,7 +6,7 @@
# Colin Blackburn <colb@bgs.ac.uk>
#
# Copyright (c) 2019 Just van den Broecke
# Copyright (c) 2019 Tom Kralidis
# Copyright (c) 2022 Tom Kralidis
# Copyright (c) 2022 John A Stevenson and Colin Blackburn
#
# Permission is hereby granted, free of charge, to any person
@@ -73,13 +73,13 @@ def test_query(config):
"""Testing query for a valid JSON object with geometry"""
p = PostgreSQLProvider(config)
feature_collection = p.query()
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert features is not None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
@@ -97,14 +97,14 @@ def test_query_with_property_filter(config):
"""Test query valid features when filtering by property"""
p = PostgreSQLProvider(config)
feature_collection = p.query(properties=[("waterway", "stream")])
features = feature_collection.get('features', None)
features = feature_collection.get('features')
stream_features = list(
filter(lambda feature: feature['properties']['waterway'] == 'stream',
features))
assert (len(features) == len(stream_features))
feature_collection = p.query(limit=50)
features = feature_collection.get('features', None)
features = feature_collection.get('features')
stream_features = list(
filter(lambda feature: feature['properties']['waterway'] == 'stream',
features))
@@ -127,7 +127,7 @@ def test_query_with_config_properties(config):
assert provider.properties == properties_subset
result = provider.query()
feature = result.get('features')[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
for property_name in properties.keys():
assert property_name in config["properties"]
@@ -240,9 +240,9 @@ def test_query_cql(config, cql, expected_ids):
provider = PostgreSQLProvider(config)
feature_collection = provider.query(filterq=ast)
assert feature_collection.get('type', None) == 'FeatureCollection'
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features', None)
features = feature_collection.get('features')
ids = [feature["id"] for feature in features]
assert ids == expected_ids
+11 -11
View File
@@ -4,7 +4,7 @@
# Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2019 Just van den Broecke
# Copyright (c) 2019 Tom Kralidis
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -66,13 +66,13 @@ def test_query_sqlite(config_sqlite):
p = SQLiteGPKGProvider(config_sqlite)
feature_collection = p.query()
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert features is not None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
@@ -81,13 +81,13 @@ def test_query_geopackage(config_geopackage):
p = SQLiteGPKGProvider(config_geopackage)
feature_collection = p.query()
assert feature_collection.get('type', None) == 'FeatureCollection'
features = feature_collection.get('features', None)
assert feature_collection.get('type') == 'FeatureCollection'
features = feature_collection.get('features')
assert features is not None
feature = features[0]
properties = feature.get('properties', None)
properties = feature.get('properties')
assert properties is not None
geometry = feature.get('geometry', None)
geometry = feature.get('geometry')
assert geometry is not None
@@ -109,7 +109,7 @@ def test_query_with_property_filter_sqlite_geopackage(config_sqlite):
p = SQLiteGPKGProvider(config_sqlite)
feature_collection = p.query(properties=[
("continent", "Europe")], limit=100)
features = feature_collection.get('features', None)
features = feature_collection.get('features')
assert len(features) == 39
@@ -118,7 +118,7 @@ def test_query_with_property_filter_bbox_sqlite_geopackage(config_sqlite):
p = SQLiteGPKGProvider(config_sqlite)
feature_collection = p.query(properties=[("continent", "Europe")],
bbox=[29.3373, -3.4099, 29.3761, -3.3924])
features = feature_collection.get('features', None)
features = feature_collection.get('features')
assert len(features) == 0
@@ -1,8 +1,10 @@
# =================================================================
#
# Authors: Martin Pontius <m.pontius@52north.org>
# Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2022 52°North Spatial Information Research GmbH
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -27,7 +29,7 @@
#
# =================================================================
import os
from pathlib import Path
from werkzeug.wrappers import Request
from werkzeug.test import create_environ
@@ -79,10 +81,10 @@ def _create_request(name, message, locales):
def test_async_hello_world_process_parallel(api_, config):
index_name = config['server']['manager']['connection']
index_name = Path(config['server']['manager']['connection'])
if os.path.exists(index_name):
os.remove(index_name)
if index_name.exists():
index_name.unlink()
NUM_PROCS = 4
process_id = "hello-world"
@@ -118,10 +120,8 @@ def test_async_hello_world_process_parallel(api_, config):
assert job_dict["mimetype"] == process_out['headers'][
'Content-Type']
try:
with open(
"{}/hello-world-{}".format(os.path.dirname(index_name),
job_id)) as json_file:
out_json = json.load(json_file)
with open(f'{index_name.parent()}/hello-world-{job_id}') as fh:
out_json = json.load(fh)
assert out_json["id"] == "echo"
assert out_json["value"] == "Hello World! Hello"
except FileNotFoundError as e:
+3 -3
View File
@@ -2,7 +2,7 @@
#
# Authors: Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2021 Tom Kralidis
# Copyright (c) 2022 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
@@ -27,8 +27,8 @@
#
# =================================================================
import os
import logging
import os.path
from werkzeug.test import create_environ
from werkzeug.wrappers import Request
@@ -37,7 +37,7 @@ from werkzeug.datastructures import ImmutableMultiDict
LOGGER = logging.getLogger(__name__)
def get_test_file_path(filename):
def get_test_file_path(filename: str) -> str:
"""helper function to open test file safely"""
if os.path.isfile(filename):