Repository: biopragmatics/bioregistry

Issue:
obofoundry metaregistry pages shows incorrect example purl
Yes, I won't shut up about case sensitivity... it will be inscribed on my gravestone... all in lowercase

But this is different from #46 which is resolved

https://bioregistry.io/metaregistry/obofoundry

Example CURIE
chebi:24867

Example CURIE Link
http://purl.obolibrary.org/obo/chebi_24867

Both the CURIE and the PURL should be all uppercase.


Discussion:
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/biopragmatics/bioregistry/pull/955?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=biopragmatics) Report
Attention: `3 lines` in your changes are missing coverage. Please review.

| [Files](https://app.codecov.io/gh/biopragmatics/bioregistry/pull/955?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=biopragmatics) | Coverage Δ | |
|---|---|---|
| [src/bioregistry/schema/struct.py](https://app.codecov.io/gh/biopragmatics/bioregistry/pull/955?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=biopragmatics#diff-c3JjL2Jpb3JlZ2lzdHJ5L3NjaGVtYS9zdHJ1Y3QucHk=) | `87.20% <100.00%> (ø)` | |
| [src/bioregistry/version.py](https://app.codecov.io/gh/biopragmatics/bioregistry/pull/955?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=biopragmatics#diff-c3JjL2Jpb3JlZ2lzdHJ5L3ZlcnNpb24ucHk=) | `68.42% <100.00%> (ø)` | |
| [src/bioregistry/resource\_manager.py](https://app.codecov.io/gh/biopragmatics/bioregistry/pull/955?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=biopragmatics#diff-c3JjL2Jpb3JlZ2lzdHJ5L3Jlc291cmNlX21hbmFnZXIucHk=) | `75.23% <0.00%> (ø)` | |
| [src/bioregistry/app/ui.py](https://app.codecov.io/gh/biopragmatics/bioregistry/pull/955?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=biopragmatics#diff-c3JjL2Jpb3JlZ2lzdHJ5L2FwcC91aS5weQ==) | `77.20% <60.00%> (-0.52%)` | :arrow_down: |


:loudspeaker: Thoughts on this report? [Let us know!](https://about.codecov.io/pull-request-comment-report/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=biopragmatics).


Patch:
diff --git a/src/bioregistry/app/templates/metaresource.html b/src/bioregistry/app/templates/metaresource.html
index 30613a770..a405c10a9 100644
--- a/src/bioregistry/app/templates/metaresource.html
+++ b/src/bioregistry/app/templates/metaresource.html
@@ -144,7 +144,7 @@
                     </dd>
                 {% endif %}
                 {% if entry.resolver_uri_format %}
-                    {% set example_resolution = entry.resolve(entry.example, example_identifier) %}
+                    {% set example_resolution = example_identifier and entry.resolve(entry.example, example_identifier) %}
                     <dt>
                         {% if entry.resolver_type == "lookup" %}
                             Example CURIE Page
diff --git a/src/bioregistry/app/ui.py b/src/bioregistry/app/ui.py
index 8726a2dda..24b616954 100644
--- a/src/bioregistry/app/ui.py
+++ b/src/bioregistry/app/ui.py
@@ -189,7 +189,18 @@ def metaresource(metaprefix: str):
     if accept != "text/html":
         return serialize_model(entry, metaresource_to_rdf_str, negotiate=True)
 
-    example_identifier = manager.get_example(entry.example)
+    external_prefix = entry.example
+    bioregistry_prefix: Optional[str]
+    if metaprefix == "bioregistry":
+        bioregistry_prefix = external_prefix
+    else:
+        # TODO change this to [external_prefix] instead of .get(external_prefix)
+        #  when all metaregistry entries are required to have corresponding schema slots
+        bioregistry_prefix = manager.get_registry_invmap(metaprefix).get(external_prefix)
+
+    # In the case that we can't map from the external registry's prefix to Bioregistry
+    # prefix, the example identifier can't be looked up
+    example_identifier = bioregistry_prefix and manager.get_example(bioregistry_prefix)
     return render_template(
         "metaresource.html",
         entry=entry,
@@ -198,15 +209,16 @@ def metaresource(metaprefix: str):
         description=entry.description,
         homepage=entry.homepage,
         download=entry.download,
-        example_prefix=entry.example,
-        example_prefix_url=entry.get_provider_uri_format(entry.example),
+        example_prefix=external_prefix,
+        example_prefix_url=entry.get_provider_uri_format(external_prefix),
         example_identifier=example_identifier,
         example_curie=(
-            curie_to_str(entry.example, example_identifier) if example_identifier else None
+            curie_to_str(external_prefix, example_identifier) if example_identifier else None
         ),
         example_curie_url=(
-            manager.get_registry_uri(metaprefix, entry.example, example_identifier)
-            if example_identifier
+            # TODO there must be a more direct way for this
+            manager.get_registry_uri(metaprefix, bioregistry_prefix, example_identifier)
+            if bioregistry_prefix and example_identifier
             else None
         ),
         formats=[
diff --git a/src/bioregistry/data/metaregistry.json b/src/bioregistry/data/metaregistry.json
index 5034130a7..b4dc6de90 100644
--- a/src/bioregistry/data/metaregistry.json
+++ b/src/bioregistry/data/metaregistry.json
@@ -124,7 +124,7 @@
       },
       "description": "The Basic Register of Thesauri, Ontologies & Classifications (BARTOC) is a database of Knowledge Organization Systems and KOS related registries.\\nThe main goal of BARTOC is to list as many Knowledge Organization Systems as possible at one place in order to achieve greater visibility, highlight their features, make them searchable and comparable, and foster knowledge sharing. BARTOC includes any kind of KOS from any subject area, in any language, any publication format, and any form of accessibility. BARTOC’s search interface is available in 20 European languages and provides two search options: Basic Search by keywords, and Advanced Search by taxonomy terms. A circle of editors has gathered around BARTOC from all across Europe and BARTOC has been approved by the International Society for Knowledge Organization (ISKO).",
       "download": "https://bartoc.org/data/dumps/latest.ndjson",
-      "example": "241",
+      "example": "181",
       "governance": {
         "accepts_external_contributions": true,
         "code_repository": "https://github.com/gbv/bartoc.org",
@@ -482,7 +482,7 @@
       },
       "description": "The Crop Ontology (CO) current objective is to compile validated concepts along with their inter-relationships on anatomy, structure and phenotype of Crops, on trait measurement and methods as well as on Germplasm with the multi-crop passport terms",
       "download": "https://cropontology.org/metadata",
-      "example": "CO_010",
+      "example": "CO_325",
       "governance": {
         "accepts_external_contributions": true,
         "curates": true,
@@ -499,7 +499,7 @@
       "logo_url": "https://cropontology.org/cropontology_static/crop_ontology_logo_2.png",
       "name": "Crop Ontology Curation Tool",
       "prefix": "cropoct",
-      "provider_uri_format": "https://www.cropontology.org/ontology/$1",
+      "provider_uri_format": "https://cropontology.org/ontology/$1",
       "qualities": {
         "automatable_download": true,
         "bulk_data": true,
@@ -533,7 +533,7 @@
         "orcid": "0000-0003-4494-839X"
       },
       "description": "The LifeWatch ERIC repository of semantic resources for the ecological domain.",
-      "example": "ALIENSPECIES",
+      "example": "AGROVOC",
       "governance": {
         "accepts_external_contributions": true,
         "curates": true,
@@ -935,7 +935,7 @@
       },
       "description": "The mission of the OBO Foundry is to develop a family of interoperable ontologies that are both logically well-formed and scientifically accurate.",
       "download": "http://www.obofoundry.org/registry/ontologies.yml",
-      "example": "chebi",
+      "example": "CHEBI",
       "governance": {
         "accepts_external_contributions": true,
         "code_repository": "https://github.com/OBOFoundry/OBOFoundry.github.io",
@@ -954,7 +954,7 @@
       "logo_url": "https://obofoundry.org/images/foundrylogo.png",
       "name": "OBO Foundry",
       "prefix": "obofoundry",
-      "provider_uri_format": "http://www.obofoundry.org/ontology/$1",
+      "provider_uri_format": "https://www.obofoundry.org/ontology/$1",
       "qualities": {
         "automatable_download": true,
         "bulk_data": true,
@@ -1140,7 +1140,7 @@
       },
       "description": "A registry of commonly used prefixes in the life sciences and linked data. The source data for this registry is a spreadsheet on Google called the Life Science Registry. This registry was previously known as Prefix Commons and was available at https://prefixcommons.org.",
       "download": "http://tinyurl.com/lsregistry",
-      "example": "CHEBI",
+      "example": "doi",
       "governance": {
         "accepts_external_contributions": true,
         "curates": true,
@@ -1152,7 +1152,7 @@
         "status": "active"
       },
       "homepage": "https://registry.bio2kg.org",
-      "license": "Public Domain",
+      "license": "CC0",
       "logo_url": "https://avatars.githubusercontent.com/u/15466415",
       "name": "Prefix Commons",
       "prefix": "prefixcommons",
@@ -1188,7 +1188,7 @@
         "orcid": "0000-0003-4221-7956"
       },
       "description": "Re3data is a global registry of research data repositories that covers research data repositories from different academic disciplines.",
-      "example": "r3d100010772",
+      "example": "r3d100014165",
       "governance": {
         "accepts_external_contributions": true,
         "curates": true,
@@ -1237,6 +1237,7 @@
         "orcid": "0000-0002-5497-0243"
       },
       "description": "The Research Resource Identification Initiative provides RRIDs to 4 main classes of resources: Antibodies, Cell Lines, Model Organisms, and Databases / Software tools",
+      "download": "https://docs.google.com/spreadsheets/d/1BEPZXZsENhK7592AR83xUwbPbR2J-GVQ/edit?usp=sharing&ouid=107737386203376389514&rtpof=true&sd=true",
       "example": "AB",
       "governance": {
         "accepts_external_contributions": true,
@@ -1259,8 +1260,9 @@
         "no_authentication": true,
         "structured_data": true
       },
-      "resolver_type": "resolver",
-      "resolver_uri_format": "https://scicrunch.org/resolver/RRID:$1_$2"
+      "resolver_type": "lookup",
+      "resolver_uri_format": "https://scicrunch.org/resolver/RRID:$1_$2",
+      "short_name": "RRID"
     },
     {
       "availability": {
diff --git a/src/bioregistry/resource_manager.py b/src/bioregistry/resource_manager.py
index 431ccc5f9..d2a0fecbd 100644
--- a/src/bioregistry/resource_manager.py
+++ b/src/bioregistry/resource_manager.py
@@ -1223,7 +1223,7 @@ class Manager:
 
         resource = self.get_resource(prefix)
         if resource is None:
-            raise KeyError
+            raise KeyError(f"Could not look up a resource by prefix: {prefix}")
         for provider in resource.get_extra_providers():
             rv.append((provider.code, provider.resolve(identifier)))
 
@@ -1262,7 +1262,23 @@ class Manager:
         return dict(self.get_providers_list(prefix, identifier))
 
     def get_registry_uri(self, metaprefix: str, prefix: str, identifier: str) -> Optional[str]:
-        """Get the URL to resolve the given prefix/identifier pair with the given resolver."""
+        """Get the URL to resolve the given prefix/identifier pair with the given resolver.
+
+        :param metaprefix: The metaprefix for an external registry
+        :param prefix: The Bioregistry prefix
+        :param identifier: The local unique identifier for a concept in the semantic space
+            denoted by the prefix
+        :returns: The external registry's URI (either for resolving or lookup) of the entity
+            denoted by the prefix/identifier pair.
+
+        >>> from bioregistry import manager
+        >>> manager.get_registry_uri("rrid", "antibodyregistry", "493771")
+        'https://scicrunch.org/resolver/RRID:AB_493771'
+
+        GO is not in RRID so this should return None
+
+        >>> manager.get_registry_uri("rrid", "GO", "493771")
+        """
         providers = self.get_providers(prefix, identifier)
         if not providers:
             return None
diff --git a/src/bioregistry/schema/struct.py b/src/bioregistry/schema/struct.py
index 39675c362..d0619d5bb 100644
--- a/src/bioregistry/schema/struct.py
+++ b/src/bioregistry/schema/struct.py
@@ -2456,10 +2456,10 @@ class Registry(BaseModel):
             return f"{BIOREGISTRY_REMOTE_URL}/metaregistry/{self.prefix}/resolve/"
         return self.provider_uri_format.replace("$1", "")
 
-    def get_provider_uri_format(self, prefix: str) -> Optional[str]:
+    def get_provider_uri_format(self, external_prefix: str) -> Optional[str]:
         """Get the provider string.
 
-        :param prefix: The prefix used in the metaregistry
+        :param external_prefix: The prefix used in the metaregistry
         :return: The URL in the registry for the prefix, if it's able to provide one
 
         >>> from bioregistry import get_registry
@@ -2470,7 +2470,7 @@ class Registry(BaseModel):
         >>> get_registry("n2t").get_provider_uri_format("go")
         'https://bioregistry.io/metaregistry/n2t/resolve/go'
         """
-        return self.get_provider_uri_prefix() + prefix
+        return self.get_provider_uri_prefix() + external_prefix
 
     def get_resolver_uri_format(self, prefix: str) -> str:
         """Generate a provider URI string based on mapping through this registry's vocabulary.
@@ -2503,6 +2503,8 @@ class Registry(BaseModel):
         'https://identifiers.org/go:0032571'
         >>> get_registry("cellosaurus").resolve("go", "0032571")
         'https://bioregistry.io/metaregistry/cellosaurus/go:0032571'
+        >>> get_registry("rrid").resolve("AB", "493771")
+        'https://scicrunch.org/resolver/RRID:AB_493771'
         """
         return self.get_resolver_uri_format(prefix).replace("$1", identifier)
 


Test patch:
diff --git a/tests/test_metaregistry.py b/tests/test_metaregistry.py
index c5d7c6121..ade471a08 100644
--- a/tests/test_metaregistry.py
+++ b/tests/test_metaregistry.py
@@ -23,10 +23,17 @@ class TestMetaregistry(unittest.TestCase):
         """Test the metaregistry entries have a minimum amount of data."""
         for metaprefix, registry in self.manager.metaregistry.items():
             self.assertIsInstance(registry, Registry)
+            external_prefixes = set(self.manager.get_registry_invmap(metaprefix))
             with self.subTest(metaprefix=metaprefix):
                 self.assertIsNotNone(registry.name)
                 self.assertIsNotNone(registry.homepage)
                 self.assertIsNotNone(registry.example)
+                if metaprefix != "bioregistry" and external_prefixes:
+                    self.assertIn(
+                        registry.example,
+                        external_prefixes,
+                        msg="Examples should be external-registry specific and mapped",
+                    )
                 self.assertIsNotNone(registry.description)
                 self.assertIsNotNone(registry.contact)
                 self.assertIsNotNone(registry.license, msg=f"Contact: {registry.contact}")