Skip to content

Commit

Permalink
support dataset specific templates (geopython#1857)
Browse files Browse the repository at this point in the history
  • Loading branch information
tomkralidis committed Nov 20, 2024
1 parent 065ef3a commit d01ae13
Show file tree
Hide file tree
Showing 8 changed files with 49 additions and 16 deletions.
2 changes: 2 additions & 0 deletions docs/source/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ For more information related to API design rules (the ``api_rules`` property in
limit: 10 # server limit on number of items to return
admin: false # whether to enable the Admin API
# optional configuration to specify a different set of templates for HTML pages. Recommend using absolute paths. Omit this to use the default provided templates
# This property can also be defined at the resource level to override global server settings for specific datasets
templates: # optional configuration to specify a different set of templates for HTML pages. Recommend using absolute paths. Omit this to use the default provided templates
path: /path/to/jinja2/templates/folder # path to templates folder containing the Jinja2 template HTML files
static: /path/to/static/folder # path to static folder containing css, js, images and other static files referenced by the template
Expand Down
18 changes: 13 additions & 5 deletions docs/source/html-templating.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ HTML Templating

pygeoapi uses `Jinja`_ as its templating engine to render HTML and `Flask`_ to provide route paths of the API that returns HTTP responses. For complete details on how to use these modules, refer to the `Jinja documentation`_ and the `Flask documentation`_.

The default pygeoapi configuration has ``server.templates`` commented out and defaults to the pygeoapi ``pygeoapi/templates`` and ``pygeoapi/static`` folder. To point to a different set of template configuration, you can edit your configuration:
The default pygeoapi configuration has ``server.templates`` commented out and defaults to the pygeoapi ``pygeoapi/templates`` and ``pygeoapi/static`` folder. To point to a different set of template configuration, you can edit your configuration as follows:

.. code-block:: yaml
Expand All @@ -20,9 +20,9 @@ Your templates folder should mimic the same file names and structure of the defa

Note that you need only copy and edit the templates you are interested in updating. For example,
if you are only interested in updating the ``landing_page.html`` template, then create your own version
of the only that same file. When pygeoapi detects that a custom HTML template is being used,
it will look for the custom template in ``server.templates.path``. If it does not exists, pygeoapi
will render the default HTML template for the given endpoint/requuest.
of only that same file. When pygeoapi detects that a custom HTML template is being used,
it will look for the custom template in ``server.templates.path``. If it does not exist, pygeoapi
will render the default HTML template for the given endpoint/request.

Linking to a static file in your HTML templates can be done using Jinja syntax and the exposed ``config['server']['url']``:

Expand All @@ -35,9 +35,17 @@ Linking to a static file in your HTML templates can be done using Jinja syntax a
<!-- Image example with metadata -->
<img src="{{ config['server']['url'] }}/static/img/logo.png" title="{{ config['metadata']['identification']['title'] }}" />

Dataset level templates
-----------------------

The ``templates`` configuration directive is applied to the entire server by default. It can also be used for dataset specific look and feel. As example use case is defining a template for a specific dataset to be able to add custom UI/UX functionality (e.g. search/filter widget).

.. note::

Dataset level templates apply to ``/collections/{collectionId}`` and below.

Featured themes
----------------
---------------

Community based themes can be found on the `pygeoapi Community Plugins and Themes wiki page`_.

Expand Down
12 changes: 10 additions & 2 deletions pygeoapi/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1287,13 +1287,15 @@ def describe_collections(self, request: Union[APIRequest, Any],
if request.format == F_HTML: # render
fcm['collections_path'] = self.get_collections_url()
if dataset is not None:
self.set_dataset_templates(dataset)

content = render_j2_template(self.tpl_config,
'collections/collection.html',
fcm, request.locale)
else:
content = render_j2_template(self.tpl_config,
'collections/index.html', fcm,
request.locale)
'collections/index.html',
fcm, request.locale)

return headers, HTTPStatus.OK, content

Expand Down Expand Up @@ -1379,6 +1381,8 @@ def get_collection_schema(self, request: Union[APIRequest, Any],
schema['properties'][k]['x-ogc-role'] = 'primary-instant'

if request.format == F_HTML: # render
self.set_dataset_templates(dataset)

schema['title'] = l10n.translate(
self.config['resources'][dataset]['title'], request.locale)

Expand Down Expand Up @@ -1450,6 +1454,10 @@ def get_format_exception(self, request) -> Tuple[dict, int, str]:
def get_collections_url(self):
return f"{self.base_url}/collections"

def set_dataset_templates(self, dataset):
if 'templates' in self.config['resources'][dataset]:
self.tpl_config['server']['templates'] = self.config['resources'][dataset]['templates'] # noqa

@staticmethod
def _create_crs_transform_spec(
config: dict,
Expand Down
2 changes: 2 additions & 0 deletions pygeoapi/api/environmental_data_retrieval.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@ def get_collection_edr_query(api: API, request: APIRequest,
err.ogc_exception_code, err.message)

if request.format == F_HTML: # render
api.set_dataset_templates(dataset)

uri = f'{api.get_collections_url()}/{dataset}/{query_type}'
serialized_query_params = ''
for k, v in request.params.items():
Expand Down
4 changes: 4 additions & 0 deletions pygeoapi/api/itemtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ def get_collection_queryables(api: API, request: Union[APIRequest, Any],
queryables['properties'][k]['x-ogc-role'] = 'primary-instant' # noqa

if request.format == F_HTML: # render
api.set_dataset_templates(dataset)

queryables['title'] = l10n.translate(
api.config['resources'][dataset]['title'], request.locale)

Expand Down Expand Up @@ -572,6 +574,7 @@ def get_collection_items(
l10n.set_response_language(headers, prv_locale, request.locale)

if request.format == F_HTML: # render
api.set_dataset_templates(dataset)
# For constructing proper URIs to items

content['items_path'] = uri
Expand Down Expand Up @@ -1174,6 +1177,7 @@ def get_collection_item(api: API, request: APIRequest,
l10n.set_response_language(headers, prv_locale, request.locale)

if request.format == F_HTML: # render
api.set_dataset_templates(dataset)
content['title'] = l10n.translate(collections[dataset]['title'],
request.locale)
content['id_field'] = p.id_field
Expand Down
1 change: 1 addition & 0 deletions pygeoapi/api/processes.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ def describe_processes(api: API, request: APIRequest,

if request.format == F_HTML: # render
if process is not None:
api.set_dataset_templates(process)
response = render_j2_template(api.tpl_config,
'processes/process.html',
response, request.locale)
Expand Down
2 changes: 2 additions & 0 deletions pygeoapi/api/tiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ def get_collection_tiles(api: API, request: APIRequest,
tiles['tilesets'].append(tile_matrix)

if request.format == F_HTML: # render
api.set_dataset_templates(dataset)
tiles['id'] = dataset
tiles['title'] = l10n.translate(
api.config['resources'][dataset]['title'], SYSTEM_LOCALE)
Expand Down Expand Up @@ -317,6 +318,7 @@ def get_collection_tiles_metadata(
language=prv_locale)

if request.format == F_HTML: # render
api.set_dataset_templates(dataset)
content = render_j2_template(api.tpl_config,
'collections/tiles/metadata.html',
tiles_metadata, request.locale)
Expand Down
24 changes: 15 additions & 9 deletions pygeoapi/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@
import uuid

import dateutil.parser
from babel.support import Translations
from jinja2 import Environment, FileSystemLoader, select_autoescape
from jinja2.exceptions import TemplateNotFound
import pyproj
import pygeofilter.ast
import pygeofilter.values
from pyproj.exceptions import CRSError
from requests import Session
from requests.structures import CaseInsensitiveDict
from shapely import ops
from shapely.geometry import (
box,
Expand All @@ -66,14 +75,6 @@
mapping as geom_to_geojson,
)
import yaml
from babel.support import Translations
from jinja2 import Environment, FileSystemLoader, select_autoescape
import pygeofilter.ast
import pygeofilter.values
import pyproj
from pyproj.exceptions import CRSError
from requests import Session
from requests.structures import CaseInsensitiveDict

from pygeoapi import __version__
from pygeoapi import l10n
Expand Down Expand Up @@ -473,7 +474,12 @@ def render_j2_template(config: dict, template: Path,
translations = Translations.load(locale_dir, [locale_])
env.install_gettext_translations(translations)

template = env.get_template(template)
try:
template = env.get_template(template)
except TemplateNotFound:
LOGGER.debug(f'template {template} not found')
template_paths.remove(templates)
template = env.get_template(template)

return template.render(config=l10n.translate_struct(config, locale_, True),
data=data, locale=locale_, version=__version__)
Expand Down

0 comments on commit d01ae13

Please sign in to comment.