update ES backend, add HTML templates

This commit is contained in:
Tom Kralidis
2018-04-03 18:24:59 +00:00
parent 313a04da9c
commit fe36a09598
7 changed files with 214 additions and 17 deletions
+42 -8
View File
@@ -34,8 +34,9 @@ import os
from jinja2 import Environment, FileSystemLoader
from pygeoapi import __version__
from pygeoapi.provider import load_provider
from pygeoapi.provider.base import ProviderConnectionError
from pygeoapi.provider.base import ProviderConnectionError, ProviderQueryError
LOGGER = logging.getLogger(__name__)
@@ -106,14 +107,13 @@ class API(object):
'rel': 'self',
'type': 'text/html',
'title': 'the OpenAPI definition as HTML',
'href': '{}?f=html'.format(self.config['server']['url'])
'href': '{}api?f=html'.format(self.config['server']['url'])
}
]
if format_ == 'html': # render
headers_['Content-type'] = 'text/html'
content = _render_j2_template(self.config, 'service.html', fcm)
content = _render_j2_template(self.config, 'root.html', fcm)
return headers_, 200, content
return headers_, 200, json.dumps(fcm)
@@ -149,6 +149,17 @@ class API(object):
'Content-type': 'application/json'
}
formats = ['json', 'html']
format_ = args.get('f')
if format_ is not None and format_ not in formats:
exception = {
'code': 'InvalidParameterValue',
'description': 'Invalid format'
}
LOGGER.error(exception)
return headers_, 400, json.dumps(exception)
conformance = {
'conformsTo': [
'http://www.opengis.net/spec/wfs-1/3.0/req/core',
@@ -158,6 +169,12 @@ class API(object):
]
}
if format_ == 'html': # render
headers_['Content-type'] = 'text/html'
content = _render_j2_template(self.config, 'conformance.html',
conformance)
return headers_, 200, content
return headers_, 200, json.dumps(conformance)
def describe_collections(self, headers, args, dataset=None):
@@ -219,13 +236,19 @@ class API(object):
collection['links'].append(lnk)
if dataset is not None and k == dataset:
return headers_, 200, json.dumps(collection)
fcm = collection
break
fcm['collections'].append(collection)
if format_ == 'html': # render
headers_['Content-type'] = 'text/html'
content = _render_j2_template(self.config, 'service.html', fcm)
if dataset is not None:
content = _render_j2_template(self.config, 'collection.html',
fcm)
else:
content = _render_j2_template(self.config, 'collections.html',
fcm)
return headers_, 200, content
@@ -242,10 +265,12 @@ class API(object):
:returns: tuple of headers, status code, content
"""
print(args)
headers_ = {
'Content-type': 'application/json'
}
LOGGER.debug('Processing query parameters')
try:
startindex = int(args.get('startindex'))
except TypeError:
@@ -256,6 +281,8 @@ class API(object):
limit = self.config['server']['limit']
resulttype = args.get('resulttype') or 'results'
bbox = args.get('bbox')
time = args.get('time')
if dataset not in self.config['datasets'].keys():
exception = {
@@ -274,8 +301,15 @@ class API(object):
try:
content = p.query(startindex=int(startindex), limit=int(limit),
resulttype=resulttype)
resulttype=resulttype, bbox=bbox, time=time)
except ProviderConnectionError:
exception = {
'code': 'NoApplicableCode',
'description': 'connection error (check logs)'
}
LOGGER.error(exception)
return headers_, 500, json.dumps(exception)
except ProviderQueryError:
exception = {
'code': 'NoApplicableCode',
'description': 'query error (check logs)'
@@ -371,4 +405,4 @@ def _render_j2_template(config, template, data):
env = Environment(loader=FileSystemLoader(TEMPLATES))
print(TEMPLATES)
template = env.get_template(template)
return template.render(config=config, data=data)
return template.render(config=config, data=data, version=__version__)
+6
View File
@@ -47,6 +47,7 @@ class BaseProvider(object):
self.name = provider_def['name']
self.data = provider_def['data']
self.id_field = provider_def['id_field']
self.time_field = provider_def.get('time_field')
def query(self):
"""
@@ -97,3 +98,8 @@ class BaseProvider(object):
class ProviderConnectionError(Exception):
"""query / backend error"""
pass
class ProviderQueryError(Exception):
"""query / backend error"""
pass
+54 -4
View File
@@ -31,7 +31,8 @@ import logging
from elasticsearch import Elasticsearch, exceptions
from pygeoapi.provider.base import BaseProvider, ProviderConnectionError
from pygeoapi.provider.base import (BaseProvider, ProviderConnectionError,
ProviderQueryError)
LOGGER = logging.getLogger(__name__)
@@ -63,33 +64,82 @@ class ElasticsearchProvider(BaseProvider):
LOGGER.debug('Connecting to Elasticsearch')
self.es = Elasticsearch(self.es_host)
def query(self, startindex=0, limit=10, resulttype='results'):
def query(self, startindex=0, limit=10, resulttype='results',
bbox=None, time=None):
"""
query Elasticsearch index
:param startindex: starting record to return (default 0)
:param limit: number of records to return (default 10)
:param resulttype: return results or hit limit (default results)
:param bbox: bounding box (minx,miny,maxx,maxy)
:param time: temporal (datestamp or extent)
:returns: dict of 0..n GeoJSON features
"""
query = {'query': {'bool': {'filter': []}}}
filter_ = []
feature_collection = {
'type': 'FeatureCollection',
'features': []
}
LOGGER.debug('Querying Elasticsearch')
if resulttype == 'hits':
LOGGER.debug('hits only specified')
limit = 0
if bbox is not None:
LOGGER.debug('processing bbox parameter')
minx, miny, maxx, maxy = bbox.split(',')
bbox_filter = {
'geo_shape': {
'geometry': {
'shape': {
'type': 'envelope',
'coordinates': [[minx, miny], [maxx, maxy]]
},
'relation': 'intersects'
}
}
}
query['query']['bool']['filter'].append(bbox_filter)
if time is not None:
LOGGER.debug('processing time parameter')
if self.time_field is None:
LOGGER.error('time_field not enabled for collection')
raise ProviderQueryError()
time_field = 'properties.{}'.format(self.time_field)
if '/' in time: # envelope
range_ = {'range': time_field}
time_begin, time_end = time.split('/')
if time_begin == '': # until
range_['range'][time_field]['lte'] = time_end
if time_end == '': # from
range_['range'][time_field]['gte'] = time_begin
filter_.append(range_)
else: # time instant
filter_.append({'match': {time_field: time}})
query['query']['bool']['filter'].append(filter_)
try:
LOGGER.debug('Querying Elasticsearch')
results = self.es.search(index=self.index_name, from_=startindex,
size=limit)
size=limit, body=query)
except exceptions.ConnectionError as err:
LOGGER.error(err)
raise ProviderConnectionError()
except exceptions.RequestError as err:
LOGGER.error(err)
raise ProviderQueryError()
feature_collection['numberMatched'] = results['hits']['total']
+35
View File
@@ -0,0 +1,35 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="{{ config['server']['encoding'] }}">
<title>{{ config['metadata']['identification']['title'] }} - data['title']</title>
<meta name="language" content="{{ config['server']['language'] }}">
<meta name="description" content="{{ config['metadata']['identification']['title'] }}">
<meta name="keywords" content="{{ config['metadata']['identification']['keywords']|join(',') }}">
<link rel="stylesheet" href="static/css/default.css">
<!--[if lt IE 9]>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.js"></script>
<![endif]-->
</head>
<body>
<header>
<h1><a title="{{ config['metadata']['identification']['title'] }}" href="{{ config['server']['url'] }}">{{ config['metadata']['identification']['title'] }}</a></h1>
<span itemprop="description">{{ config['metadata']['identification']['description'] }}</span>
</header>
<section id="collection">
<h2>{{ data['title'] }}</h2>
<span>{{ data['description'] }}</span>
<h2>Links</h2>
<ul>
{% for link in data['links'] %}
<li><a title="{{ link['rel'] }}" href="{{ link['href'] }}">{{ link['href'] }}</a></li>
{% endfor %}
</ul>
</section>
<hr/>
<footer>Powered by <a title="pygeoapi" href="https://github.com/geopython/pygeoapi">pygeoapi</a> {{ version }}</footer>
</body>
</html>
+33
View File
@@ -0,0 +1,33 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="{{ config['server']['encoding'] }}">
<title>{{ config['metadata']['identification']['title'] }} - Collections</title>
<meta name="language" content="{{ config['server']['language'] }}">
<meta name="description" content="{{ config['metadata']['identification']['title'] }}">
<meta name="keywords" content="{{ config['metadata']['identification']['keywords']|join(',') }}">
<link rel="stylesheet" href="static/css/default.css">
<!--[if lt IE 9]>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.js"></script>
<![endif]-->
</head>
<body>
<header>
<h1><a title="{{ config['metadata']['identification']['title'] }}" href="{{ config['server']['url'] }}">{{ config['metadata']['identification']['title'] }}</a></h1>
<span itemprop="description">{{ config['metadata']['identification']['description'] }}</span>
</header>
<section id="collections">
<h2>Collections</h2>
<ul>
{% for k, v in config['datasets'].items()%}
<li><a title="{{ v['title'] }}" href="{{ config['server']['url'] }}/collections/{{ k }}?f=html">{{ v['title'] }}</a></li>
{% endfor %}
</ul>
</section>
<hr/>
<footer>Powered by <a title="pygeoapi" href="https://github.com/geopython/pygeoapi">pygeoapi</a> {{ version }}</footer>
</body>
</html>
+33
View File
@@ -0,0 +1,33 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="{{ config['server']['encoding'] }}">
<title>{{ config['metadata']['identification']['title'] }} - Conformance</title>
<meta name="language" content="{{ config['server']['language'] }}">
<meta name="description" content="{{ config['metadata']['identification']['title'] }}">
<meta name="keywords" content="{{ config['metadata']['identification']['keywords']|join(',') }}">
<link rel="stylesheet" href="static/css/default.css">
<!--[if lt IE 9]>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.js"></script>
<![endif]-->
</head>
<body>
<header>
<h1><a title="{{ config['metadata']['identification']['title'] }}" href="{{ config['server']['url'] }}">{{ config['metadata']['identification']['title'] }}</a></h1>
<span itemprop="description">{{ config['metadata']['identification']['description'] }}</span>
</header>
<section id="conformance">
<h2>Conformance</h2>
<ul>
{% for link in data['conformsTo'] %}
<li><a title="{{ link }}" href="{{ link }}">{{ link }}</a></li>
{% endfor %}
</ul>
</section>
<hr/>
<footer>Powered by <a title="pygeoapi" href="https://github.com/geopython/pygeoapi">pygeoapi</a> {{ version }}</footer>
</body>
</html>
@@ -16,7 +16,7 @@
</head>
<body>
<header>
<h1><a title="{{ config['metadata']['identification']['title'] }}" href="{{ config['server']['url'] }}">{{ config['metadata']['identification']['title'] }}</a></h1>
<h1><a title="{{ config['metadata']['identification']['title'] }}" href="{{ config['server']['url'] }}/">{{ config['metadata']['identification']['title'] }}</a></h1>
<span itemprop="description">{{ config['metadata']['identification']['description'] }}</span>
</header>
<section id="service-metadata">
@@ -28,11 +28,17 @@
</tr>
</table>
</section>
<section id="datasets">
<h2>Datasets</h2>
<section id="conformance">
<h2><a href="conformance?f=html">Conformance</a></h2>
</section>
<section id="collections">
<h2><a href="collections?f=html">Collections</a></h2>
</section>
<section id="links">
<h2>Links</h2>
<ul>
{% for k, v in config['datasets'].items()%}
<li><a title="{{ v['title'] }}" href="{{ config['server']['url'] }}{{ k }}">{{ v['title'] }}</a></li>
{% for link in data['links'] %}
<li><a title="{{ link['title'] }}" href="{{ link['href'] }}">{{ link['title'] }}</a></li>
{% endfor %}
</ul>
</section>