Skip to content

landing

OPTIMADE landing page router.

CONFIG: ServerConfig = ServerConfig() module-attribute

This singleton loads the config from a hierarchy of sources (see customise_sources) and makes it importable in the server code.

ENTRY_COLLECTIONS = {'links': links_coll, 'references': references_coll, 'structures': structures_coll} module-attribute

__api_version__ = '1.1.0' module-attribute

router = Router(routes=[Route('/', endpoint=landing)]) module-attribute

get_base_url(parsed_url_request)

Get base URL for current server

Take the base URL from the config file, if it exists, otherwise use the request.

Source code in optimade/server/routers/utils.py
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
def get_base_url(
    parsed_url_request: Union[
        urllib.parse.ParseResult, urllib.parse.SplitResult, StarletteURL, str
    ]
) -> str:
    """Get base URL for current server

    Take the base URL from the config file, if it exists, otherwise use the request.
    """
    parsed_url_request = (
        urllib.parse.urlparse(parsed_url_request)
        if isinstance(parsed_url_request, str)
        else parsed_url_request
    )
    return (
        CONFIG.base_url.rstrip("/")
        if CONFIG.base_url
        else f"{parsed_url_request.scheme}://{parsed_url_request.netloc}"
    )

landing(request) async

Show a human-readable landing page when the base URL is accessed.

Source code in optimade/server/routers/landing.py
84
85
86
async def landing(request: Request):
    """Show a human-readable landing page when the base URL is accessed."""
    return render_landing_page(str(request.url))

meta_values(url, data_returned, data_available, more_data_available, schema=None, **kwargs)

Helper to initialize the meta values

Source code in optimade/server/routers/utils.py
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def meta_values(
    url: Union[urllib.parse.ParseResult, urllib.parse.SplitResult, StarletteURL, str],
    data_returned: Optional[int],
    data_available: int,
    more_data_available: bool,
    schema: Optional[str] = None,
    **kwargs,
) -> ResponseMeta:
    """Helper to initialize the meta values"""
    from optimade.models import ResponseMetaQuery

    if isinstance(url, str):
        url = urllib.parse.urlparse(url)

    # To catch all (valid) variations of the version part of the URL, a regex is used
    if re.match(r"/v[0-9]+(\.[0-9]+){,2}/.*", url.path) is not None:
        url_path = re.sub(r"/v[0-9]+(\.[0-9]+){,2}/", "/", url.path)
    else:
        url_path = url.path

    if schema is None:
        schema = CONFIG.schema_url if not CONFIG.is_index else CONFIG.index_schema_url

    return ResponseMeta(
        query=ResponseMetaQuery(representation=f"{url_path}?{url.query}"),
        api_version=__api_version__,
        time_stamp=datetime.now(),
        data_returned=data_returned,
        more_data_available=more_data_available,
        provider=CONFIG.provider,
        data_available=data_available,
        implementation=CONFIG.implementation,
        schema=schema,
        **kwargs,
    )

render_landing_page(url) cached

Render and cache the landing page.

This function uses the template file ./static/landing_page.html, adapted from the original Jinja template. Instead of Jinja, some basic string replacement is used to fill out the fields from the server configuration.

Careful

The removal of Jinja means that the fields are no longer validated as web safe before inclusion in the template.

Source code in optimade/server/routers/landing.py
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
@lru_cache
def render_landing_page(url: str) -> HTMLResponse:
    """Render and cache the landing page.

    This function uses the template file `./static/landing_page.html`, adapted
    from the original Jinja template. Instead of Jinja, some basic string
    replacement is used to fill out the fields from the server configuration.

    !!! warning "Careful"
        The removal of Jinja means that the fields are no longer validated as
        web safe before inclusion in the template.

    """
    meta = meta_values(url, 1, 1, more_data_available=False, schema=CONFIG.schema_url)
    major_version = __api_version__.split(".")[0]
    versioned_url = f"{get_base_url(url)}/v{major_version}/"

    if CONFIG.custom_landing_page:
        html = Path(CONFIG.custom_landing_page).resolve().read_text()
    else:
        template_dir = Path(__file__).parent.joinpath("static").resolve()
        html = (template_dir / "landing_page.html").read_text()

    # Build a dictionary that maps the old Jinja keys to the new simplified replacements
    replacements = {
        "api_version": __api_version__,
    }

    if meta.provider:
        replacements.update(
            {
                "provider.name": meta.provider.name,
                "provider.prefix": meta.provider.prefix,
                "provider.description": meta.provider.description,
                "provider.homepage": str(meta.provider.homepage) or "",
            }
        )

    if meta.implementation:
        replacements.update(
            {
                "implementation.name": meta.implementation.name or "",
                "implementation.version": meta.implementation.version or "",
                "implementation.source_url": str(meta.implementation.source_url or ""),
            }
        )

    for replacement in replacements:
        html = html.replace(f"{{{{ {replacement} }}}}", replacements[replacement])

    # Build the list of endpoints. The template already opens and closes the `<ul>` tag.
    endpoints_list = [
        f'<li><a href="{versioned_url}{endp}">{versioned_url}{endp}</a></li>'
        for endp in list(ENTRY_COLLECTIONS.keys()) + ["info"]
    ]
    html = html.replace("{% ENDPOINTS %}", "\n".join(endpoints_list))

    # If the index base URL has been configured, also list it
    index_base_url_html = ""
    if CONFIG.index_base_url:
        index_base_url_html = f"""<h3>Index base URL:</h3>
<p><a href={CONFIG.index_base_url}>{CONFIG.index_base_url}</a></p>
"""
    html = html.replace("{% INDEX_BASE_URL %}", index_base_url_html)

    return HTMLResponse(html)