logo Netzschleuder network catalog, repository, and centrifuge

History of obo_ontologies

The changes to the source code of this entry in the repository, newest first, each with the differences it made to the entry's files. They are also listed on GitLab.

[entries] Add the date when each entry was added, and remove icon_hash by Tiago de Paula Peixoto beff7c9
The dates are those of the commits that first added the entries' files
(2020-08-11 for the entries of the initial commit); renamed entries keep
the date of their first name.

Changed: none
diff --git a/entries/obo_ontologies/__init__.py b/entries/obo_ontologies/__init__.py
index f81d585..21b9b78 100644
--- a/entries/obo_ontologies/__init__.py
+++ b/entries/obo_ontologies/__init__.py
@@ -181,7 +181,7 @@ citation = [('M. Ashburner, C. A. Ball, J. A. Blake, D. Botstein, H. Butler, J.
              'https://doi.org/10.1101/2022.04.13.22273750'),
             ('C. J. Mungall, C. Torniai, G. V. Gkoutos, S. E. Lewis, M. A. Haendel, "Uberon, an integrative multi-species anatomy ontology." Genome Biology 13, R5 (2012)',
              'https://doi.org/10.1186/gb-2012-13-1-r5')]
-icon_hash = None
+date_added = '2026-10-31'
 upstream_license = 'Gene Ontology: CC BY 4.0; Human Phenotype Ontology: HPO license (https://hpo.jax.org/license); Mondo: CC BY 4.0; Uberon: CC BY 3.0'
 upstream_prefix = None
 files = [(fname, alt, None) for alt, prefix, fname in RELEASES]
[entries] Prefix the properties in the descriptions with (Node), (Edge) or (Graph) by Tiago de Paula Peixoto bc3dbbd
The lists of properties at the end of the descriptions marked edge
properties with "Edge" but left node properties unmarked, or grouped
them in "Nodes: `a`, ...; `b`, ..." items, so the kind of a property
was often unclear. Every item now starts with (Node), (Edge) or (Graph),
followed by the property names, a colon and the explanation, one item
per property or group of properties; qualifications (kinds of nodes,
networks) follow the names in parentheses. Properties that were only
named got a short explanation, and a few items that named the wrong
kind of property were corrected.
diff --git a/entries/obo_ontologies/__init__.py b/entries/obo_ontologies/__init__.py
index 9a8a9e5..f81d585 100644
--- a/entries/obo_ontologies/__init__.py
+++ b/entries/obo_ontologies/__init__.py
@@ -155,16 +155,16 @@ There is one network `uberon_<year>` for each year from 2011 to 2026, from the l
 
 The networks have the following properties (those of each ontology):
 
-- `term_id`: the identifier of the term, in the networks of the Gene Ontology (e.g. `GO:0004672`), the Human Phenotype Ontology (e.g. `HP:0001631`), Mondo (e.g. `MONDO:0005015`, or of an external term, e.g. `UBERON:0002107`, `NCBITaxon:9606`, `http://identifiers.org/hgnc/1100`) and Uberon (e.g. `UBERON:0000948`, or of an external term, e.g. `NCBITaxon:7742`).
-- `name`: the name of the term.
-- `namespace` (`go_*`, `uberon_*`): the namespace of the term: in the Gene Ontology, the sub-ontology; in Uberon, when given (e.g. `uberon/phenoscape-anatomy`, in the releases of 2013-2020; `cell` for the terms of the Cell Ontology in 2016).
-- `definition`: the textual definition of the term.
-- `synonyms`: the synonyms of the term.
-- `xrefs`: the cross-references of the term to other databases and vocabularies (e.g. EC numbers, Reactome and Wikipedia for GO; UMLS, SNOMED CT and MeSH for HPO; `OMIM:...`, `Orphanet:...`, `DOID:...` for Mondo; `FMA:...`, `MA:...`, `ZFA:...` for Uberon).
-- `subsets`: the subsets that the term belongs to (e.g. GO slims; `rare` in Mondo).
-- `is_external` (`mondo_*`, `uberon_*`): whether the node is a term of another ontology (or, in Mondo, a gene).
-- `is_obsolete` (`mondo_*`, some `uberon_*`): whether the term is obsolete.
-- Edge `relation` (`go_*`, `mondo_*`, `uberon_*`): the relation between the terms (`is_a`, `part_of`, etc.)."""
+- (Node) `term_id`: the identifier of the term, in the networks of the Gene Ontology (e.g. `GO:0004672`), the Human Phenotype Ontology (e.g. `HP:0001631`), Mondo (e.g. `MONDO:0005015`, or of an external term, e.g. `UBERON:0002107`, `NCBITaxon:9606`, `http://identifiers.org/hgnc/1100`) and Uberon (e.g. `UBERON:0000948`, or of an external term, e.g. `NCBITaxon:7742`).
+- (Node) `name`: the name of the term.
+- (Node) `namespace` (`go_*`, `uberon_*`): the namespace of the term: in the Gene Ontology, the sub-ontology; in Uberon, when given (e.g. `uberon/phenoscape-anatomy`, in the releases of 2013-2020; `cell` for the terms of the Cell Ontology in 2016).
+- (Node) `definition`: the textual definition of the term.
+- (Node) `synonyms`: the synonyms of the term.
+- (Node) `xrefs`: the cross-references of the term to other databases and vocabularies (e.g. EC numbers, Reactome and Wikipedia for GO; UMLS, SNOMED CT and MeSH for HPO; `OMIM:...`, `Orphanet:...`, `DOID:...` for Mondo; `FMA:...`, `MA:...`, `ZFA:...` for Uberon).
+- (Node) `subsets`: the subsets that the term belongs to (e.g. GO slims; `rare` in Mondo).
+- (Node) `is_external` (`mondo_*`, `uberon_*`): whether the node is a term of another ontology (or, in Mondo, a gene).
+- (Node) `is_obsolete` (`mondo_*`, some `uberon_*`): whether the term is obsolete.
+- (Edge) `relation` (`go_*`, `mondo_*`, `uberon_*`): the relation between the terms (`is_a`, `part_of`, etc.)."""
 tags = ['Biological', 'Informational', 'Unweighted', 'Multigraph', 'Metadata']
 url = ['https://geneontology.org/', 'https://release.geneontology.org/',
        'https://hpo.jax.org/', 'https://github.com/obophenotype/human-phenotype-ontology',
[entries] New entry: obo_ontologies by Tiago de Paula Peixoto 7163c20
diff --git a/entries/obo_ontologies/__init__.py b/entries/obo_ontologies/__init__.py
new file mode 100644
index 0000000..9a8a9e5
--- /dev/null
+++ b/entries/obo_ontologies/__init__.py
@@ -0,0 +1,364 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2026 Tiago de Paula Peixoto <tiago@skewed.de>
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Affero General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Affero General Public License along
+# with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import re
+from collections import defaultdict
+
+from .. import *
+
+# The four ontologies of the Open Biological and Biomedical Ontologies (OBO)
+# Foundry, as networks named <prefix>_<year>, from the last release of each
+# year. The cached files keep the names (relative to their URL prefixes)
+# that they had in the former entries gene_ontology,
+# human_phenotype_ontology, mondo_disease and uberon_anatomy.
+
+# Gene Ontology: the archive of the Gene Ontology Consortium
+# (release.geneontology.org), with the full edition of the ontology:
+# gene_ontology.obo until 2008, go.obo since 2009 (when both exist, they
+# contain the same terms; go.obo also has the relations other than is_a and
+# part_of in the early years)
+GO = "https://release.geneontology.org"
+GO_RELEASES = {
+    "2004": "2004-12-01/ontology/gene_ontology.obo",
+    "2005": "2005-12-01/ontology/gene_ontology.obo",
+    "2006": "2006-12-01/ontology/gene_ontology.obo",
+    "2007": "2007-12-01/ontology/gene_ontology.obo",
+    "2008": "2008-12-01/ontology/gene_ontology.obo",
+    "2009": "2009-12-01/ontology/go.obo",
+    "2010": "2010-12-01/ontology/go.obo",
+    "2011": "2011-12-01/ontology/go.obo",
+    "2012": "2012-12-01/ontology/go.obo",
+    "2013": "2013-12-01/ontology/go.obo",
+    "2014": "2014-12-01/ontology/go.obo",
+    "2015": "2015-12-01/ontology/go.obo",
+    "2016": "2016-12-01/ontology/go.obo",
+    "2017": "2017-12-01/ontology/go.obo",
+    "2018": "2018-12-01/ontology/go.obo",
+    "2019": "2019-12-09/ontology/go.obo",
+    "2020": "2020-12-08/ontology/go.obo",
+    "2021": "2021-12-15/ontology/go.obo",
+    "2022": "2022-12-04/ontology/go.obo",
+    "2023": "2023-11-15/ontology/go.obo",
+    "2024": "2024-11-03/ontology/go.obo",
+    "2025": "2025-10-10/ontology/go.obo",
+    "2026": "2026-08-05/ontology/go.obo",
+}
+
+# Human Phenotype Ontology: GitHub release tags. Until 2021 the releases have
+# no attached files, and hp.obo is read from the repository at the release
+# tag; since 2022 it is a file of the release.
+HPO_REPO = "obophenotype/human-phenotype-ontology"
+HPO_RAW = f"https://raw.githubusercontent.com/{HPO_REPO}"
+HPO_RELEASE = f"https://github.com/{HPO_REPO}/releases/download"
+HPO_RELEASES = {
+    "2017": (HPO_RAW, "v2017-12-12/hp.obo"),
+    "2018": (HPO_RAW, "v2018-12-21/hp.obo"),
+    "2019": (HPO_RAW, "v2019-11-08/hp.obo"),
+    "2020": (HPO_RAW, "v2020-12-07/hp.obo"),
+    "2021": (HPO_RAW, "v2021-10-10/hp.obo"),
+    "2022": (HPO_RELEASE, "v2022-12-15/hp.obo"),
+    "2023": (HPO_RELEASE, "v2023-10-09/hp.obo"),
+    "2024": (HPO_RELEASE, "v2024-12-12/hp.obo"),
+    "2025": (HPO_RELEASE, "v2025-11-24/hp.obo"),
+    "2026": (HPO_RELEASE, "v2026-09-01/hp.obo"),
+}
+
+# Mondo: GitHub releases, with mondo.obo attached since December 2018
+MONDO = "https://github.com/monarch-initiative/mondo/releases/download"
+MONDO_RELEASES = {
+    "2018": "v2018-12-02/mondo.obo",
+    "2019": "v2019-11-25/mondo.obo",
+    "2020": "v2020-12-18/mondo.obo",
+    "2021": "v2021-12-30/mondo.obo",
+    "2022": "v2022-12-01/mondo.obo",
+    "2023": "v2023-12-12/mondo.obo",
+    "2024": "v2024-12-03/mondo.obo",
+    "2025": "v2025-12-02/mondo.obo",
+    "2026": "v2026-10-06/mondo.obo",
+}
+
+# Uberon: until 2020 from the archive of the OBO Foundry (Sourceforge), in
+# 2021 from the GitHub repository at the release tag, and since 2022 the
+# files of the GitHub releases. The "basic" edition (basic.obo) is used;
+# since 2024 it is named uberon-basic.obo (the releases of 2022 and 2023 have
+# both files, and uberon-basic.obo is larger; basic.obo is used for
+# continuity).
+UBERON_SVN = "http://svn.code.sf.net/p/obo/svn/uberon/releases"
+UBERON_RAW = "https://raw.githubusercontent.com/obophenotype/uberon"
+UBERON_RELEASE = "https://github.com/obophenotype/uberon/releases/download"
+UBERON_RELEASES = {
+    "2011": (UBERON_SVN, "2011-12-14/basic.obo"),
+    "2012": (UBERON_SVN, "2012-10-09/basic.obo"),
+    "2013": (UBERON_SVN, "2013-12-18/basic.obo"),
+    "2014": (UBERON_SVN, "2014-12-09/basic.obo"),
+    "2015": (UBERON_SVN, "2015-11-19/basic.obo"),
+    "2016": (UBERON_SVN, "2016-11-23/basic.obo"),
+    "2017": (UBERON_SVN, "2017-10-28/basic.obo"),
+    "2018": (UBERON_SVN, "2018-10-14/basic.obo"),
+    "2019": (UBERON_SVN, "2019-11-22/basic.obo"),
+    "2020": (UBERON_SVN, "2020-09-16/basic.obo"),
+    "2021": (UBERON_RAW, "v2021-11-28/basic.obo"),
+    "2022": (UBERON_RELEASE, "v2022-12-13/basic.obo"),
+    "2023": (UBERON_RELEASE, "v2023-12-08/basic.obo"),
+    "2024": (UBERON_RELEASE, "v2024-11-25/uberon-basic.obo"),
+    "2025": (UBERON_RELEASE, "v2025-12-04/uberon-basic.obo"),
+    "2026": (UBERON_RELEASE, "v2026-10-01/uberon-basic.obo"),
+}
+
+# (network name, URL prefix, file name)
+RELEASES = ([(f"go_{y}", GO, f) for y, f in GO_RELEASES.items()] +
+            [(f"hpo_{y}", p, f) for y, (p, f) in HPO_RELEASES.items()] +
+            [(f"mondo_{y}", MONDO, f) for y, f in MONDO_RELEASES.items()] +
+            [(f"uberon_{y}", p, f) for y, (p, f) in UBERON_RELEASES.items()])
+
+title = "Gene Ontology, Human Phenotype Ontology, Mondo and Uberon ontologies (OBO, 2004-2026)"
+description = r"""Networks of four ontologies of the Open Biological and Biomedical Ontologies (OBO) Foundry, each a vocabulary of terms with the relations between them, from the last release of each year: the Gene Ontology (`go_<year>`, 2004-2026), the Human Phenotype Ontology (`hpo_<year>`, 2017-2026), the Mondo Disease Ontology (`mondo_<year>`, 2018-2026) and the Uberon anatomy ontology (`uberon_<year>`, 2011-2026). In all of them, nodes are terms and a directed edge goes from a term to a more general or related term, according to the relations of the ontology (edge `relation`, except in the networks of the Human Phenotype Ontology, which only have `is_a` relations). The networks are read from the OBO flat files of the ontologies.
+
+**Gene Ontology (`go_<year>`).** The Gene Ontology (GO) is the ontology of the functions of genes and gene products developed by the Gene Ontology Consortium, which is used to annotate genes in most biological databases. The terms belong to three namespaces (sub-ontologies): molecular functions (e.g. "protein kinase activity"), biological processes (e.g. "mitotic cell cycle") and cellular components (e.g. "mitochondrial membrane").
+
+Nodes are terms, and a directed edge goes from a term to a more general or related term, according to the relations of the ontology: `is_a` (subclass, e.g. "protein kinase activity" is a "kinase activity"), `part_of`, `has_part`, `regulates`, `positively_regulates`, `negatively_regulates`, `occurs_in`, `happens_during`, `ends_during` and (in 2012-2013) `results_in`. A pair of terms can be connected by several relations. The networks are directed acyclic graphs with respect to `is_a` and `part_of`, while relations such as `has_part` go in the opposite direction. Obsolete terms, which have no relations and are only kept by GO so that old identifiers can be resolved, are left out. The logical definitions of terms (`intersection_of` and `union_of` axioms), the alternative identifiers, comments and other annotations are also left out.
+
+There is one network `go_<year>` for each year from 2004 to 2026, from the last release of the year in the archive of the Gene Ontology Consortium (monthly releases since 2004): 2004-12-01, 2005-12-01, 2006-12-01, 2007-12-01, 2008-12-01, 2009-12-01, 2010-12-01, 2011-12-01, 2012-12-01, 2013-12-01, 2014-12-01, 2015-12-01, 2016-12-01, 2017-12-01, 2018-12-01, 2019-12-09, 2020-12-08, 2021-12-15, 2022-12-04, 2023-11-15, 2024-11-03, 2025-10-10 and 2026-08-05 (the latest one). The networks are of the full edition of the ontology (`gene_ontology.obo` until 2008, `go.obo` from 2009). The releases until 2007 contain only the relations `is_a` and `part_of`; the regulation relations appear in 2008, and `has_part` in 2009. The release of 2004 uses the namespace names `function`, `process` and `component` (later `molecular_function`, `biological_process` and `cellular_component`).
+
+**Human Phenotype Ontology (`hpo_<year>`).** The Human Phenotype Ontology (HPO) is the standardized vocabulary of the phenotypic abnormalities encountered in human disease (e.g. "Atrial septal defect", "Intellectual disability"), developed by the Monarch Initiative and used to annotate diseases and patients in clinical genetics. Besides the phenotypic abnormalities, the ontology has terms for the mode of inheritance, the clinical course (e.g. onset, progression), clinical modifiers and frequencies.
+
+Nodes are terms, and a directed edge goes from a term to a more general term (relation `is_a`, subclass), so that the networks are directed acyclic graphs with the root `HP:0000001` ("All"). A term can have several parents. Obsolete terms, which have no relations and are only kept so that old identifiers can be resolved, are left out. The logical definitions of terms (`intersection_of` axioms, which refer to terms of other ontologies), the alternative identifiers, comments and other annotations are also left out.
+
+There is one network `hpo_<year>` for each year from 2017 to 2026, from the last release of the year on GitHub: 2017-12-12, 2018-12-21, 2019-11-08, 2020-12-07, 2021-10-10, 2022-12-15, 2023-10-09, 2024-12-12, 2025-11-24 and 2026-09-01 (the latest one), from the file `hp.obo` of each release.
+
+**Mondo Disease Ontology (`mondo_<year>`).** The Mondo Disease Ontology, developed by the Monarch Initiative, harmonizes the disease definitions of many disease vocabularies (OMIM, Orphanet, the Disease Ontology, NCIt, MedGen, ICD, GARD, etc.) into a single ontology of human and animal diseases, with precise mappings between them.
+
+Nodes are terms, and a directed edge goes from a disease to a more general disease (relation `is_a`, subclass), and from a disease to other entities it is related to by the relations of the ontology, e.g. its anatomical location (`disease_has_location`, to terms of the Uberon anatomy ontology), its features (`disease_has_feature`, to phenotypes of the Human Phenotype Ontology), the gene whose mutations cause it (`has_material_basis_in_germline_mutation_in`, to HGNC genes), its infectious agent or taxon (`disease_has_infectious_agent`, `in_taxon`, to NCBI taxa), the biological processes it disrupts (to Gene Ontology terms), etc. The relation `excluded_subClassOf` marks a curated exclusion: the disease is explicitly not a subclass of the target, contrary to some source vocabulary. A pair of terms can be connected by several relations. The terms of other ontologies (and the genes) that are targets of the relations are nodes as well, marked by the property `is_external`; their names are given when the file of Mondo includes them. Since 2024, the file includes the terms of other ontologies that Mondo refers to, with their relations among themselves (e.g. `part_of` and `develops_from` among anatomical terms), which are kept, so that the networks of 2024-2026 have about twice as many nodes as the earlier ones. Relations whose targets are web pages are left out (e.g. `curated_content_resource`, `should_conform_to`, `seeAlso` and `excluded_from_qc_check`, which point to web resources, design patterns and issue trackers). Obsolete terms, which have no relations and are only kept so that old identifiers can be resolved, are left out, except those that are targets of relations (mostly of `excluded_subClassOf`), which are marked by the property `is_obsolete`. The logical definitions of terms (`intersection_of`, `union_of` and `disjoint_from` axioms), the alternative identifiers, comments and other annotations are also left out.
+
+There is one network `mondo_<year>` for each year from 2018 to 2026, from the last release of the year on GitHub: 2018-12-02, 2019-11-25, 2020-12-18, 2021-12-30, 2022-12-01, 2023-12-12, 2024-12-03, 2025-12-02 and 2026-10-06 (the latest one), from the file `mondo.obo` of each release (the earlier releases have no files attached).
+
+**Uberon anatomy ontology (`uberon_<year>`).** Uberon is the integrated cross-species ontology of the anatomy of animals (mostly vertebrates), which describes anatomical structures, tissues, organs, body parts, embryonic structures and life stages independently of the species (e.g. "heart", "forelimb", "neural tube"), and links them to the anatomy ontologies of single species.
+
+Nodes are terms, and a directed edge goes from a term to a related term, according to the relations of the ontology: `is_a` (subclass, e.g. "cardiac muscle tissue" is a "muscle tissue"), `part_of`, `develops_from`, `transformation_of`, `immediate_transformation_of`, `mutually_spatially_disjoint_with`, `has_part`, etc., as well as relations to the taxa in which a structure is present or absent (`present_in_taxon`, `never_in_taxon`, `dubious_for_taxon`, `ambiguous_for_taxon`, to taxa of the NCBI taxonomy) and to terms of other ontologies (e.g. `fma_set_term`, to the Foundational Model of Anatomy). A pair of terms can be connected by several relations. The terms of other ontologies that are targets of relations are nodes as well, marked by the property `is_external`. Relations whose targets are web pages are left out (e.g. `dc-contributor`, the ORCID of the contributor of a term, `seeAlso`, `source_atlas` and `implements_design_pattern`), as are the images of the structures (`depicted_by`, in 2019). Obsolete terms, which have no relations and are only kept so that old identifiers can be resolved, are left out, except those that are targets of relations, which are marked by the property `is_obsolete`. The logical definitions of terms (`intersection_of` and `union_of` axioms), the alternative identifiers, comments and other annotations are also left out.
+
+There is one network `uberon_<year>` for each year from 2011 to 2026, from the last release of the year: 2011-12-14, 2012-10-09, 2013-12-18, 2014-12-09, 2015-11-19, 2016-11-23, 2017-10-28, 2018-10-14 and 2019-11-22, 2020-09-16 (from the archive of releases of the OBO Foundry on Sourceforge), 2021-11-28 (from the GitHub repository at the release tag), 2022-12-13, 2023-12-08, 2024-11-25, 2025-12-04 and 2026-10-01 (the latest one; files of the GitHub releases). The networks are of the "basic" edition of the ontology, which has only the terms of Uberon and the relations between them that are useful for most applications, without the imported terms of other ontologies (`basic.obo`, named `uberon-basic.obo` since 2024; the release of 2016 includes the terms of the Cell Ontology, which are marked as external).
+
+The networks have the following properties (those of each ontology):
+
+- `term_id`: the identifier of the term, in the networks of the Gene Ontology (e.g. `GO:0004672`), the Human Phenotype Ontology (e.g. `HP:0001631`), Mondo (e.g. `MONDO:0005015`, or of an external term, e.g. `UBERON:0002107`, `NCBITaxon:9606`, `http://identifiers.org/hgnc/1100`) and Uberon (e.g. `UBERON:0000948`, or of an external term, e.g. `NCBITaxon:7742`).
+- `name`: the name of the term.
+- `namespace` (`go_*`, `uberon_*`): the namespace of the term: in the Gene Ontology, the sub-ontology; in Uberon, when given (e.g. `uberon/phenoscape-anatomy`, in the releases of 2013-2020; `cell` for the terms of the Cell Ontology in 2016).
+- `definition`: the textual definition of the term.
+- `synonyms`: the synonyms of the term.
+- `xrefs`: the cross-references of the term to other databases and vocabularies (e.g. EC numbers, Reactome and Wikipedia for GO; UMLS, SNOMED CT and MeSH for HPO; `OMIM:...`, `Orphanet:...`, `DOID:...` for Mondo; `FMA:...`, `MA:...`, `ZFA:...` for Uberon).
+- `subsets`: the subsets that the term belongs to (e.g. GO slims; `rare` in Mondo).
+- `is_external` (`mondo_*`, `uberon_*`): whether the node is a term of another ontology (or, in Mondo, a gene).
+- `is_obsolete` (`mondo_*`, some `uberon_*`): whether the term is obsolete.
+- Edge `relation` (`go_*`, `mondo_*`, `uberon_*`): the relation between the terms (`is_a`, `part_of`, etc.)."""
+tags = ['Biological', 'Informational', 'Unweighted', 'Multigraph', 'Metadata']
+url = ['https://geneontology.org/', 'https://release.geneontology.org/',
+       'https://hpo.jax.org/', 'https://github.com/obophenotype/human-phenotype-ontology',
+       'https://mondo.monarchinitiative.org/', 'https://github.com/monarch-initiative/mondo',
+       'https://obophenotype.github.io/uberon/', 'https://github.com/obophenotype/uberon',
+       'http://svn.code.sf.net/p/obo/svn/uberon/releases/']
+citation = [('M. Ashburner, C. A. Ball, J. A. Blake, D. Botstein, H. Butler, J. M. Cherry, A. P. Davis, K. Dolinski, S. S. Dwight, J. T. Eppig, M. A. Harris, D. P. Hill, L. Issel-Tarver, A. Kasarskis, S. Lewis, J. C. Matese, J. E. Richardson, M. Ringwald, G. M. Rubin, G. Sherlock, "Gene Ontology: tool for the unification of biology." Nature Genetics 25, 25-29 (2000)',
+             'https://doi.org/10.1038/75556'),
+            ('The Gene Ontology Consortium, S. A. Aleksander, J. Balhoff, S. Carbon, J. M. Cherry, H. J. Drabkin, D. Ebert, M. Feuermann, P. Gaudet, N. L. Harris, et al., "The Gene Ontology knowledgebase in 2023." Genetics 224, iyad031 (2023)',
+             'https://doi.org/10.1093/genetics/iyad031'),
+            ('M. A. Gargano, N. Matentzoglu, B. Coleman, E. B. Addo-Lartey, A. V. Anagnostopoulos, J. Anderton, P. Avillach, A. M. Bagley, E. Bakštein, J. P. Balhoff, et al., "The Human Phenotype Ontology in 2024: phenotypes around the world." Nucleic Acids Research 52, D1333-D1346 (2024)',
+             'https://doi.org/10.1093/nar/gkad1005'),
+            ('N. A. Vasilevsky, N. A. Matentzoglu, S. Toro, J. E. Flack, H. Hegde, D. R. Unni, G. F. Alyea, J. S. Amberger, L. Babb, J. P. Balhoff, et al., "Mondo: Unifying diseases for the world, by the world." medRxiv 2022.04.13.22273750 (2022)',
+             'https://doi.org/10.1101/2022.04.13.22273750'),
+            ('C. J. Mungall, C. Torniai, G. V. Gkoutos, S. E. Lewis, M. A. Haendel, "Uberon, an integrative multi-species anatomy ontology." Genome Biology 13, R5 (2012)',
+             'https://doi.org/10.1186/gb-2012-13-1-r5')]
+icon_hash = None
+upstream_license = 'Gene Ontology: CC BY 4.0; Human Phenotype Ontology: HPO license (https://hpo.jax.org/license); Mondo: CC BY 4.0; Uberon: CC BY 3.0'
+upstream_prefix = None
+files = [(fname, alt, None) for alt, prefix, fname in RELEASES]
+
+def fetch_upstream(force=False):
+    name = __name__.split(".")[-1]
+    for alt, prefix, fname in RELEASES:
+        fetch_upstream_files(name, prefix, [(fname, alt, None)], force)
+
+# for checks: read only this many stanzas of each file (None: all)
+_check_rows = None
+
+## OBO flat files (could be shared in parsers.py)
+
+def obo_unescape(s):
+    return re.sub(r"\\(.)", lambda m: {"n": "\n", "t": "\t"}.get(m.group(1), m.group(1)), s)
+
+def obo_quoted(v):
+    "The quoted text at the beginning of a value (definitions, synonyms)."
+    m = re.match(r'"((?:[^"\\]|\\.)*)"', v)
+    return obo_unescape(m.group(1)) if m else v
+
+def obo_strip(v):
+    "A value without its trailing comment (' ! ...') and qualifiers ('{...}')."
+    v = re.sub(r"\s+!\s.*$", "", v)
+    v = re.sub(r"\s*\{[^}]*\}\s*$", "", v)
+    return v.strip()
+
+def read_obo(f):
+    """Yield the stanzas of an OBO file as pairs (kind, tags), where `tags` is
+    a dictionary of the tags to the lists of their (raw) values."""
+    kind, tags = None, defaultdict(list)
+    n = 0
+    for l in f:
+        l = l.rstrip("\r\n")
+        if l.startswith("["):
+            if kind is not None:
+                yield kind, tags
+                n += 1
+                if _check_rows is not None and n >= _check_rows:
+                    return
+            kind, tags = l.strip()[1:-1], defaultdict(list)
+            continue
+        if kind is None or not l.strip() or l.startswith("!"):
+            continue
+        t, sep, v = l.partition(":")
+        if sep:
+            tags[t.strip()].append(v.strip())
+    if kind is not None:
+        yield kind, tags
+
+SYNONYM_TAGS = ("synonym", "exact_synonym", "narrow_synonym", "broad_synonym",
+                "related_synonym")
+XREF_TAGS = ("xref", "xref_analog", "xref_unknown")
+
+def obo_graph(f, id_prop, namespace=True, skip=()):
+    """Build the network of the non-obsolete terms of an OBO file, with an edge
+    from each term to the targets of its `is_a` and `relationship` tags.
+    Obsolete terms are only included if they are targets of relations.
+    Targets that are not terms of the file (e.g. terms of other ontologies)
+    are added as nodes without other properties; relations whose targets are
+    web pages (URLs other than identifiers.org ones) are left out. Relations
+    given by an identifier are named by their Typedef stanza. Relations in
+    `skip` are left out as well. Returns the
+    graph and the number of edges left out for each relation."""
+    terms = {}
+    obsolete = {}
+    typedefs = {}
+    for kind, tags in read_obo(f):
+        if kind == "Typedef" and tags.get("id") and tags.get("name"):
+            typedefs[obo_strip(tags["id"][0])] = tags["name"][0]
+        if kind != "Term" or not tags.get("id"):
+            continue
+        if any(v.split()[0] == "true" for v in tags.get("is_obsolete", [])):
+            obsolete[obo_strip(tags["id"][0])] = tags
+            continue
+        terms[obo_strip(tags["id"][0])] = tags
+
+    vidx = {}
+    vprops = defaultdict(list)
+    def add_vertex(tid):
+        v = vidx.get(tid)
+        if v is not None:
+            return v
+        v = vidx[tid] = len(vidx)
+        tags = terms.get(tid, obsolete.get(tid, {}))
+        vprops[id_prop].append(tid)
+        vprops["name"].append(tags["name"][0] if tags.get("name") else "")
+        vprops["namespace"].append(tags["namespace"][0] if tags.get("namespace") else "")
+        vprops["definition"].append(obo_quoted(tags["def"][0]) if tags.get("def") else "")
+        vprops["synonyms"].append([obo_quoted(s) for t in SYNONYM_TAGS
+                                   for s in tags.get(t, [])])
+        vprops["xrefs"].append([obo_strip(x.split('"')[0]) for t in XREF_TAGS
+                                for x in tags.get(t, [])])
+        vprops["subsets"].append([obo_strip(s) for s in tags.get("subset", [])])
+        vprops["is_external"].append(tid not in terms and tid not in obsolete)
+        vprops["is_obsolete"].append(tid in obsolete)
+        return v
+
+    def is_page(t):
+        return (t.startswith("http://") or t.startswith("https://")) and \
+            "identifiers.org/" not in t
+
+    for tid in terms:
+        add_vertex(tid)
+    edges, rel = [], []
+    dropped = defaultdict(int)
+    for tid, tags in terms.items():
+        s = vidx[tid]
+        targets = [("is_a", obo_strip(v)) for v in tags.get("is_a", [])]
+        for v in tags.get("relationship", []):
+            r, t = obo_strip(v).split()[:2]
+            # relations given by identifier (e.g. BFO:0000050) are named
+            # after their definition (e.g. part_of)
+            if re.fullmatch(r"[A-Za-z]+:[0-9]+", r) and r in typedefs:
+                r = typedefs[r].strip().replace(" ", "_")
+            targets.append((r, t))
+        for r, t in targets:
+            if is_page(t) or r in skip:
+                dropped[r] += 1
+                continue
+            edges.append((s, add_vertex(t)))
+            rel.append(r)
+
+    g = Graph(directed=True)
+    g.add_vertex(len(vidx))
+    g.add_edge_list(numpy.array(edges, dtype="int64").reshape(-1, 2))
+    g.vp[id_prop] = g.new_vp("string", vals=vprops[id_prop])
+    g.vp.name = g.new_vp("string", vals=vprops["name"])
+    if namespace:
+        g.vp.namespace = g.new_vp("string", vals=vprops["namespace"])
+    g.vp.definition = g.new_vp("string", vals=vprops["definition"])
+    g.vp.synonyms = g.new_vp("vector<string>", vals=vprops["synonyms"])
+    g.vp.xrefs = g.new_vp("vector<string>", vals=vprops["xrefs"])
+    g.vp.subsets = g.new_vp("vector<string>", vals=vprops["subsets"])
+    if any(vprops["is_external"]):
+        g.vp.is_external = g.new_vp("bool", vals=vprops["is_external"])
+    if any(vprops["is_obsolete"]):
+        g.vp.is_obsolete = g.new_vp("bool", vals=vprops["is_obsolete"])
+    g.ep.relation = g.new_ep("string", vals=rel)
+    return g, dict(dropped)
+
+_verbose = False
+
+@cache_network()
+@coerce_props()
+@annotate()
+def parse(alts=None):
+    name = __name__.split(".")[-1]
+    for alt, prefix, fname in RELEASES:
+        if alts is not None and alt not in alts:
+            continue
+        onto = alt.split("_")[0]
+        with open_upstream_file(name, fname, "r") as f:
+            if onto == "go":
+                g, dropped = obo_graph(f, "term_id")
+                # GO has no relations to other ontologies in these editions
+                assert _check_rows is not None or "is_obsolete" not in g.vp, alt
+                assert _check_rows is not None or ("is_external" not in g.vp and not dropped), (alt, dropped)
+            elif onto == "hpo":
+                g, dropped = obo_graph(f, "term_id", namespace=False)
+                # only is_a relations, between terms of HPO
+                assert set(g.ep.relation) == {"is_a"} and not dropped, alt
+                assert _check_rows is not None or "is_obsolete" not in g.vp, alt
+                assert _check_rows is not None or "is_external" not in g.vp, alt
+                del g.ep["relation"]
+            elif onto == "mondo":
+                g, dropped = obo_graph(f, "term_id", namespace=False)
+                # the terms of other ontologies (included in the file since
+                # 2024, or only referenced) and the genes
+                g.vp.is_external = g.new_vp("bool", vals=[not x.startswith("MONDO:")
+                                                          for x in g.vp.term_id])
+            else:
+                # depicted_by: images of the structures (in 2019)
+                g, dropped = obo_graph(f, "term_id", skip=("depicted_by",))
+                g.vp.is_external = g.new_vp("bool", vals=[not x.startswith("UBERON:")
+                                                          for x in g.vp.term_id])
+        if _verbose:
+            print(alt, "left out:", dropped)
+        yield alt, g