Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show provenance for annotations in web app #1095

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
43 changes: 34 additions & 9 deletions src/bioregistry/app/templates/resource.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{% extends "base.html" %}
{% import "macros.html" as utils %}

{% block title %}{{ config.METAREGISTRY_TITLE }} - {{ name }}{% endblock %}
{% block title %}{{ config.METAREGISTRY_TITLE }} - {{ name_pack.value }}{% endblock %}

{% block styles %}
{{ super() }}
Expand Down Expand Up @@ -57,7 +57,10 @@
<div class="row align-items-center">
<div class="col-8">
<h5 style="margin: 0"><a href="{{ url_for("metaregistry_ui.resources") }}">Registry</a> <i
class="fas fa-angle-right"></i> {{ name }}
class="fas fa-angle-right"></i> {{ name_pack.value }}
<a type="button" data-toggle="modal" data-target="#name-modal">
<i class="far fa-question-circle"></i>
</a>
{{ utils.render_resource_warnings(resource) }}
</h5>
</div>
Expand All @@ -79,7 +82,7 @@ <h5 style="margin: 0"><a href="{{ url_for("metaregistry_ui.resources") }}">Regis
</div>
</div>
<a class="btn btn-sm btn-outline-dark" style="margin-right: 0.5em"
href="https://github.com/biopragmatics/bioregistry/issues/new?labels=Update&template=update-misc.yml&prefix={{ prefix }}&title=Update+{{ name }}">
href="https://github.com/biopragmatics/bioregistry/issues/new?labels=Update&template=update-misc.yml&prefix={{ prefix }}&title=Update+{{ name_pack.value }}">
Suggest
</a>
</div>
Expand All @@ -90,7 +93,7 @@ <h5 style="margin: 0"><a href="{{ url_for("metaregistry_ui.resources") }}">Regis
{% if description %}
<p>
{% if resource.get_logo() %}
<img style="max-height: 70px; float: right;" src="{{ resource.get_logo() }}" alt="Logo for {{ resource.get_name() }}"/>
<img style="max-height: 70px; float: right;" src="{{ resource.get_logo() }}" alt="Logo for {{ name_pack.value }}"/>
{% endif %}
{{ description }}
</p>
Expand Down Expand Up @@ -251,7 +254,7 @@ <h5 style="margin: 0"><a href="{{ url_for("metaregistry_ui.resources") }}">Regis
<dd>
{% if pattern %}
<p>
Local identifiers in {{ name }} should match this
Local identifiers in {{ name_pack.value }} should match this
regular expression:<br/><kbd>{{ pattern }}</kbd>
</p>
{% elif has_no_terms %}
Expand Down Expand Up @@ -286,7 +289,7 @@ <h5 style="margin: 0"><a href="{{ url_for("metaregistry_ui.resources") }}">Regis
<dt>Pattern for CURIES</dt>
<dd>
<p>
Compact URIs (CURIEs) constructed from {{ name }} should match
Compact URIs (CURIEs) constructed from {{ name_pack.value }} should match
this regular expression:<br/><kbd>{{ curie_pattern }}</kbd>
</p>
</dd>
Expand Down Expand Up @@ -484,7 +487,7 @@ <h5 class="card-header">Ontology</h5>
<div class="card" style="margin-top: 20px">
<a id="mappings"></a>
<h5 class="card-header">
Metaregistry <i class="fas fa-angle-right"></i> {{ name }}
Metaregistry <i class="fas fa-angle-right"></i> {{ name_pack.value }}
</h5>
<div class="card-body">
<p>
Expand Down Expand Up @@ -529,7 +532,7 @@ <h5 class="card-header">
{# <td>{{ resource.get_prefix_key("name", mapping.metaprefix) or "" }}</td> #}
<td style="text-align: center;">
<a data-toggle="tooltip" data-html="true"
title="Report misalignment between {{ name }} and <code>{{ mapping.metaprefix }}:{{ mapping.xref }}</code>"
title="Report misalignment between {{ name_pack.value }} and <code>{{ mapping.metaprefix }}:{{ mapping.xref }}</code>"
href="https://github.com/biopragmatics/bioregistry/issues/new?labels=Update%2CPrefix&template=mismatch.yml&title=Remove+incorrect+mapping+from+{{ resource.prefix }}+to+%60{{ mapping.metaprefix }}%3A{{ mapping.xref }}%60&prefix={{ resource.prefix }}&metaprefix={{ mapping.metaprefix }}&external-prefix={{ mapping.xref }}">
<i class="fas fa-pen"></i>
</a>
Expand Down Expand Up @@ -592,7 +595,7 @@ <h5 style="margin: 0">Providers</h5>
{% if providers %}
<p>
The local unique identifier <code>{{ example }}</code> is used to demonstrate the providers
available for {{ name }}. A guide for curating additional providers can be found
available for {{ name_pack.value }}. A guide for curating additional providers can be found
<a href="https://biopragmatics.github.io/bioregistry/curation/providers">here</a>.
</p>
{% else %}
Expand Down Expand Up @@ -685,6 +688,28 @@ <h5 class="modal-title" id="versionModalLabel">Programmatic Access to Version</h
</div>
{% endif %}

<div class="modal fade" id="name-modal" tabindex="-1" role="dialog" aria-labelledby="nameModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="nameModalLabel">Programmatic Access to Name</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>
This name annotation was originally annotated in
<a href="{{ url_for("metaregistry_ui.metaresource", metaprefix=name_pack.metaprefix) }}">{{ name_pack.name }}</a>
and is redistributed under the {{ name_pack.license }} license.
</p>
<p>Get the name:</p>
{{ utils.code_example(prefix, "get_name", name_pack.value) }}
</div>
</div>
</div>
</div>

{% if resource_license %}
<div class="modal fade" id="license-modal" tabindex="-1" role="dialog" aria-labelledby="licenseModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
Expand Down
3 changes: 2 additions & 1 deletion src/bioregistry/app/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,14 @@ def resource(prefix: str) -> str | flask.Response:
example_curie_extras = [
_resource.get_curie(example_extra, use_preferred=True) for example_extra in example_extras
]
name_pack = manager._repack(_resource.get_name(provenance=True))
return render_template(
"resource.html",
zip=zip,
prefix=prefix,
resource=_resource,
bioschemas=json.dumps(_resource.get_bioschemas_jsonld(), ensure_ascii=False),
name=manager.get_name(prefix),
name_pack=name_pack,
example=example,
example_extras=example_extras,
example_curie=example_curie,
Expand Down
37 changes: 27 additions & 10 deletions src/bioregistry/resolve.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
import logging
import typing
from functools import lru_cache
from typing import Any, Dict, List, Mapping, Optional, Set
from typing import Any, Dict, List, Literal, Mapping, Optional, Set, Union, overload

import curies

from .constants import MaybeCURIE
from .resource_manager import manager
from .resource_manager import MetaresourceAnnotatedValue, manager
from .schema import Attributable, Resource

__all__ = [
Expand Down Expand Up @@ -83,9 +83,25 @@ def get_resource(prefix: str) -> Optional[Resource]:
return manager.get_resource(prefix)


def get_name(prefix: str) -> Optional[str]:
"""Get the name for the given prefix, it it's available."""
return manager.get_name(prefix)
# docstr-coverage:excused `overload`
@overload
def get_name(prefix: str, *, provenance: Literal[False] = False) -> Union[None, str]: ...


# docstr-coverage:excused `overload`
@overload
def get_name(
prefix: str, *, provenance: Literal[True] = True
) -> Union[None, MetaresourceAnnotatedValue[str]]: ...


def get_name(
prefix: str, *, provenance: bool = False
) -> Union[None, str, MetaresourceAnnotatedValue[str]]:
"""Get the name for the given prefix, if it's available."""
if provenance:
return manager.get_name(prefix, provenance=True)
return manager.get_name(prefix, provenance=False)


def get_description(prefix: str, *, use_markdown: bool = False) -> Optional[str]:
Expand Down Expand Up @@ -161,12 +177,13 @@ def get_pattern(prefix: str) -> Optional[str]:
return manager.get_pattern(prefix)


def get_namespace_in_lui(prefix: str) -> Optional[bool]:
def get_namespace_in_lui(
prefix: str, *, provenance: bool = False
) -> Union[None, bool, MetaresourceAnnotatedValue[bool]]:
"""Check if the namespace should appear in the LUI."""
entry = get_resource(prefix)
if entry is None:
return None
return entry.get_namespace_in_lui()
if provenance:
return manager.get_namespace_in_lui(prefix, provenance=True)
return manager.get_namespace_in_lui(prefix, provenance=False)


def get_appears_in(prefix: str) -> Optional[List[str]]:
Expand Down
100 changes: 97 additions & 3 deletions src/bioregistry/resource_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,26 @@
import typing
import warnings
from collections import Counter, defaultdict
from dataclasses import dataclass
from functools import lru_cache
from pathlib import Path
from typing import (
Any,
Callable,
Dict,
Generic,
Iterable,
List,
Literal,
Mapping,
Optional,
Sequence,
Set,
Tuple,
TypeVar,
Union,
cast,
overload,
)

import curies
Expand Down Expand Up @@ -50,6 +55,7 @@
Resource,
sanitize_model,
)
from .schema.struct import MetaprefixAnnotatedValue
from .schema_utils import (
_collections_from_path,
_contexts_from_path,
Expand All @@ -67,6 +73,31 @@

logger = logging.getLogger(__name__)

X = TypeVar("X", bound=Union[int, str])


@dataclass
class MetaresourceAnnotatedValue(Generic[X]):
"""A pack for a value that has extra information."""

value: X
registry: Registry

@property
def metaprefix(self) -> str:
"""Get prefix for the source registry for the annotation."""
return self.registry.prefix

@property
def name(self) -> str:
"""Get the name of the source registry for the annotation."""
return self.registry.name

@property
def license(self) -> str:
"""Get the license for the annotation."""
return self.registry.license or "unknown"


def _synonym_to_canonical(registry: Mapping[str, Resource]) -> NormDict:
"""Return a mapping from several variants of each synonym to the canonical namespace."""
Expand Down Expand Up @@ -516,12 +547,75 @@ def get_uri_prefix(
return None
return entry.get_uri_prefix(priority=priority)

def get_name(self, prefix: str) -> Optional[str]:
"""Get the name for the given prefix, it it's available."""
# docstr-coverage:excused `overload`
@overload
def _repack(self, obj: None) -> None: ...

# docstr-coverage:excused `overload`
@overload
def _repack(self, obj: X) -> X: ...

# docstr-coverage:excused `overload`
@overload
def _repack(self, obj: MetaprefixAnnotatedValue[X]) -> MetaresourceAnnotatedValue[X]: ...

def _repack(
self, obj: Union[None, X, MetaprefixAnnotatedValue[X]]
) -> Union[MetaresourceAnnotatedValue[X], X, None]:
if obj is None:
return None
elif isinstance(obj, MetaprefixAnnotatedValue):
mp = self.get_registry(obj.metaprefix)
if mp is None:
raise ValueError
return MetaresourceAnnotatedValue(obj.value, mp)
else:
return obj

# docstr-coverage:excused `overload`
@overload
def get_name(self, prefix: str, *, provenance: Literal[False] = False) -> Union[None, str]: ...

# docstr-coverage:excused `overload`
@overload
def get_name(
self, prefix: str, *, provenance: Literal[True] = True
) -> Union[None, MetaresourceAnnotatedValue[str]]: ...

def get_name(
self, prefix: str, *, provenance: bool = False
) -> Union[None, str, MetaresourceAnnotatedValue[str]]:
"""Get the name for the given prefix, if it's available."""
entry = self.get_resource(prefix)
if entry is None:
return None
if provenance:
_tmp = entry.get_name(provenance=True)
return self._repack(_tmp)
return entry.get_name(provenance=False)

# docstr-coverage:excused `overload`
@overload
def get_namespace_in_lui(
self, prefix: str, *, provenance: Literal[False] = False
) -> Union[bool, None]: ...

# docstr-coverage:excused `overload`
@overload
def get_namespace_in_lui(
self, prefix: str, *, provenance: Literal[True] = True
) -> Union[None, MetaresourceAnnotatedValue[bool]]: ...

def get_namespace_in_lui(
self, prefix: str, *, provenance: bool = False
) -> Union[None, bool, MetaresourceAnnotatedValue[bool]]:
"""Get the name for the given prefix, if it's available."""
entry = self.get_resource(prefix)
if entry is None:
return None
return entry.get_name()
if provenance:
return self._repack(entry.get_namespace_in_lui(provenance=True))
return entry.get_namespace_in_lui(provenance=False)

def get_description(self, prefix: str, *, use_markdown: bool = False) -> Optional[str]:
"""Get the description for the given prefix, it it's available."""
Expand Down
Loading
Loading