55import curies
66import rdflib
77from bioregistry import NormalizedNamableReference , NormalizedNamedReference
8+ from bioregistry .schema import AnnotatedURL
89from rdflib import DCTERMS , RDF , RDFS , SKOS , VANN , Graph , Node , URIRef
910from tqdm import tqdm
1011
12+ from pyobo import Annotation
1113from pyobo .identifier_utils import get_converter
1214from 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
3341def 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
127169def _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
136196if __name__ == "__main__" :
0 commit comments