pyobo 0.11.2__py3-none-any.whl → 0.12.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyobo/.DS_Store +0 -0
- pyobo/__init__.py +95 -20
- pyobo/__main__.py +0 -0
- pyobo/api/__init__.py +81 -10
- pyobo/api/alts.py +52 -42
- pyobo/api/combine.py +39 -0
- pyobo/api/edges.py +68 -0
- pyobo/api/hierarchy.py +231 -203
- pyobo/api/metadata.py +14 -19
- pyobo/api/names.py +207 -127
- pyobo/api/properties.py +117 -117
- pyobo/api/relations.py +68 -94
- pyobo/api/species.py +24 -21
- pyobo/api/typedefs.py +11 -11
- pyobo/api/utils.py +66 -13
- pyobo/api/xrefs.py +107 -114
- pyobo/cli/__init__.py +0 -0
- pyobo/cli/cli.py +35 -50
- pyobo/cli/database.py +210 -160
- pyobo/cli/database_utils.py +155 -0
- pyobo/cli/lookup.py +163 -195
- pyobo/cli/utils.py +19 -6
- pyobo/constants.py +102 -3
- pyobo/getters.py +209 -191
- pyobo/gilda_utils.py +52 -250
- pyobo/identifier_utils/__init__.py +33 -0
- pyobo/identifier_utils/api.py +305 -0
- pyobo/identifier_utils/preprocessing.json +873 -0
- pyobo/identifier_utils/preprocessing.py +27 -0
- pyobo/identifier_utils/relations/__init__.py +8 -0
- pyobo/identifier_utils/relations/api.py +162 -0
- pyobo/identifier_utils/relations/data.json +5824 -0
- pyobo/identifier_utils/relations/data_owl.json +57 -0
- pyobo/identifier_utils/relations/data_rdf.json +1 -0
- pyobo/identifier_utils/relations/data_rdfs.json +7 -0
- pyobo/mocks.py +9 -6
- pyobo/ner/__init__.py +9 -0
- pyobo/ner/api.py +72 -0
- pyobo/ner/normalizer.py +33 -0
- pyobo/obographs.py +48 -40
- pyobo/plugins.py +5 -4
- pyobo/py.typed +0 -0
- pyobo/reader.py +1354 -395
- pyobo/reader_utils.py +155 -0
- pyobo/resource_utils.py +42 -22
- pyobo/resources/__init__.py +0 -0
- pyobo/resources/goc.py +75 -0
- pyobo/resources/goc.tsv +188 -0
- pyobo/resources/ncbitaxon.py +4 -5
- pyobo/resources/ncbitaxon.tsv.gz +0 -0
- pyobo/resources/ro.py +3 -2
- pyobo/resources/ro.tsv +0 -0
- pyobo/resources/so.py +0 -0
- pyobo/resources/so.tsv +0 -0
- pyobo/sources/README.md +12 -8
- pyobo/sources/__init__.py +52 -29
- pyobo/sources/agrovoc.py +0 -0
- pyobo/sources/antibodyregistry.py +11 -12
- pyobo/sources/bigg/__init__.py +13 -0
- pyobo/sources/bigg/bigg_compartment.py +81 -0
- pyobo/sources/bigg/bigg_metabolite.py +229 -0
- pyobo/sources/bigg/bigg_model.py +46 -0
- pyobo/sources/bigg/bigg_reaction.py +77 -0
- pyobo/sources/biogrid.py +1 -2
- pyobo/sources/ccle.py +7 -12
- pyobo/sources/cgnc.py +9 -6
- pyobo/sources/chebi.py +1 -1
- pyobo/sources/chembl/__init__.py +9 -0
- pyobo/sources/{chembl.py → chembl/chembl_compound.py} +13 -25
- pyobo/sources/chembl/chembl_target.py +160 -0
- pyobo/sources/civic_gene.py +55 -15
- pyobo/sources/clinicaltrials.py +160 -0
- pyobo/sources/complexportal.py +24 -24
- pyobo/sources/conso.py +14 -22
- pyobo/sources/cpt.py +0 -0
- pyobo/sources/credit.py +1 -9
- pyobo/sources/cvx.py +27 -5
- pyobo/sources/depmap.py +9 -12
- pyobo/sources/dictybase_gene.py +2 -7
- pyobo/sources/drugbank/__init__.py +9 -0
- pyobo/sources/{drugbank.py → drugbank/drugbank.py} +11 -16
- pyobo/sources/{drugbank_salt.py → drugbank/drugbank_salt.py} +3 -8
- pyobo/sources/drugcentral.py +17 -13
- pyobo/sources/expasy.py +31 -34
- pyobo/sources/famplex.py +13 -18
- pyobo/sources/flybase.py +8 -13
- pyobo/sources/gard.py +62 -0
- pyobo/sources/geonames/__init__.py +9 -0
- pyobo/sources/geonames/features.py +28 -0
- pyobo/sources/{geonames.py → geonames/geonames.py} +87 -26
- pyobo/sources/geonames/utils.py +115 -0
- pyobo/sources/gmt_utils.py +6 -7
- pyobo/sources/go.py +20 -13
- pyobo/sources/gtdb.py +154 -0
- pyobo/sources/gwascentral/__init__.py +9 -0
- pyobo/sources/{gwascentral_phenotype.py → gwascentral/gwascentral_phenotype.py} +5 -7
- pyobo/sources/{gwascentral_study.py → gwascentral/gwascentral_study.py} +1 -7
- pyobo/sources/hgnc/__init__.py +9 -0
- pyobo/sources/{hgnc.py → hgnc/hgnc.py} +56 -70
- pyobo/sources/{hgncgenefamily.py → hgnc/hgncgenefamily.py} +8 -18
- pyobo/sources/icd/__init__.py +9 -0
- pyobo/sources/{icd10.py → icd/icd10.py} +35 -37
- pyobo/sources/icd/icd11.py +148 -0
- pyobo/sources/{icd_utils.py → icd/icd_utils.py} +66 -20
- pyobo/sources/interpro.py +4 -9
- pyobo/sources/itis.py +0 -5
- pyobo/sources/kegg/__init__.py +0 -0
- pyobo/sources/kegg/api.py +16 -38
- pyobo/sources/kegg/genes.py +9 -20
- pyobo/sources/kegg/genome.py +1 -7
- pyobo/sources/kegg/pathway.py +9 -21
- pyobo/sources/mesh.py +58 -24
- pyobo/sources/mgi.py +3 -10
- pyobo/sources/mirbase/__init__.py +11 -0
- pyobo/sources/{mirbase.py → mirbase/mirbase.py} +8 -11
- pyobo/sources/{mirbase_constants.py → mirbase/mirbase_constants.py} +0 -0
- pyobo/sources/{mirbase_family.py → mirbase/mirbase_family.py} +4 -8
- pyobo/sources/{mirbase_mature.py → mirbase/mirbase_mature.py} +3 -7
- pyobo/sources/msigdb.py +74 -39
- pyobo/sources/ncbi/__init__.py +9 -0
- pyobo/sources/ncbi/ncbi_gc.py +162 -0
- pyobo/sources/{ncbigene.py → ncbi/ncbigene.py} +18 -19
- pyobo/sources/nih_reporter.py +60 -0
- pyobo/sources/nlm/__init__.py +9 -0
- pyobo/sources/nlm/nlm_catalog.py +48 -0
- pyobo/sources/nlm/nlm_publisher.py +36 -0
- pyobo/sources/nlm/utils.py +116 -0
- pyobo/sources/npass.py +6 -8
- pyobo/sources/omim_ps.py +11 -4
- pyobo/sources/pathbank.py +4 -8
- pyobo/sources/pfam/__init__.py +9 -0
- pyobo/sources/{pfam.py → pfam/pfam.py} +3 -8
- pyobo/sources/{pfam_clan.py → pfam/pfam_clan.py} +2 -7
- pyobo/sources/pharmgkb/__init__.py +15 -0
- pyobo/sources/pharmgkb/pharmgkb_chemical.py +89 -0
- pyobo/sources/pharmgkb/pharmgkb_disease.py +77 -0
- pyobo/sources/pharmgkb/pharmgkb_gene.py +108 -0
- pyobo/sources/pharmgkb/pharmgkb_pathway.py +63 -0
- pyobo/sources/pharmgkb/pharmgkb_variant.py +84 -0
- pyobo/sources/pharmgkb/utils.py +86 -0
- pyobo/sources/pid.py +1 -6
- pyobo/sources/pombase.py +6 -10
- pyobo/sources/pubchem.py +4 -9
- pyobo/sources/reactome.py +5 -11
- pyobo/sources/rgd.py +11 -16
- pyobo/sources/rhea.py +37 -36
- pyobo/sources/ror.py +69 -42
- pyobo/sources/selventa/__init__.py +0 -0
- pyobo/sources/selventa/schem.py +4 -7
- pyobo/sources/selventa/scomp.py +1 -6
- pyobo/sources/selventa/sdis.py +4 -7
- pyobo/sources/selventa/sfam.py +1 -6
- pyobo/sources/sgd.py +6 -11
- pyobo/sources/signor/__init__.py +7 -0
- pyobo/sources/signor/download.py +41 -0
- pyobo/sources/signor/signor_complexes.py +105 -0
- pyobo/sources/slm.py +12 -15
- pyobo/sources/umls/__init__.py +7 -1
- pyobo/sources/umls/__main__.py +0 -0
- pyobo/sources/umls/get_synonym_types.py +20 -4
- pyobo/sources/umls/sty.py +57 -0
- pyobo/sources/umls/synonym_types.tsv +1 -1
- pyobo/sources/umls/umls.py +18 -22
- pyobo/sources/unimod.py +46 -0
- pyobo/sources/uniprot/__init__.py +1 -1
- pyobo/sources/uniprot/uniprot.py +40 -32
- pyobo/sources/uniprot/uniprot_ptm.py +4 -34
- pyobo/sources/utils.py +3 -2
- pyobo/sources/wikipathways.py +7 -10
- pyobo/sources/zfin.py +5 -10
- pyobo/ssg/__init__.py +12 -16
- pyobo/ssg/base.html +0 -0
- pyobo/ssg/index.html +26 -13
- pyobo/ssg/term.html +12 -2
- pyobo/ssg/typedef.html +0 -0
- pyobo/struct/__init__.py +54 -8
- pyobo/struct/functional/__init__.py +1 -0
- pyobo/struct/functional/dsl.py +2572 -0
- pyobo/struct/functional/macros.py +423 -0
- pyobo/struct/functional/obo_to_functional.py +385 -0
- pyobo/struct/functional/ontology.py +272 -0
- pyobo/struct/functional/utils.py +112 -0
- pyobo/struct/reference.py +331 -136
- pyobo/struct/struct.py +1484 -657
- pyobo/struct/struct_utils.py +1078 -0
- pyobo/struct/typedef.py +162 -210
- pyobo/struct/utils.py +12 -5
- pyobo/struct/vocabulary.py +138 -0
- pyobo/utils/__init__.py +0 -0
- pyobo/utils/cache.py +16 -15
- pyobo/utils/io.py +51 -41
- pyobo/utils/iter.py +5 -5
- pyobo/utils/misc.py +41 -53
- pyobo/utils/ndex_utils.py +0 -0
- pyobo/utils/path.py +73 -70
- pyobo/version.py +3 -3
- pyobo-0.12.1.dist-info/METADATA +671 -0
- pyobo-0.12.1.dist-info/RECORD +201 -0
- pyobo-0.12.1.dist-info/WHEEL +4 -0
- {pyobo-0.11.2.dist-info → pyobo-0.12.1.dist-info}/entry_points.txt +1 -0
- pyobo-0.12.1.dist-info/licenses/LICENSE +21 -0
- pyobo/aws.py +0 -162
- pyobo/cli/aws.py +0 -47
- pyobo/identifier_utils.py +0 -142
- pyobo/normalizer.py +0 -232
- pyobo/registries/__init__.py +0 -16
- pyobo/registries/metaregistry.json +0 -507
- pyobo/registries/metaregistry.py +0 -135
- pyobo/sources/icd11.py +0 -105
- pyobo/xrefdb/__init__.py +0 -1
- pyobo/xrefdb/canonicalizer.py +0 -214
- pyobo/xrefdb/priority.py +0 -59
- pyobo/xrefdb/sources/__init__.py +0 -60
- pyobo/xrefdb/sources/biomappings.py +0 -36
- pyobo/xrefdb/sources/cbms2019.py +0 -91
- pyobo/xrefdb/sources/chembl.py +0 -83
- pyobo/xrefdb/sources/compath.py +0 -82
- pyobo/xrefdb/sources/famplex.py +0 -64
- pyobo/xrefdb/sources/gilda.py +0 -50
- pyobo/xrefdb/sources/intact.py +0 -113
- pyobo/xrefdb/sources/ncit.py +0 -133
- pyobo/xrefdb/sources/pubchem.py +0 -27
- pyobo/xrefdb/sources/wikidata.py +0 -116
- pyobo/xrefdb/xrefs_pipeline.py +0 -180
- pyobo-0.11.2.dist-info/METADATA +0 -711
- pyobo-0.11.2.dist-info/RECORD +0 -157
- pyobo-0.11.2.dist-info/WHEEL +0 -5
- pyobo-0.11.2.dist-info/top_level.txt +0 -1
|
@@ -0,0 +1,1078 @@
|
|
|
1
|
+
"""Utiltites on top of the reference."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import datetime
|
|
6
|
+
import itertools as itt
|
|
7
|
+
import logging
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
from collections import defaultdict
|
|
10
|
+
from collections.abc import Iterable, Mapping, Sequence
|
|
11
|
+
from typing import TYPE_CHECKING, Literal, NamedTuple, TypeAlias, overload
|
|
12
|
+
|
|
13
|
+
import curies
|
|
14
|
+
from curies import ReferenceTuple
|
|
15
|
+
from curies import vocabulary as _v
|
|
16
|
+
from curies.vocabulary import SynonymScope
|
|
17
|
+
from pydantic import BaseModel, ConfigDict
|
|
18
|
+
from ssslm import LiteralMapping
|
|
19
|
+
from typing_extensions import Self
|
|
20
|
+
|
|
21
|
+
from . import vocabulary as v
|
|
22
|
+
from .reference import (
|
|
23
|
+
OBOLiteral,
|
|
24
|
+
Reference,
|
|
25
|
+
Referenced,
|
|
26
|
+
comma_separate_references,
|
|
27
|
+
default_reference,
|
|
28
|
+
get_preferred_curie,
|
|
29
|
+
multi_reference_escape,
|
|
30
|
+
reference_escape,
|
|
31
|
+
reference_or_literal_to_str,
|
|
32
|
+
unspecified_matching,
|
|
33
|
+
)
|
|
34
|
+
from .utils import obo_escape_slim
|
|
35
|
+
from ..identifier_utils import (
|
|
36
|
+
NotCURIEError,
|
|
37
|
+
ParseError,
|
|
38
|
+
_is_valid_identifier,
|
|
39
|
+
_parse_str_or_curie_or_uri_helper,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
if TYPE_CHECKING:
|
|
43
|
+
from pyobo.struct.struct import Synonym, TypeDef
|
|
44
|
+
|
|
45
|
+
__all__ = [
|
|
46
|
+
"AnnotationsDict",
|
|
47
|
+
"HasReferencesMixin",
|
|
48
|
+
"ReferenceHint",
|
|
49
|
+
"Stanza",
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
logger = logging.getLogger(__name__)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class Annotation(NamedTuple):
|
|
56
|
+
"""A tuple representing a predicate-object pair."""
|
|
57
|
+
|
|
58
|
+
predicate: Reference
|
|
59
|
+
value: Reference | OBOLiteral
|
|
60
|
+
|
|
61
|
+
@classmethod
|
|
62
|
+
def float(cls, predicate: Reference, value: float) -> Self:
|
|
63
|
+
"""Return a literal property for a float."""
|
|
64
|
+
return cls(predicate, OBOLiteral.float(value))
|
|
65
|
+
|
|
66
|
+
@staticmethod
|
|
67
|
+
def _sort_key(x: Annotation):
|
|
68
|
+
return x.predicate, _reference_or_literal_key(x.value)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _property_resolve(p: ReferenceHint, o: Reference | Referenced | OBOLiteral) -> Annotation:
|
|
72
|
+
p = _ensure_ref(p)
|
|
73
|
+
if isinstance(o, Referenced):
|
|
74
|
+
o = o.reference
|
|
75
|
+
return Annotation(p, o)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
PropertiesHint: TypeAlias = dict[Reference, list[Reference | OBOLiteral]]
|
|
79
|
+
RelationsHint: TypeAlias = dict[Reference, list[Reference]]
|
|
80
|
+
AnnotationsDict: TypeAlias = dict[Annotation, list[Annotation]]
|
|
81
|
+
# note that an intersection is not valid in ROBOT with a literal, even though this _might_ make sense.
|
|
82
|
+
IntersectionOfHint: TypeAlias = list[Reference | tuple[Reference, Reference]]
|
|
83
|
+
UnionOfHint: TypeAlias = list[Reference]
|
|
84
|
+
|
|
85
|
+
StanzaType: TypeAlias = Literal["Term", "Instance", "TypeDef"]
|
|
86
|
+
|
|
87
|
+
stanza_type_to_prop: dict[StanzaType, Reference] = {
|
|
88
|
+
"Term": v.is_a,
|
|
89
|
+
"Instance": v.rdf_type,
|
|
90
|
+
"TypeDef": v.subproperty_of,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
stanza_type_to_eq_prop: dict[StanzaType, Reference] = {
|
|
94
|
+
"Term": v.equivalent_class,
|
|
95
|
+
"Instance": v.owl_same_as,
|
|
96
|
+
"TypeDef": v.equivalent_property,
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class HasReferencesMixin(ABC):
|
|
101
|
+
"""A class that can report on the references it contains."""
|
|
102
|
+
|
|
103
|
+
def _get_prefixes(self) -> set[str]:
|
|
104
|
+
return set(self._get_references())
|
|
105
|
+
|
|
106
|
+
@abstractmethod
|
|
107
|
+
def _get_references(self) -> dict[str, set[Reference]]:
|
|
108
|
+
raise NotImplementedError
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class Stanza(Referenced, HasReferencesMixin):
|
|
112
|
+
"""A high-level class for stanzas."""
|
|
113
|
+
|
|
114
|
+
reference: Reference
|
|
115
|
+
relationships: RelationsHint
|
|
116
|
+
properties: PropertiesHint
|
|
117
|
+
xrefs: list[Reference]
|
|
118
|
+
parents: list[Reference]
|
|
119
|
+
intersection_of: IntersectionOfHint
|
|
120
|
+
equivalent_to: list[Reference]
|
|
121
|
+
union_of: UnionOfHint
|
|
122
|
+
subsets: list[Reference]
|
|
123
|
+
disjoint_from: list[Reference]
|
|
124
|
+
synonyms: list[Synonym]
|
|
125
|
+
|
|
126
|
+
type: StanzaType
|
|
127
|
+
|
|
128
|
+
_axioms: AnnotationsDict
|
|
129
|
+
|
|
130
|
+
#: An annotation for obsolescence. By default, is None, but this means that it is not obsolete.
|
|
131
|
+
is_obsolete: bool | None
|
|
132
|
+
|
|
133
|
+
#: A description of the entity
|
|
134
|
+
definition: str | None = None
|
|
135
|
+
|
|
136
|
+
@staticmethod
|
|
137
|
+
def _reference(
|
|
138
|
+
reference: Reference, ontology_prefix: str, add_name_comment: bool = False
|
|
139
|
+
) -> str:
|
|
140
|
+
return reference_escape(
|
|
141
|
+
reference, ontology_prefix=ontology_prefix, add_name_comment=add_name_comment
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
def _get_prefixes(self) -> set[str]:
|
|
145
|
+
return set(self._get_references())
|
|
146
|
+
|
|
147
|
+
def _get_references(self) -> dict[str, set[Reference]]:
|
|
148
|
+
"""Get all prefixes used by the typedef."""
|
|
149
|
+
rv: defaultdict[str, set[Reference]] = defaultdict(set)
|
|
150
|
+
|
|
151
|
+
def _add(r: Reference) -> None:
|
|
152
|
+
rv[r.prefix].add(r)
|
|
153
|
+
|
|
154
|
+
_add(self.reference)
|
|
155
|
+
|
|
156
|
+
for synonym in self.synonyms:
|
|
157
|
+
for prefix, references in synonym._get_references().items():
|
|
158
|
+
rv[prefix].update(references)
|
|
159
|
+
|
|
160
|
+
if self.xrefs:
|
|
161
|
+
# xrefs themselves added in the chain below
|
|
162
|
+
_add(v.has_dbxref)
|
|
163
|
+
|
|
164
|
+
for predicate, values in self.properties.items():
|
|
165
|
+
_add(predicate)
|
|
166
|
+
for value in values:
|
|
167
|
+
if isinstance(value, Reference):
|
|
168
|
+
_add(value)
|
|
169
|
+
elif isinstance(value, OBOLiteral):
|
|
170
|
+
_add(v._c(value.datatype))
|
|
171
|
+
for parent in itt.chain(
|
|
172
|
+
self.parents,
|
|
173
|
+
self.union_of,
|
|
174
|
+
self.equivalent_to,
|
|
175
|
+
self.disjoint_from,
|
|
176
|
+
self.subsets,
|
|
177
|
+
self.xrefs,
|
|
178
|
+
):
|
|
179
|
+
_add(parent)
|
|
180
|
+
for intersection_of in self.intersection_of:
|
|
181
|
+
match intersection_of:
|
|
182
|
+
case Reference():
|
|
183
|
+
_add(intersection_of)
|
|
184
|
+
case (intersection_predicate, intersection_value):
|
|
185
|
+
_add(intersection_predicate)
|
|
186
|
+
_add(intersection_value)
|
|
187
|
+
|
|
188
|
+
for rel_predicate, rel_values in self.relationships.items():
|
|
189
|
+
_add(rel_predicate)
|
|
190
|
+
for r in rel_values:
|
|
191
|
+
_add(r)
|
|
192
|
+
for p_o, annotations_ in self._axioms.items():
|
|
193
|
+
_add(p_o.predicate)
|
|
194
|
+
if isinstance(p_o.value, Reference):
|
|
195
|
+
_add(p_o.value)
|
|
196
|
+
for prefix, references in _get_references_from_annotations(annotations_).items():
|
|
197
|
+
rv[prefix].update(references)
|
|
198
|
+
return rv
|
|
199
|
+
|
|
200
|
+
def get_literal_mappings(self) -> list[LiteralMapping]:
|
|
201
|
+
"""Get synonym objects for this term, including one for its label."""
|
|
202
|
+
rv = [_convert_synoynym(self, synonym) for synonym in self.synonyms]
|
|
203
|
+
if self.reference.name:
|
|
204
|
+
rv.append(_get_stanza_name_synonym(self))
|
|
205
|
+
return rv
|
|
206
|
+
|
|
207
|
+
def append_relationship(
|
|
208
|
+
self,
|
|
209
|
+
typedef: ReferenceHint,
|
|
210
|
+
reference: ReferenceHint,
|
|
211
|
+
*,
|
|
212
|
+
annotations: Iterable[Annotation] | None = None,
|
|
213
|
+
) -> Self:
|
|
214
|
+
"""Append a relationship."""
|
|
215
|
+
typedef = _ensure_ref(typedef)
|
|
216
|
+
reference = _ensure_ref(reference)
|
|
217
|
+
self.relationships[typedef].append(reference)
|
|
218
|
+
self._extend_annotations(typedef, reference, annotations)
|
|
219
|
+
return self
|
|
220
|
+
|
|
221
|
+
def _extend_annotations(
|
|
222
|
+
self, p: Reference, o: Reference | OBOLiteral, annotations: Iterable[Annotation] | None
|
|
223
|
+
) -> None:
|
|
224
|
+
if annotations is None:
|
|
225
|
+
return
|
|
226
|
+
for annotation in annotations:
|
|
227
|
+
self._append_annotation(p, o, annotation)
|
|
228
|
+
|
|
229
|
+
def _append_annotation(
|
|
230
|
+
self, p: ReferenceHint, o: Reference | OBOLiteral, annotation: Annotation
|
|
231
|
+
) -> None:
|
|
232
|
+
self._axioms[_property_resolve(p, o)].append(annotation)
|
|
233
|
+
|
|
234
|
+
def append_equivalent(
|
|
235
|
+
self,
|
|
236
|
+
reference: ReferenceHint,
|
|
237
|
+
*,
|
|
238
|
+
annotations: Iterable[Annotation] | None = None,
|
|
239
|
+
) -> Self:
|
|
240
|
+
"""Append an equivalent class axiom."""
|
|
241
|
+
return self.append_relationship(
|
|
242
|
+
stanza_type_to_eq_prop[self.type], reference, annotations=annotations
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
def append_xref(
|
|
246
|
+
self,
|
|
247
|
+
reference: ReferenceHint,
|
|
248
|
+
*,
|
|
249
|
+
mapping_justification: Reference | None = None,
|
|
250
|
+
confidence: float | None = None,
|
|
251
|
+
contributor: Reference | None = None,
|
|
252
|
+
annotations: list[Annotation] | None = None,
|
|
253
|
+
) -> Self:
|
|
254
|
+
"""Append an xref."""
|
|
255
|
+
reference = _ensure_ref(reference)
|
|
256
|
+
self.xrefs.append(reference)
|
|
257
|
+
if annotations is None:
|
|
258
|
+
annotations = []
|
|
259
|
+
annotations.extend(
|
|
260
|
+
self._prepare_mapping_annotations(
|
|
261
|
+
mapping_justification=mapping_justification,
|
|
262
|
+
confidence=confidence,
|
|
263
|
+
contributor=contributor,
|
|
264
|
+
)
|
|
265
|
+
)
|
|
266
|
+
self._extend_annotations(v.has_dbxref, reference, annotations)
|
|
267
|
+
return self
|
|
268
|
+
|
|
269
|
+
def _prepare_mapping_annotations(
|
|
270
|
+
self,
|
|
271
|
+
*,
|
|
272
|
+
mapping_justification: Reference | None = None,
|
|
273
|
+
confidence: float | None = None,
|
|
274
|
+
contributor: Reference | None = None,
|
|
275
|
+
) -> Iterable[Annotation]:
|
|
276
|
+
if mapping_justification is not None:
|
|
277
|
+
yield Annotation(v.mapping_has_justification, mapping_justification)
|
|
278
|
+
if contributor is not None:
|
|
279
|
+
yield Annotation(v.has_contributor, contributor)
|
|
280
|
+
if confidence is not None:
|
|
281
|
+
yield Annotation.float(v.mapping_has_confidence, confidence)
|
|
282
|
+
|
|
283
|
+
def append_parent(
|
|
284
|
+
self,
|
|
285
|
+
reference: ReferenceHint,
|
|
286
|
+
*,
|
|
287
|
+
annotations: Iterable[Annotation] | None = None,
|
|
288
|
+
) -> Self:
|
|
289
|
+
"""Add a parent to this entity."""
|
|
290
|
+
reference = _ensure_ref(reference)
|
|
291
|
+
if reference not in self.parents:
|
|
292
|
+
self.parents.append(reference)
|
|
293
|
+
self._extend_annotations(stanza_type_to_prop[self.type], reference, annotations)
|
|
294
|
+
return self
|
|
295
|
+
|
|
296
|
+
def append_intersection_of(
|
|
297
|
+
self,
|
|
298
|
+
/,
|
|
299
|
+
reference: ReferenceHint | tuple[ReferenceHint, ReferenceHint],
|
|
300
|
+
r2: ReferenceHint | None = None,
|
|
301
|
+
*,
|
|
302
|
+
annotations: Iterable[Annotation] | None = None,
|
|
303
|
+
) -> Self:
|
|
304
|
+
"""Append an intersection of."""
|
|
305
|
+
if r2 is not None:
|
|
306
|
+
if isinstance(reference, tuple):
|
|
307
|
+
raise TypeError
|
|
308
|
+
self.intersection_of.append((_ensure_ref(reference), _ensure_ref(r2)))
|
|
309
|
+
elif isinstance(reference, tuple):
|
|
310
|
+
self.intersection_of.append((_ensure_ref(reference[0]), _ensure_ref(reference[1])))
|
|
311
|
+
else:
|
|
312
|
+
self.intersection_of.append(_ensure_ref(reference))
|
|
313
|
+
return self
|
|
314
|
+
|
|
315
|
+
def append_union_of(self, reference: ReferenceHint) -> Self:
|
|
316
|
+
"""Append to the "union of" list."""
|
|
317
|
+
self.union_of.append(_ensure_ref(reference))
|
|
318
|
+
return self
|
|
319
|
+
|
|
320
|
+
def append_equivalent_to(
|
|
321
|
+
self, reference: ReferenceHint, *, annotations: Iterable[Annotation] | None = None
|
|
322
|
+
) -> Self:
|
|
323
|
+
"""Append to the "equivalent to" list."""
|
|
324
|
+
reference = _ensure_ref(reference)
|
|
325
|
+
self.equivalent_to.append(reference)
|
|
326
|
+
self._extend_annotations(stanza_type_to_eq_prop[self.type], reference, annotations)
|
|
327
|
+
return self
|
|
328
|
+
|
|
329
|
+
def _iterate_intersection_of_obo(self, *, ontology_prefix: str) -> Iterable[str]:
|
|
330
|
+
for element in sorted(self.intersection_of, key=self._intersection_of_key):
|
|
331
|
+
match element:
|
|
332
|
+
case Reference():
|
|
333
|
+
end = reference_escape(
|
|
334
|
+
element, ontology_prefix=ontology_prefix, add_name_comment=True
|
|
335
|
+
)
|
|
336
|
+
case (predicate, object):
|
|
337
|
+
match object:
|
|
338
|
+
case Reference():
|
|
339
|
+
end = multi_reference_escape(
|
|
340
|
+
[predicate, object],
|
|
341
|
+
ontology_prefix=ontology_prefix,
|
|
342
|
+
add_name_comment=True,
|
|
343
|
+
)
|
|
344
|
+
case OBOLiteral():
|
|
345
|
+
raise NotImplementedError
|
|
346
|
+
case _:
|
|
347
|
+
raise TypeError
|
|
348
|
+
yield f"intersection_of: {end}"
|
|
349
|
+
|
|
350
|
+
@staticmethod
|
|
351
|
+
def _intersection_of_key(io: Reference | tuple[Reference, Reference]):
|
|
352
|
+
if isinstance(io, Reference):
|
|
353
|
+
return 0, io
|
|
354
|
+
else:
|
|
355
|
+
return 1, io
|
|
356
|
+
|
|
357
|
+
def _iterate_xref_obo(self, *, ontology_prefix) -> Iterable[str]:
|
|
358
|
+
for xref in sorted(self.xrefs):
|
|
359
|
+
xref_yv = f"xref: {reference_escape(xref, ontology_prefix=ontology_prefix, add_name_comment=False)}"
|
|
360
|
+
xref_yv += _get_obo_trailing_modifiers(
|
|
361
|
+
v.has_dbxref, xref, self._axioms, ontology_prefix=ontology_prefix
|
|
362
|
+
)
|
|
363
|
+
if xref.name:
|
|
364
|
+
xref_yv += f" ! {xref.name}"
|
|
365
|
+
yield xref_yv
|
|
366
|
+
|
|
367
|
+
def _get_annotations(
|
|
368
|
+
self, p: ReferenceHint, o: Reference | Referenced | OBOLiteral | str
|
|
369
|
+
) -> list[Annotation]:
|
|
370
|
+
if isinstance(o, str):
|
|
371
|
+
o = OBOLiteral.string(o)
|
|
372
|
+
return self._axioms.get(_property_resolve(p, o), [])
|
|
373
|
+
|
|
374
|
+
def _get_annotation(
|
|
375
|
+
self, p: ReferenceHint, o: Reference | OBOLiteral, ap: Reference
|
|
376
|
+
) -> Reference | OBOLiteral | None:
|
|
377
|
+
ap_norm = _ensure_ref(ap)
|
|
378
|
+
for annotation in self._get_annotations(p, o):
|
|
379
|
+
if annotation.predicate.pair == ap_norm.pair:
|
|
380
|
+
return annotation.value
|
|
381
|
+
return None
|
|
382
|
+
|
|
383
|
+
def append_property(
|
|
384
|
+
self, prop: Annotation, *, annotations: Iterable[Annotation] | None = None
|
|
385
|
+
) -> Self:
|
|
386
|
+
"""Annotate a property."""
|
|
387
|
+
self.properties[prop.predicate].append(prop.value)
|
|
388
|
+
self._extend_annotations(prop.predicate, prop.value, annotations)
|
|
389
|
+
return self
|
|
390
|
+
|
|
391
|
+
def annotate_literal(
|
|
392
|
+
self,
|
|
393
|
+
prop: ReferenceHint,
|
|
394
|
+
value: OBOLiteral,
|
|
395
|
+
*,
|
|
396
|
+
annotations: Iterable[Annotation] | None = None,
|
|
397
|
+
) -> Self:
|
|
398
|
+
"""Append an object annotation."""
|
|
399
|
+
prop = _ensure_ref(prop)
|
|
400
|
+
self.properties[prop].append(value)
|
|
401
|
+
self._extend_annotations(prop, value, annotations)
|
|
402
|
+
return self
|
|
403
|
+
|
|
404
|
+
def annotate_string(
|
|
405
|
+
self,
|
|
406
|
+
prop: ReferenceHint,
|
|
407
|
+
value: str,
|
|
408
|
+
*,
|
|
409
|
+
annotations: Iterable[Annotation] | None = None,
|
|
410
|
+
language: str | None = None,
|
|
411
|
+
) -> Self:
|
|
412
|
+
"""Append an object annotation."""
|
|
413
|
+
return self.annotate_literal(
|
|
414
|
+
prop, OBOLiteral.string(value, language=language), annotations=annotations
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
def annotate_boolean(
|
|
418
|
+
self,
|
|
419
|
+
prop: ReferenceHint,
|
|
420
|
+
value: bool,
|
|
421
|
+
*,
|
|
422
|
+
annotations: Iterable[Annotation] | None = None,
|
|
423
|
+
) -> Self:
|
|
424
|
+
"""Append an object annotation."""
|
|
425
|
+
return self.annotate_literal(prop, OBOLiteral.boolean(value), annotations=annotations)
|
|
426
|
+
|
|
427
|
+
def annotate_integer(
|
|
428
|
+
self,
|
|
429
|
+
prop: ReferenceHint,
|
|
430
|
+
value: int | str,
|
|
431
|
+
*,
|
|
432
|
+
annotations: Iterable[Annotation] | None = None,
|
|
433
|
+
) -> Self:
|
|
434
|
+
"""Append an object annotation."""
|
|
435
|
+
return self.annotate_literal(prop, OBOLiteral.integer(value), annotations=annotations)
|
|
436
|
+
|
|
437
|
+
def annotate_float(
|
|
438
|
+
self, prop: ReferenceHint, value: float, *, annotations: Iterable[Annotation] | None = None
|
|
439
|
+
) -> Self:
|
|
440
|
+
"""Append a float annotation."""
|
|
441
|
+
return self.annotate_literal(prop, OBOLiteral.float(value), annotations=annotations)
|
|
442
|
+
|
|
443
|
+
def annotate_decimal(
|
|
444
|
+
self, prop: ReferenceHint, value: float, *, annotations: Iterable[Annotation] | None = None
|
|
445
|
+
) -> Self:
|
|
446
|
+
"""Append a decimal annotation."""
|
|
447
|
+
return self.annotate_literal(prop, OBOLiteral.decimal(value), annotations=annotations)
|
|
448
|
+
|
|
449
|
+
def annotate_year(
|
|
450
|
+
self,
|
|
451
|
+
prop: ReferenceHint,
|
|
452
|
+
value: int | str,
|
|
453
|
+
*,
|
|
454
|
+
annotations: Iterable[Annotation] | None = None,
|
|
455
|
+
) -> Self:
|
|
456
|
+
"""Append a year annotation."""
|
|
457
|
+
return self.annotate_literal(prop, OBOLiteral.year(value), annotations=annotations)
|
|
458
|
+
|
|
459
|
+
def annotate_uri(
|
|
460
|
+
self, prop: ReferenceHint, value: str, *, annotations: Iterable[Annotation] | None = None
|
|
461
|
+
) -> Self:
|
|
462
|
+
"""Append a URI annotation."""
|
|
463
|
+
return self.annotate_literal(prop, OBOLiteral.uri(value), annotations=annotations)
|
|
464
|
+
|
|
465
|
+
def annotate_datetime(
|
|
466
|
+
self,
|
|
467
|
+
prop: ReferenceHint,
|
|
468
|
+
value: datetime.datetime | str,
|
|
469
|
+
*,
|
|
470
|
+
annotations: Iterable[Annotation] | None = None,
|
|
471
|
+
) -> Self:
|
|
472
|
+
"""Append a datetime annotation."""
|
|
473
|
+
return self.annotate_literal(prop, OBOLiteral.datetime(value), annotations=annotations)
|
|
474
|
+
|
|
475
|
+
def _iterate_obo_properties(
|
|
476
|
+
self,
|
|
477
|
+
*,
|
|
478
|
+
ontology_prefix: str,
|
|
479
|
+
skip_predicate_objects: Iterable[Reference] | None = None,
|
|
480
|
+
skip_predicate_literals: Iterable[Reference] | None = None,
|
|
481
|
+
typedefs: Mapping[ReferenceTuple, TypeDef],
|
|
482
|
+
) -> Iterable[str]:
|
|
483
|
+
for line in _iterate_obo_relations(
|
|
484
|
+
# the type checker seems to be a bit confused, this is an okay typing since we're
|
|
485
|
+
# passing a more explicit version. The issue is that list is used for the typing,
|
|
486
|
+
# which means it can't narrow properly
|
|
487
|
+
self.properties, # type:ignore
|
|
488
|
+
self._axioms,
|
|
489
|
+
ontology_prefix=ontology_prefix,
|
|
490
|
+
skip_predicate_objects=skip_predicate_objects,
|
|
491
|
+
skip_predicate_literals=skip_predicate_literals,
|
|
492
|
+
typedefs=typedefs,
|
|
493
|
+
):
|
|
494
|
+
yield f"property_value: {line}"
|
|
495
|
+
|
|
496
|
+
def _iterate_obo_relations(
|
|
497
|
+
self, *, ontology_prefix: str, typedefs: Mapping[ReferenceTuple, TypeDef]
|
|
498
|
+
) -> Iterable[str]:
|
|
499
|
+
for line in _iterate_obo_relations(
|
|
500
|
+
# the type checker seems to be a bit confused, this is an okay typing since we're
|
|
501
|
+
# passing a more explicit version. The issue is that list is used for the typing,
|
|
502
|
+
# which means it can't narrow properly
|
|
503
|
+
self.relationships, # type:ignore
|
|
504
|
+
self._axioms,
|
|
505
|
+
ontology_prefix=ontology_prefix,
|
|
506
|
+
typedefs=typedefs,
|
|
507
|
+
):
|
|
508
|
+
yield f"relationship: {line}"
|
|
509
|
+
|
|
510
|
+
def append_subset(self, subset: ReferenceHint) -> Self:
|
|
511
|
+
"""Add a subset."""
|
|
512
|
+
self.subsets.append(_ensure_ref(subset))
|
|
513
|
+
return self
|
|
514
|
+
|
|
515
|
+
def append_disjoint_from(self, reference: ReferenceHint) -> Self:
|
|
516
|
+
"""Add a disjoint from."""
|
|
517
|
+
self.disjoint_from.append(_ensure_ref(reference))
|
|
518
|
+
return self
|
|
519
|
+
|
|
520
|
+
def annotate_object(
|
|
521
|
+
self,
|
|
522
|
+
typedef: ReferenceHint,
|
|
523
|
+
value: ReferenceHint,
|
|
524
|
+
*,
|
|
525
|
+
annotations: Iterable[Annotation] | None = None,
|
|
526
|
+
) -> Self:
|
|
527
|
+
"""Append an object annotation."""
|
|
528
|
+
typedef = _ensure_ref(typedef)
|
|
529
|
+
value = _ensure_ref(value)
|
|
530
|
+
self.properties[typedef].append(value)
|
|
531
|
+
self._extend_annotations(typedef, value, annotations)
|
|
532
|
+
return self
|
|
533
|
+
|
|
534
|
+
def append_contributor(self, reference: ReferenceHint) -> Self:
|
|
535
|
+
"""Append contributor."""
|
|
536
|
+
return self.annotate_object(v.has_contributor, reference)
|
|
537
|
+
|
|
538
|
+
def append_creation_date(self, date: datetime.datetime | str) -> Self:
|
|
539
|
+
"""Append contributor."""
|
|
540
|
+
return self.annotate_datetime(v.obo_creation_date, date)
|
|
541
|
+
|
|
542
|
+
def get_see_also(self) -> list[Reference]:
|
|
543
|
+
"""Get all see also objects."""
|
|
544
|
+
return self.get_property_objects(v.see_also)
|
|
545
|
+
|
|
546
|
+
def get_replaced_by(self) -> list[Reference]:
|
|
547
|
+
"""Get all replaced by."""
|
|
548
|
+
return self.get_property_objects(v.term_replaced_by)
|
|
549
|
+
|
|
550
|
+
def append_replaced_by(
|
|
551
|
+
self, reference: Reference, *, annotations: Iterable[Annotation] | None = None
|
|
552
|
+
) -> Self:
|
|
553
|
+
"""Add a replaced by property."""
|
|
554
|
+
return self.annotate_object(v.term_replaced_by, reference, annotations=annotations)
|
|
555
|
+
|
|
556
|
+
def iterate_relations(self) -> Iterable[tuple[Reference, Reference]]:
|
|
557
|
+
"""Iterate over pairs of typedefs and targets."""
|
|
558
|
+
for typedef, targets in sorted(self.relationships.items()):
|
|
559
|
+
for target in sorted(targets):
|
|
560
|
+
yield typedef, target
|
|
561
|
+
|
|
562
|
+
def iterate_object_properties(self) -> Iterable[tuple[Reference, Reference]]:
|
|
563
|
+
"""Iterate over properties with references as their targets."""
|
|
564
|
+
for predicate, values in self.properties.items():
|
|
565
|
+
for value in values:
|
|
566
|
+
if isinstance(value, Reference):
|
|
567
|
+
yield predicate, value
|
|
568
|
+
|
|
569
|
+
def iterate_literal_properties(self) -> Iterable[tuple[Reference, OBOLiteral]]:
|
|
570
|
+
"""Iterate over properties with literals as their targets."""
|
|
571
|
+
for predicate, values in self.properties.items():
|
|
572
|
+
for value in values:
|
|
573
|
+
if isinstance(value, OBOLiteral):
|
|
574
|
+
yield predicate, value
|
|
575
|
+
|
|
576
|
+
def get_relationships(self, typedef: ReferenceHint) -> list[Reference]:
|
|
577
|
+
"""Get relationships from the given type."""
|
|
578
|
+
return self.relationships.get(_ensure_ref(typedef), [])
|
|
579
|
+
|
|
580
|
+
def get_relationship(self, typedef: ReferenceHint) -> Reference | None:
|
|
581
|
+
"""Get a single relationship of the given type."""
|
|
582
|
+
r = self.get_relationships(typedef)
|
|
583
|
+
if not r:
|
|
584
|
+
return None
|
|
585
|
+
if len(r) > 1:
|
|
586
|
+
raise ValueError
|
|
587
|
+
return r[0]
|
|
588
|
+
|
|
589
|
+
def iterate_relation_targets(self, typedef: ReferenceHint) -> list[Reference]:
|
|
590
|
+
"""Iterate over pairs of typedefs and targets."""
|
|
591
|
+
return sorted(self.relationships.get(_ensure_ref(typedef), []))
|
|
592
|
+
|
|
593
|
+
def get_property_annotations(self) -> list[Annotation]:
|
|
594
|
+
"""Iterate over pairs of property and values."""
|
|
595
|
+
return [
|
|
596
|
+
Annotation(prop, value)
|
|
597
|
+
for prop, values in sorted(self.properties.items())
|
|
598
|
+
for value in sorted(values, key=_reference_or_literal_key)
|
|
599
|
+
]
|
|
600
|
+
|
|
601
|
+
def get_property_values(self, typedef: ReferenceHint) -> list[Reference | OBOLiteral]:
|
|
602
|
+
"""Iterate over references or values."""
|
|
603
|
+
return sorted(self.properties.get(_ensure_ref(typedef), []))
|
|
604
|
+
|
|
605
|
+
def get_property_objects(self, prop: ReferenceHint) -> list[Reference]:
|
|
606
|
+
"""Get properties from the given key."""
|
|
607
|
+
return sorted(
|
|
608
|
+
reference
|
|
609
|
+
for reference in self.properties.get(_ensure_ref(prop), [])
|
|
610
|
+
if isinstance(reference, curies.Reference)
|
|
611
|
+
)
|
|
612
|
+
|
|
613
|
+
def append_exact_synonym(
|
|
614
|
+
self,
|
|
615
|
+
synonym: str | Synonym,
|
|
616
|
+
*,
|
|
617
|
+
type: Reference | Referenced | None = None,
|
|
618
|
+
provenance: Sequence[Reference | OBOLiteral] | None = None,
|
|
619
|
+
annotations: Iterable[Annotation] | None = None,
|
|
620
|
+
language: str | None = None,
|
|
621
|
+
) -> Self:
|
|
622
|
+
"""Add an exact synonym."""
|
|
623
|
+
return self.append_synonym(
|
|
624
|
+
synonym,
|
|
625
|
+
type=type,
|
|
626
|
+
specificity="EXACT",
|
|
627
|
+
provenance=provenance,
|
|
628
|
+
annotations=annotations,
|
|
629
|
+
language=language,
|
|
630
|
+
)
|
|
631
|
+
|
|
632
|
+
def append_synonym(
|
|
633
|
+
self,
|
|
634
|
+
synonym: str | Synonym,
|
|
635
|
+
*,
|
|
636
|
+
type: Reference | Referenced | None = None,
|
|
637
|
+
specificity: SynonymScope | None = None,
|
|
638
|
+
provenance: Sequence[Reference | OBOLiteral] | None = None,
|
|
639
|
+
annotations: Iterable[Annotation] | None = None,
|
|
640
|
+
language: str | None = None,
|
|
641
|
+
) -> Self:
|
|
642
|
+
"""Add a synonym."""
|
|
643
|
+
if isinstance(type, Referenced):
|
|
644
|
+
type = type.reference
|
|
645
|
+
if isinstance(synonym, str):
|
|
646
|
+
from pyobo.struct.struct import Synonym
|
|
647
|
+
|
|
648
|
+
synonym = Synonym(
|
|
649
|
+
synonym,
|
|
650
|
+
type=type,
|
|
651
|
+
specificity=specificity,
|
|
652
|
+
provenance=list(provenance or []),
|
|
653
|
+
annotations=list(annotations or []),
|
|
654
|
+
language=language,
|
|
655
|
+
)
|
|
656
|
+
self.synonyms.append(synonym)
|
|
657
|
+
return self
|
|
658
|
+
|
|
659
|
+
def append_alt(
|
|
660
|
+
self, alt: Reference, *, annotations: Iterable[Annotation] | None = None
|
|
661
|
+
) -> Self:
|
|
662
|
+
"""Add an alternative identifier."""
|
|
663
|
+
return self.annotate_object(v.alternative_term, alt, annotations=annotations)
|
|
664
|
+
|
|
665
|
+
def append_see_also(
|
|
666
|
+
self, reference: ReferenceHint, *, annotations: Iterable[Annotation] | None = None
|
|
667
|
+
) -> Self:
|
|
668
|
+
"""Add a see also property."""
|
|
669
|
+
_reference = _ensure_ref(reference)
|
|
670
|
+
return self.annotate_object(v.see_also, _reference, annotations=annotations)
|
|
671
|
+
|
|
672
|
+
def append_comment(
|
|
673
|
+
self,
|
|
674
|
+
value: str,
|
|
675
|
+
*,
|
|
676
|
+
annotations: Iterable[Annotation] | None = None,
|
|
677
|
+
language: str | None = None,
|
|
678
|
+
) -> Self:
|
|
679
|
+
"""Add a comment property."""
|
|
680
|
+
return self.annotate_string(v.comment, value, annotations=annotations, language=language)
|
|
681
|
+
|
|
682
|
+
@property
|
|
683
|
+
def alt_ids(self) -> Sequence[Reference]:
|
|
684
|
+
"""Get alternative terms."""
|
|
685
|
+
return tuple(self.get_property_objects(v.alternative_term))
|
|
686
|
+
|
|
687
|
+
def get_edges(self) -> list[tuple[Reference, Reference]]:
|
|
688
|
+
"""Get edges."""
|
|
689
|
+
return list(self._iter_edges())
|
|
690
|
+
|
|
691
|
+
def _iter_parents(self) -> Iterable[tuple[Reference, Reference]]:
|
|
692
|
+
parent_prop = stanza_type_to_prop[self.type]
|
|
693
|
+
for parent in itt.chain(self.parents, self.union_of):
|
|
694
|
+
yield parent_prop, parent
|
|
695
|
+
|
|
696
|
+
def _iter_intersections(self) -> Iterable[tuple[Reference, Reference]]:
|
|
697
|
+
parent_prop = stanza_type_to_prop[self.type]
|
|
698
|
+
for intersection_of in self.intersection_of:
|
|
699
|
+
match intersection_of:
|
|
700
|
+
case Reference():
|
|
701
|
+
yield parent_prop, intersection_of
|
|
702
|
+
case (predicate, target):
|
|
703
|
+
yield predicate, target
|
|
704
|
+
|
|
705
|
+
def _iter_edges(self) -> Iterable[tuple[Reference, Reference]]:
|
|
706
|
+
# The following are "object" properties, meaning
|
|
707
|
+
# they're part of the definition of the object
|
|
708
|
+
yield from self.iterate_relations()
|
|
709
|
+
yield from self._iter_parents()
|
|
710
|
+
yield from self._iter_intersections()
|
|
711
|
+
for equivalent_to in self.equivalent_to:
|
|
712
|
+
yield stanza_type_to_eq_prop[self.type], equivalent_to
|
|
713
|
+
|
|
714
|
+
# The following are "annotation" properties
|
|
715
|
+
for subset in self.subsets:
|
|
716
|
+
yield v.in_subset, subset
|
|
717
|
+
yield from self.iterate_object_properties()
|
|
718
|
+
for xref_reference in self.xrefs:
|
|
719
|
+
yield v.has_dbxref, xref_reference
|
|
720
|
+
|
|
721
|
+
# TODO disjoint_from
|
|
722
|
+
|
|
723
|
+
# docstr-coverage:excused `overload`
|
|
724
|
+
@overload
|
|
725
|
+
def get_mappings(
|
|
726
|
+
self, *, include_xrefs: bool = ..., add_context: Literal[True] = True
|
|
727
|
+
) -> list[tuple[Reference, Reference, MappingContext]]: ...
|
|
728
|
+
|
|
729
|
+
# docstr-coverage:excused `overload`
|
|
730
|
+
@overload
|
|
731
|
+
def get_mappings(
|
|
732
|
+
self, *, include_xrefs: bool = ..., add_context: Literal[False] = False
|
|
733
|
+
) -> list[tuple[Reference, Reference]]: ...
|
|
734
|
+
|
|
735
|
+
def get_mappings(
|
|
736
|
+
self, *, include_xrefs: bool = True, add_context: bool = False
|
|
737
|
+
) -> list[tuple[Reference, Reference]] | list[tuple[Reference, Reference, MappingContext]]:
|
|
738
|
+
"""Get mappings with preferred curies."""
|
|
739
|
+
rows = []
|
|
740
|
+
for predicate in v.extended_match_typedefs:
|
|
741
|
+
for xref_reference in itt.chain(
|
|
742
|
+
self.get_property_objects(predicate), self.get_relationships(predicate)
|
|
743
|
+
):
|
|
744
|
+
rows.append((predicate, xref_reference))
|
|
745
|
+
if include_xrefs:
|
|
746
|
+
for xref_reference in self.xrefs:
|
|
747
|
+
rows.append((v.has_dbxref, xref_reference))
|
|
748
|
+
for equivalent_to in self.equivalent_to:
|
|
749
|
+
rows.append((v.equivalent_class, equivalent_to))
|
|
750
|
+
rv = sorted(set(rows))
|
|
751
|
+
if not add_context:
|
|
752
|
+
return rv
|
|
753
|
+
return [(k, v, self._get_mapping_context(k, v)) for k, v in rv]
|
|
754
|
+
|
|
755
|
+
def _get_object_annotation_target(
|
|
756
|
+
self, p: Reference, o: Reference | OBOLiteral, ap: Reference
|
|
757
|
+
) -> Reference | None:
|
|
758
|
+
match self._get_annotation(p, o, ap):
|
|
759
|
+
case OBOLiteral():
|
|
760
|
+
raise TypeError
|
|
761
|
+
case Reference() as target:
|
|
762
|
+
return target
|
|
763
|
+
case None:
|
|
764
|
+
return None
|
|
765
|
+
case _:
|
|
766
|
+
raise TypeError
|
|
767
|
+
|
|
768
|
+
def _get_str_annotation_target(
|
|
769
|
+
self, p: Reference, o: Reference | OBOLiteral, ap: Reference
|
|
770
|
+
) -> str | None:
|
|
771
|
+
match self._get_annotation(p, o, ap):
|
|
772
|
+
case OBOLiteral(value, _):
|
|
773
|
+
return value
|
|
774
|
+
case Reference():
|
|
775
|
+
raise TypeError
|
|
776
|
+
case None:
|
|
777
|
+
return None
|
|
778
|
+
case _:
|
|
779
|
+
raise TypeError
|
|
780
|
+
|
|
781
|
+
def _get_mapping_context(self, p: Reference, o: Reference) -> MappingContext:
|
|
782
|
+
return MappingContext(
|
|
783
|
+
justification=self._get_object_annotation_target(p, o, v.mapping_has_justification)
|
|
784
|
+
or unspecified_matching,
|
|
785
|
+
contributor=self._get_object_annotation_target(p, o, v.has_contributor),
|
|
786
|
+
confidence=self._get_str_annotation_target(p, o, v.mapping_has_confidence),
|
|
787
|
+
)
|
|
788
|
+
|
|
789
|
+
def _definition_fp(self) -> str:
|
|
790
|
+
definition = obo_escape_slim(self.definition) if self.definition else ""
|
|
791
|
+
dp = self._get_definition_provenance()
|
|
792
|
+
if dp:
|
|
793
|
+
return f'"{definition}" [{comma_separate_references(dp)}]'
|
|
794
|
+
else:
|
|
795
|
+
return f'"{definition}"'
|
|
796
|
+
|
|
797
|
+
def _get_definition_provenance(self) -> Sequence[Reference | OBOLiteral]:
|
|
798
|
+
if self.definition is None:
|
|
799
|
+
return []
|
|
800
|
+
return [
|
|
801
|
+
annotation.value
|
|
802
|
+
for annotation in self._get_annotations(v.has_description, self.definition)
|
|
803
|
+
if annotation.predicate.pair == v.has_dbxref.pair
|
|
804
|
+
]
|
|
805
|
+
|
|
806
|
+
@property
|
|
807
|
+
def provenance(self) -> Sequence[Reference | OBOLiteral]:
|
|
808
|
+
"""Get definition provenance."""
|
|
809
|
+
# return as a tuple to make sure nobody is appending on it
|
|
810
|
+
return (
|
|
811
|
+
*self.get_property_objects(v.has_citation),
|
|
812
|
+
# This gets all of the xrefs on _any_ axiom,
|
|
813
|
+
# which includes the definition provenance
|
|
814
|
+
*(
|
|
815
|
+
annotation.value
|
|
816
|
+
for annotation in itt.chain.from_iterable(self._axioms.values())
|
|
817
|
+
if annotation.predicate.pair == v.has_dbxref.pair
|
|
818
|
+
),
|
|
819
|
+
)
|
|
820
|
+
|
|
821
|
+
def append_definition_xref(self, reference: ReferenceHint) -> Self:
|
|
822
|
+
"""Add a reference to this term's definition."""
|
|
823
|
+
if not self.definition:
|
|
824
|
+
raise ValueError("can not append definition provenance if no definition is set")
|
|
825
|
+
self._append_annotation(
|
|
826
|
+
v.has_description,
|
|
827
|
+
OBOLiteral.string(self.definition),
|
|
828
|
+
Annotation(v.has_dbxref, _ensure_ref(reference)),
|
|
829
|
+
)
|
|
830
|
+
return self
|
|
831
|
+
|
|
832
|
+
def append_provenance(
|
|
833
|
+
self,
|
|
834
|
+
reference: Reference,
|
|
835
|
+
*,
|
|
836
|
+
annotations: Iterable[Annotation] | None = None,
|
|
837
|
+
) -> Self:
|
|
838
|
+
"""Append a citation."""
|
|
839
|
+
return self.annotate_object(v.has_citation, reference, annotations=annotations)
|
|
840
|
+
|
|
841
|
+
|
|
842
|
+
ReferenceHint: TypeAlias = (
|
|
843
|
+
Reference | Referenced | curies.Reference | curies.NamedReference | tuple[str, str] | str
|
|
844
|
+
)
|
|
845
|
+
|
|
846
|
+
|
|
847
|
+
def _ensure_ref(
|
|
848
|
+
reference: ReferenceHint,
|
|
849
|
+
*,
|
|
850
|
+
ontology_prefix: str | None = None,
|
|
851
|
+
) -> Reference:
|
|
852
|
+
if isinstance(reference, Referenced):
|
|
853
|
+
return reference.reference
|
|
854
|
+
if isinstance(reference, tuple):
|
|
855
|
+
return Reference(prefix=reference[0], identifier=reference[1])
|
|
856
|
+
if isinstance(reference, Reference):
|
|
857
|
+
return reference
|
|
858
|
+
if isinstance(reference, curies.NamedReference):
|
|
859
|
+
return Reference(
|
|
860
|
+
prefix=reference.prefix, identifier=reference.identifier, name=reference.name
|
|
861
|
+
)
|
|
862
|
+
if isinstance(reference, curies.Reference):
|
|
863
|
+
return Reference(prefix=reference.prefix, identifier=reference.identifier)
|
|
864
|
+
|
|
865
|
+
match _parse_str_or_curie_or_uri_helper(reference, ontology_prefix=ontology_prefix):
|
|
866
|
+
case Reference() as parsed_reference:
|
|
867
|
+
return parsed_reference
|
|
868
|
+
case NotCURIEError() as exc:
|
|
869
|
+
if ontology_prefix and _is_valid_identifier(reference):
|
|
870
|
+
return default_reference(ontology_prefix, reference)
|
|
871
|
+
else:
|
|
872
|
+
raise exc
|
|
873
|
+
case ParseError() as exc:
|
|
874
|
+
raise exc
|
|
875
|
+
|
|
876
|
+
raise TypeError
|
|
877
|
+
|
|
878
|
+
|
|
879
|
+
def _chain_tag(
|
|
880
|
+
tag: str, chains: list[list[Reference]] | None, ontology_prefix: str
|
|
881
|
+
) -> Iterable[str]:
|
|
882
|
+
for chain in chains or []:
|
|
883
|
+
yield f"{tag}: {multi_reference_escape(chain, ontology_prefix=ontology_prefix, add_name_comment=True)}"
|
|
884
|
+
|
|
885
|
+
|
|
886
|
+
def _tag_property_targets(
|
|
887
|
+
tag: str, stanza: Stanza, prod: ReferenceHint, *, ontology_prefix: str
|
|
888
|
+
) -> Iterable[str]:
|
|
889
|
+
for x in stanza.get_property_values(_ensure_ref(prod)):
|
|
890
|
+
if isinstance(x, Reference):
|
|
891
|
+
yield f"{tag}: {reference_escape(x, ontology_prefix=ontology_prefix, add_name_comment=True)}"
|
|
892
|
+
|
|
893
|
+
|
|
894
|
+
def _iterate_obo_relations(
|
|
895
|
+
relations: Mapping[Reference, Sequence[Reference | OBOLiteral]],
|
|
896
|
+
annotations: AnnotationsDict,
|
|
897
|
+
*,
|
|
898
|
+
ontology_prefix: str,
|
|
899
|
+
skip_predicate_objects: Iterable[Reference] | None = None,
|
|
900
|
+
skip_predicate_literals: Iterable[Reference] | None = None,
|
|
901
|
+
typedefs: Mapping[ReferenceTuple, TypeDef],
|
|
902
|
+
) -> Iterable[str]:
|
|
903
|
+
"""Iterate over relations/property values for OBO."""
|
|
904
|
+
skip_predicate_objects = set(skip_predicate_objects or [])
|
|
905
|
+
skip_predicate_literals = set(skip_predicate_literals or [])
|
|
906
|
+
for predicate, values in sorted(relations.items()):
|
|
907
|
+
_typedef_warn(prefix=ontology_prefix, predicate=predicate, typedefs=typedefs)
|
|
908
|
+
pc = reference_escape(predicate, ontology_prefix=ontology_prefix)
|
|
909
|
+
start = f"{pc} "
|
|
910
|
+
for value in sorted(values, key=_reference_or_literal_key):
|
|
911
|
+
match value:
|
|
912
|
+
case OBOLiteral(dd, datatype, _language):
|
|
913
|
+
if predicate in skip_predicate_literals:
|
|
914
|
+
continue
|
|
915
|
+
# TODO how to clean/escape value?
|
|
916
|
+
end = f'"{dd}" {get_preferred_curie(datatype)}'
|
|
917
|
+
name = None
|
|
918
|
+
case curies.Reference(): # it's a reference
|
|
919
|
+
if predicate in skip_predicate_objects:
|
|
920
|
+
# this allows us to special case out iterating over
|
|
921
|
+
# ones that are configured with their own tags
|
|
922
|
+
continue
|
|
923
|
+
end = reference_escape(value, ontology_prefix=ontology_prefix)
|
|
924
|
+
name = value.name
|
|
925
|
+
case _:
|
|
926
|
+
raise TypeError(f"got unexpected value: {values}")
|
|
927
|
+
end += _get_obo_trailing_modifiers(
|
|
928
|
+
predicate, value, annotations, ontology_prefix=ontology_prefix
|
|
929
|
+
)
|
|
930
|
+
if predicate.name and name:
|
|
931
|
+
end += f" ! {predicate.name} {name}"
|
|
932
|
+
yield start + end
|
|
933
|
+
|
|
934
|
+
|
|
935
|
+
def _reference_or_literal_key(x: Reference | OBOLiteral) -> tuple[int, Reference | OBOLiteral]:
|
|
936
|
+
if isinstance(x, Reference):
|
|
937
|
+
return 0, x
|
|
938
|
+
else:
|
|
939
|
+
return 1, x
|
|
940
|
+
|
|
941
|
+
|
|
942
|
+
def _get_obo_trailing_modifiers(
|
|
943
|
+
p: ReferenceHint,
|
|
944
|
+
o: Reference | OBOLiteral,
|
|
945
|
+
annotations_dict: AnnotationsDict,
|
|
946
|
+
*,
|
|
947
|
+
ontology_prefix: str,
|
|
948
|
+
) -> str:
|
|
949
|
+
"""Lookup then format a sequence of annotations for OBO trailing modifiers."""
|
|
950
|
+
if annotations := annotations_dict.get(_property_resolve(p, o), []):
|
|
951
|
+
return _format_obo_trailing_modifiers(annotations, ontology_prefix=ontology_prefix)
|
|
952
|
+
return ""
|
|
953
|
+
|
|
954
|
+
|
|
955
|
+
def _format_obo_trailing_modifiers(
|
|
956
|
+
annotations: Sequence[Annotation], *, ontology_prefix: str
|
|
957
|
+
) -> str:
|
|
958
|
+
"""Format a sequence of annotations for OBO trailing modifiers.
|
|
959
|
+
|
|
960
|
+
:param annotations: A list of annnotations
|
|
961
|
+
:param ontology_prefix: The ontology prefix
|
|
962
|
+
|
|
963
|
+
:returns: The trailing modifiers string
|
|
964
|
+
|
|
965
|
+
See https://owlcollab.github.io/oboformat/doc/GO.format.obo-1_4.html#S.1.4 trailing
|
|
966
|
+
modifiers can be both annotations and some other implementation-specific things, so
|
|
967
|
+
split up the place where annotations are put in here.
|
|
968
|
+
"""
|
|
969
|
+
modifiers: list[tuple[str, str]] = []
|
|
970
|
+
for prop in sorted(annotations, key=Annotation._sort_key):
|
|
971
|
+
left = reference_escape(prop.predicate, ontology_prefix=ontology_prefix)
|
|
972
|
+
match prop.value:
|
|
973
|
+
case Reference():
|
|
974
|
+
right = reference_escape(prop.value, ontology_prefix=ontology_prefix)
|
|
975
|
+
case OBOLiteral(value, _datatype, _language):
|
|
976
|
+
right = value
|
|
977
|
+
modifiers.append((left, right))
|
|
978
|
+
inner = ", ".join(f"{key}={value}" for key, value in modifiers)
|
|
979
|
+
return " {" + inner + "}"
|
|
980
|
+
|
|
981
|
+
|
|
982
|
+
#: A set of warnings, used to make sure we don't show the same one over and over
|
|
983
|
+
_TYPEDEF_WARNINGS: set[tuple[str, Reference]] = set()
|
|
984
|
+
|
|
985
|
+
|
|
986
|
+
def _typedef_warn(
|
|
987
|
+
prefix: str, predicate: Reference, typedefs: Mapping[ReferenceTuple, TypeDef]
|
|
988
|
+
) -> None:
|
|
989
|
+
from pyobo.struct.typedef import default_typedefs
|
|
990
|
+
|
|
991
|
+
if predicate.pair in default_typedefs or predicate.pair in typedefs:
|
|
992
|
+
return None
|
|
993
|
+
key = prefix, predicate
|
|
994
|
+
if key not in _TYPEDEF_WARNINGS:
|
|
995
|
+
_TYPEDEF_WARNINGS.add(key)
|
|
996
|
+
if predicate.prefix == "obo":
|
|
997
|
+
# Throw our hands up in the air. By using `obo` as the prefix,
|
|
998
|
+
# we already threw using "real" definitions out the window
|
|
999
|
+
logger.warning(
|
|
1000
|
+
f"[{prefix}] predicate with OBO prefix not defined: {predicate.curie}."
|
|
1001
|
+
f"\n\tThis might be because you used an unqualified prefix in an OBO file, "
|
|
1002
|
+
f"which automatically gets an OBO prefix."
|
|
1003
|
+
)
|
|
1004
|
+
else:
|
|
1005
|
+
logger.warning(f"[{prefix}] typedef not defined: {predicate.curie}")
|
|
1006
|
+
|
|
1007
|
+
|
|
1008
|
+
class MappingContext(BaseModel):
|
|
1009
|
+
"""Context for a mapping, corresponding to SSSOM."""
|
|
1010
|
+
|
|
1011
|
+
justification: Reference = unspecified_matching
|
|
1012
|
+
contributor: Reference | None = None
|
|
1013
|
+
confidence: float | None = None
|
|
1014
|
+
|
|
1015
|
+
model_config = ConfigDict(
|
|
1016
|
+
frozen=True, # Makes the model immutable and hashable
|
|
1017
|
+
)
|
|
1018
|
+
|
|
1019
|
+
|
|
1020
|
+
def _get_prefixes_from_annotations(annotations: Iterable[Annotation]) -> set[str]:
|
|
1021
|
+
return set(_get_references_from_annotations(annotations))
|
|
1022
|
+
|
|
1023
|
+
|
|
1024
|
+
def _get_references_from_annotations(
|
|
1025
|
+
annotations: Iterable[Annotation],
|
|
1026
|
+
) -> dict[str, set[Reference]]:
|
|
1027
|
+
rv: defaultdict[str, set[Reference]] = defaultdict(set)
|
|
1028
|
+
for left, right in annotations:
|
|
1029
|
+
rv[left.prefix].add(left)
|
|
1030
|
+
if isinstance(right, Reference):
|
|
1031
|
+
rv[right.prefix].add(right)
|
|
1032
|
+
return dict(rv)
|
|
1033
|
+
|
|
1034
|
+
|
|
1035
|
+
def _get_stanza_name_synonym(stanza: Stanza) -> LiteralMapping:
|
|
1036
|
+
return LiteralMapping(
|
|
1037
|
+
text=stanza.reference.name,
|
|
1038
|
+
reference=stanza.reference,
|
|
1039
|
+
predicate=_v.has_label,
|
|
1040
|
+
type=None,
|
|
1041
|
+
provenance=[p for p in stanza.provenance if isinstance(p, curies.Reference)],
|
|
1042
|
+
contributor=None, # TODO
|
|
1043
|
+
comment=None, # TODO
|
|
1044
|
+
source=stanza.reference.prefix,
|
|
1045
|
+
date=None, # TODO
|
|
1046
|
+
)
|
|
1047
|
+
|
|
1048
|
+
|
|
1049
|
+
def _convert_synoynym(stanza: Stanza, synonym: Synonym) -> LiteralMapping:
|
|
1050
|
+
o = OBOLiteral.string(synonym.name, language=synonym.language)
|
|
1051
|
+
# TODO make this indexing reusable? similar code used for SSSOM export
|
|
1052
|
+
idx: dict[Reference, Reference | OBOLiteral] = {
|
|
1053
|
+
annotation.predicate: annotation.value
|
|
1054
|
+
for annotation in stanza._get_annotations(synonym.predicate, o)
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
comment = _safe_str(idx.get(v.comment))
|
|
1058
|
+
contributor = _safe_str(idx.get(v.has_contributor))
|
|
1059
|
+
date = _safe_str(idx.get(v.has_date))
|
|
1060
|
+
|
|
1061
|
+
return LiteralMapping(
|
|
1062
|
+
text=synonym.name,
|
|
1063
|
+
language=synonym.language,
|
|
1064
|
+
reference=stanza.reference,
|
|
1065
|
+
predicate=synonym.predicate,
|
|
1066
|
+
type=synonym.type,
|
|
1067
|
+
provenance=[p for p in synonym.provenance if isinstance(p, curies.Reference)],
|
|
1068
|
+
contributor=contributor,
|
|
1069
|
+
comment=comment,
|
|
1070
|
+
source=stanza.reference.prefix,
|
|
1071
|
+
date=date,
|
|
1072
|
+
)
|
|
1073
|
+
|
|
1074
|
+
|
|
1075
|
+
def _safe_str(x: Reference | OBOLiteral | None) -> str | None:
|
|
1076
|
+
if x is None:
|
|
1077
|
+
return None
|
|
1078
|
+
return reference_or_literal_to_str(x)
|