Skip to content

Commit a4a28a9

Browse files
committed
Update reader.py
1 parent 5848ff7 commit a4a28a9

1 file changed

Lines changed: 77 additions & 17 deletions

File tree

src/pyobo/struct/skos/reader.py

Lines changed: 77 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@
55
import curies
66
import rdflib
77
from bioregistry import NormalizedNamableReference, NormalizedNamedReference
8+
from bioregistry.schema import AnnotatedURL
89
from rdflib import DCTERMS, RDF, RDFS, SKOS, VANN, Graph, Node, URIRef
910
from tqdm import tqdm
1011

12+
from pyobo import Annotation
1113
from pyobo.identifier_utils import get_converter
1214
from pyobo.struct import Obo, Term, build_ontology
15+
from pyobo.struct.vocabulary import has_source
1316

1417
__all__ = [
1518
"get_skos_from_rdflib",
@@ -27,16 +30,27 @@ def read_skos(
2730
"""Read a SKOS RDF file."""
2831
graph = rdflib.Graph()
2932
graph.parse(path, format=rdf_format or "ttl")
30-
return get_skos_from_rdflib(graph, prefix=prefix, converter=converter)
33+
return get_skos_from_rdflib(
34+
graph,
35+
prefix=prefix,
36+
converter=converter,
37+
source=path if isinstance(path, str) and path.startswith("http") else None,
38+
)
3139

3240

3341
def get_skos_from_rdflib(
3442
graph: rdflib.Graph,
3543
*,
3644
prefix: str | None = None,
3745
converter: curies.Converter | None = None,
46+
broad_match_becomes_parent: bool = True,
47+
source: str | None = None,
3848
) -> Obo:
39-
"""Extract an ontology from a SKOS RDF graph."""
49+
"""Extract an ontology from a SKOS RDF graph.
50+
51+
:param source: The URL to the SKOS document
52+
:returns: An ontology.
53+
"""
4054
if converter is None:
4155
converter = get_converter()
4256
schemes = list(graph.subjects(RDF.type, SKOS.ConceptScheme))
@@ -62,11 +76,23 @@ def _get_scheme_object_literal(p: Node) -> str | None:
6276
for subject in graph.objects(scheme, SKOS.hasTopConcept)
6377
]
6478
terms = [
65-
get_term(graph, concept, converter=converter)
66-
for concept in tqdm(graph.subjects(RDF.type, SKOS.Concept))
79+
get_term(
80+
graph,
81+
concept,
82+
converter=converter,
83+
broad_match_becomes_parent=broad_match_becomes_parent,
84+
)
85+
for concept in tqdm(
86+
graph.subjects(RDF.type, SKOS.Concept),
87+
desc=f"[{prefix}] SKOS concepts to OWL",
88+
unit="term",
89+
unit_scale=True,
90+
leave=False,
91+
)
6792
]
6893

69-
# FIXME need to put in parents
94+
if source is None:
95+
source = str(scheme)
7096

7197
return build_ontology(
7298
prefix=prefix,
@@ -76,6 +102,7 @@ def _get_scheme_object_literal(p: Node) -> str | None:
76102
name=_get_scheme_object_literal(DCTERMS.title),
77103
description=_get_scheme_object_literal(DCTERMS.description)
78104
or _get_scheme_object_literal(RDFS.comment),
105+
properties=[Annotation.uri(has_source, source)],
79106
)
80107

81108

@@ -90,10 +117,15 @@ def _literal_objects(graph: Graph, subject: Node, predicate: Node) -> list[rdfli
90117
# until we have a better way of representing internationalization, this
91118
# just extracts a language-less or english language literal. otherwise,
92119
# it takes one at random
93-
DEFAULT_LANGUAGES = {"en", None}
120+
DEFAULT_LANGUAGES = {"en", "en-US", None}
94121

95122

96-
def get_term(graph: rdflib.Graph, node: URIRef, converter: curies.Converter) -> Term:
123+
def get_term(
124+
graph: rdflib.Graph,
125+
node: URIRef,
126+
converter: curies.Converter,
127+
broad_match_becomes_parent: bool = True,
128+
) -> Term:
97129
"""Get a term."""
98130
reference_tuple = converter.parse_uri(str(node), strict=True)
99131
labels = _literal_objects(graph, node, SKOS.prefLabel)
@@ -111,26 +143,54 @@ def get_term(graph: rdflib.Graph, node: URIRef, converter: curies.Converter) ->
111143

112144
for exact_match in graph.objects(node, SKOS.exactMatch):
113145
if isinstance(exact_match, URIRef):
114-
term.append_exact_match(converter.parse_uri(str(exact_match), strict=True))
146+
term.append_exact_match(
147+
converter.parse_uri(str(exact_match), strict=True).to_pydantic()
148+
)
115149
for broad_match in graph.objects(node, SKOS.broadMatch):
116150
if isinstance(broad_match, URIRef):
117-
term.append_broad_match(converter.parse_uri(str(broad_match), strict=True))
151+
obj = converter.parse_uri(str(broad_match), strict=True).to_pydantic()
152+
if broad_match_becomes_parent and obj.prefix == term.prefix:
153+
term.append_parent(obj)
154+
else:
155+
term.append_broad_match(obj)
118156
for narrow_match in graph.objects(node, SKOS.narrowMatch):
119157
if isinstance(narrow_match, URIRef):
120-
term.append_narrow_match(converter.parse_uri(str(narrow_match), strict=True))
158+
term.append_narrow_match(
159+
converter.parse_uri(str(narrow_match), strict=True).to_pydantic()
160+
)
121161
for related_match in graph.objects(node, SKOS.relatedMatch):
122162
if isinstance(related_match, URIRef):
123-
term.append_related_match(converter.parse_uri(str(related_match), strict=True))
163+
term.append_related_match(
164+
converter.parse_uri(str(related_match), strict=True).to_pydantic()
165+
)
124166
return term
125167

126168

127169
def _demo() -> None:
128-
import pystow
129-
130-
url = "https://raw.githubusercontent.com/dini-ag-kim/hcrt/refs/heads/master/hcrt.ttl"
131-
graph = pystow.ensure_rdf("dalia", url=url)
132-
ontology = get_skos_from_rdflib(graph)
133-
ontology.write_obo("/Users/cthoyt/Desktop/hcrt.obo")
170+
import bioregistry
171+
from tabulate import tabulate
172+
173+
rows = []
174+
for resource in bioregistry.resources():
175+
match resource.get_download_skos(get_format=True):
176+
case None:
177+
continue
178+
case str() as url:
179+
try:
180+
ontology = read_skos(url, prefix=resource.prefix)
181+
except SyntaxError:
182+
tqdm.write(f"need explicit RDF format for {resource.prefix}")
183+
continue
184+
ontology.write_obo(f"/Users/cthoyt/Desktop/{resource.prefix}.obo")
185+
rows.append((resource.prefix, url, "", len(list(ontology.iter_terms()))))
186+
case AnnotatedURL() as model:
187+
ontology = read_skos(model.url, prefix=resource.prefix, rdf_format=model.rdf_format)
188+
ontology.write_obo(f"/Users/cthoyt/Desktop/{resource.prefix}.obo")
189+
rows.append(
190+
(resource.prefix, model.url, model.rdf_format, len(list(ontology.iter_terms())))
191+
)
192+
193+
tqdm.write(tabulate(rows))
134194

135195

136196
if __name__ == "__main__":

0 commit comments

Comments
 (0)