1818logger .setLevel (logging .INFO )
1919
2020try :
21- from rdflib import BNode , Graph , Literal , Namespace , URIRef
21+ from rdflib import BNode , Graph , Literal , Namespace , URIRef , Bag
2222 from rdflib .namespace import DC , DCTERMS , FOAF , RDFS
2323except ImportError :
2424 logger .warning ("Please install optional dependencies to use annotation features:" )
@@ -89,6 +89,7 @@ def create_annotation(
8989 subject ,
9090 title = None ,
9191 abstract = None ,
92+ annotation_style : typing .Literal ["miriam" , "biosimulations" ] = "biosimulations" ,
9293 description : typing .Optional [str ] = None ,
9394 keywords : typing .Optional [typing .List [str ]] = None ,
9495 thumbnails : typing .Optional [typing .List [str ]] = None ,
@@ -101,7 +102,7 @@ def create_annotation(
101102 has_property : typing .Optional [typing .Dict [str , str ]] = None ,
102103 is_property_of : typing .Optional [typing .Dict [str , str ]] = None ,
103104 sources : typing .Optional [typing .Dict [str , str ]] = None ,
104- isinstance_of : typing .Optional [typing .Dict [str , str ]] = None ,
105+ is_instance_of : typing .Optional [typing .Dict [str , str ]] = None ,
105106 has_instance : typing .Optional [typing .Dict [str , str ]] = None ,
106107 predecessors : typing .Optional [typing .Dict [str , str ]] = None ,
107108 successors : typing .Optional [typing .Dict [str , str ]] = None ,
@@ -131,8 +132,10 @@ def create_annotation(
131132
132133 For information on the specifications, see:
133134
134- - https://github.com/combine-org/combine-specifications/blob/main/specifications/qualifiers-1.1.md
135- - https://docs.biosimulations.org/concepts/conventions/simulation-project-metadata/
135+ - COMBINE specifications: https://github.com/combine-org/combine-specifications/blob/main/specifications/qualifiers-1.1.md
136+ - Biosimulations guidelines: https://docs.biosimulations.org/concepts/conventions/simulation-project-metadata/
137+ - MIRIAM guidelines: https://drive.google.com/file/d/1JqjcH0T0UTWMuBj-scIMwsyt2z38A0vp/view
138+
136139
137140 Note that:
138141
@@ -157,6 +160,14 @@ def create_annotation(
157160 :type title: str
158161 :param abstract: an abstract
159162 :type abstract: str
163+ :param annotation_style: type of annotation: either "miriam" or
164+ "biosimulations" (default).
165+
166+ There's a difference in the annotation "style" suggested by MIRIAM and
167+ Biosimulations. MIRIAM suggests the use of RDF containers (bags)
168+ wherewas Biosimulations does not. This argument allows the user to
169+ select what style they want to use for the annotation.
170+ :type annotation_style: str
160171 :param description: a longer description
161172 :type description: str
162173 :param keywords: keywords
@@ -181,8 +192,8 @@ def create_annotation(
181192 :type is_property_of: dict(str, str)
182193 :param sources: links to sources (on GitHub and so on)
183194 :type sources: dict(str, str)
184- :param isinstance_of : is an instance of
185- :type isinstance_of : dict(str, str)
195+ :param is_instance_of : is an instance of
196+ :type is_instance_of : dict(str, str)
186197 :param has_instance: has instance of another entity
187198 :type has_instance: dict(str, str)
188199 :param predecessors: predecessors of this entity
@@ -253,146 +264,78 @@ def create_annotation(
253264 doc .add ((subjectobj , COLLEX .thumbnail , URIRef (f"{ fileprefix } /{ t } " )))
254265 if organisms :
255266 doc .bind ("bqbiol:hasTaxon" , BQBIOL + "/hasTaxon" )
256- for idf , label in organisms .items ():
257- hasTaxon = BNode ()
258- doc .add ((subjectobj , BQBIOL .hasTaxon , hasTaxon ))
259- doc .add ((hasTaxon , DC .identifier , URIRef (idf )))
260- doc .add ((hasTaxon , RDFS .label , Literal (label )))
267+ _add_element (doc , subjectobj , organisms , BQBIOL .hasTaxon , annotation_style )
261268 if encodes_other_biology :
262269 doc .bind ("bqbiol:encodes" , BQBIOL + "/encodes" )
263- for idf , label in encodes_other_biology .items ():
264- encodes = BNode ()
265- doc .add ((subjectobj , BQBIOL .encodes , encodes ))
266- doc .add ((encodes , DC .identifier , URIRef (idf )))
267- doc .add ((encodes , RDFS .label , Literal (label )))
270+ _add_element (
271+ doc , subjectobj , encodes_other_biology , BQBIOL .encodes , annotation_style
272+ )
268273 if has_version :
269274 doc .bind ("bqbiol:hasVersion" , BQBIOL + "/hasVersion" )
270- for idf , label in has_version .items ():
271- hasVersion = BNode ()
272- doc .add ((subjectobj , BQBIOL .hasVersion , hasVersion ))
273- doc .add ((hasVersion , DC .identifier , URIRef (idf )))
274- doc .add ((hasVersion , RDFS .label , Literal (label )))
275+ _add_element (doc , subjectobj , has_version , BQBIOL .hasVersion , annotation_style )
275276 if is_version_of :
276277 doc .bind ("bqbiol:isVersionOf" , BQBIOL + "/isVersionOf" )
277- for idf , label in is_version_of .items ():
278- isVersionOf = BNode ()
279- doc .add ((subjectobj , BQBIOL .isVersionOf , isVersionOf ))
280- doc .add ((isVersionOf , DC .identifier , URIRef (idf )))
281- doc .add ((isVersionOf , RDFS .label , Literal (label )))
278+ _add_element (
279+ doc , subjectobj , is_version_of , BQBIOL .isVersionOf , annotation_style
280+ )
282281 if has_part :
283282 doc .bind ("bqbiol:hasPart" , BQBIOL + "/hasPart" )
284- for idf , label in has_part .items ():
285- hasPart = BNode ()
286- doc .add ((subjectobj , BQBIOL .hasPart , hasPart ))
287- doc .add ((hasPart , DC .identifier , URIRef (idf )))
288- doc .add ((hasPart , RDFS .label , Literal (label )))
283+ _add_element (doc , subjectobj , has_part , BQBIOL .hasPart , annotation_style )
289284 if is_part_of :
290285 doc .bind ("bqbiol:isPartOf" , BQBIOL + "/isPartOf" )
291- for idf , label in is_part_of .items ():
292- isPartOf = BNode ()
293- doc .add ((subjectobj , BQBIOL .isPartOf , isPartOf ))
294- doc .add ((isPartOf , DC .identifier , URIRef (idf )))
295- doc .add ((isPartOf , RDFS .label , Literal (label )))
286+ _add_element (doc , subjectobj , is_part_of , BQBIOL .isPartOf , annotation_style )
296287 if has_property :
297288 doc .bind ("bqbiol:hasProperty" , BQBIOL + "/hasProperty" )
298- for idf , label in has_property .items ():
299- hasProperty = BNode ()
300- doc .add ((subjectobj , BQBIOL .hasProperty , hasProperty ))
301- doc .add ((hasProperty , DC .identifier , URIRef (idf )))
302- doc .add ((hasProperty , RDFS .label , Literal (label )))
289+ _add_element (
290+ doc , subjectobj , has_property , BQBIOL .hasProperty , annotation_style
291+ )
303292 if is_property_of :
304293 doc .bind ("bqbiol:isPropertyOf" , BQBIOL + "/isPropertyOf" )
305- for idf , label in is_property_of .items ():
306- isPropertyOf = BNode ()
307- doc .add ((subjectobj , BQBIOL .isPropertyOf , isPropertyOf ))
308- doc .add ((isPropertyOf , DC .identifier , URIRef (idf )))
309- doc .add ((isPropertyOf , RDFS .label , Literal (label )))
294+ _add_element (
295+ doc , subjectobj , is_property_of , BQBIOL .isPropertyOf , annotation_style
296+ )
310297 if sources :
311- for idf , label in sources .items ():
312- s = BNode ()
313- doc .add ((subjectobj , DC .source , s ))
314- doc .add ((s , DC .identifier , URIRef (idf )))
315- doc .add ((s , RDFS .label , Literal (label )))
316- if isinstance_of :
298+ _add_element (doc , subjectobj , sources , DC .source , annotation_style )
299+ if is_instance_of :
317300 doc .bind ("bqmodel:isInstanceOf" , BQMODEL + "/isInstanceOf" )
318- for idf , label in isinstance_of .items ():
319- isInstanceOf = BNode ()
320- doc .add ((subjectobj , BQMODEL .isInstanceOf , isInstanceOf ))
321- doc .add ((isInstanceOf , DC .identifier , URIRef (idf )))
322- doc .add ((isInstanceOf , RDFS .label , Literal (label )))
301+ _add_element (
302+ doc , subjectobj , is_instance_of , BQMODEL .isInstanceOf , annotation_style
303+ )
323304 if has_instance :
324305 doc .bind ("bqmodel:hasInstance" , BQMODEL + "/hasInstance" )
325- for idf , label in has_instance .items ():
326- hasInstance = BNode ()
327- doc .add ((subjectobj , BQMODEL .hasInstance , hasInstance ))
328- doc .add ((hasInstance , DC .identifier , URIRef (idf )))
329- doc .add ((hasInstance , RDFS .label , Literal (label )))
306+ _add_element (
307+ doc , subjectobj , has_instance , BQMODEL .hasInstance , annotation_style
308+ )
330309 if predecessors :
331310 doc .bind ("bqmodel:isDerivedFrom" , BQMODEL + "/isDerivedFrom" )
332- for idf , label in predecessors .items ():
333- isDerivedFrom = BNode ()
334- doc .add ((subjectobj , BQMODEL .isDerivedFrom , isDerivedFrom ))
335- doc .add ((isDerivedFrom , DC .identifier , URIRef (idf )))
336- doc .add ((isDerivedFrom , RDFS .label , Literal (label )))
311+ _add_element (
312+ doc , subjectobj , predecessors , BQMODEL .isDerivedFrom , annotation_style
313+ )
337314 if successors :
338315 doc .bind ("scoro:successor" , SCORO + "/successor" )
339- for idf , label in successors .items ():
340- succ = BNode ()
341- doc .add ((subjectobj , SCORO .successor , succ ))
342- doc .add ((succ , DC .identifier , URIRef (idf )))
343- doc .add ((succ , RDFS .label , Literal (label )))
316+ _add_element (doc , subjectobj , successors , SCORO .successor , annotation_style )
344317 if see_also :
345- for idf , label in see_also .items ():
346- sa = BNode ()
347- doc .add ((subjectobj , RDFS .seeAlso , sa ))
348- doc .add ((sa , DC .identifier , URIRef (idf )))
349- doc .add ((sa , RDFS .label , Literal (label )))
318+ _add_element (doc , subjectobj , see_also , RDFS .seeAlso , annotation_style )
350319 if references :
351- for idf , label in references .items ():
352- r = BNode ()
353- doc .add ((subjectobj , DCTERMS .references , sa ))
354- doc .add ((r , DC .identifier , URIRef (idf )))
355- doc .add ((r , RDFS .label , Literal (label )))
320+ _add_element (doc , subjectobj , references , DCTERMS .references , annotation_style )
356321 if other_ids :
357322 doc .bind ("bqmodel:is" , BQMODEL + "/is" )
358- for idf , label in other_ids .items ():
359- oi = BNode ()
360- doc .add ((subjectobj , BQMODEL .IS , oi ))
361- doc .add ((oi , DC .identifier , URIRef (idf )))
362- doc .add ((oi , RDFS .label , Literal (label )))
323+ _add_element (doc , subjectobj , other_ids , BQMODEL .IS , annotation_style )
363324 if citations :
364325 doc .bind ("bqmodel:isDescribedBy" , BQMODEL + "/isDescribedBy" )
365- for idf , label in citations .items ():
366- cit = BNode ()
367- doc .add ((subjectobj , BQMODEL .isDescribedBy , cit ))
368- doc .add ((cit , DC .identifier , URIRef (idf )))
369- doc .add ((cit , RDFS .label , Literal (label )))
326+ _add_element (
327+ doc , subjectobj , citations , BQMODEL .isDescribedBy , annotation_style
328+ )
370329 if authors :
371- for idf , label in authors .items ():
372- ac = BNode ()
373- doc .add ((subjectobj , DC .creator , ac ))
374- doc .add ((ac , FOAF .name , Literal (idf )))
375- doc .add ((ac , FOAF .label , Literal (label )))
330+ _add_element (doc , subjectobj , authors , DC .creator , annotation_style )
376331 if contributors :
377- for idf , label in contributors .items ():
378- ac = BNode ()
379- doc .add ((subjectobj , DC .contributor , ac ))
380- doc .add ((ac , FOAF .name , Literal (idf )))
381- doc .add ((ac , FOAF .label , Literal (label )))
332+ _add_element (doc , subjectobj , contributors , DC .contributor , annotation_style )
382333 if license :
383334 assert len (license .items ()) == 1
384- for idf , label in license .items ():
385- ac = BNode ()
386- doc .add ((subjectobj , DCTERMS .license , ac ))
387- doc .add ((ac , FOAF .name , Literal (idf )))
388- doc .add ((ac , FOAF .label , Literal (label )))
335+ _add_element (doc , subjectobj , license , DCTERMS .license , annotation_style )
389336 if funders :
390337 doc .bind ("scoro:funder" , SCORO + "/funder" )
391- for idf , label in funders .items ():
392- ac = BNode ()
393- doc .add ((subjectobj , SCORO .funder , ac ))
394- doc .add ((ac , FOAF .name , Literal (idf )))
395- doc .add ((ac , FOAF .label , Literal (label )))
338+ _add_element (doc , subjectobj , funders , SCORO .funder , annotation_style )
396339 if creation_date :
397340 ac = BNode ()
398341 doc .add ((subjectobj , DCTERMS .created , ac ))
@@ -410,3 +353,37 @@ def create_annotation(
410353 print (annotation , file = f )
411354
412355 return annotation
356+
357+
358+ def _add_element (
359+ doc : Graph ,
360+ subjectobj : typing .Union [URIRef , Literal ],
361+ info : typing .Dict [str , str ],
362+ node_type : URIRef ,
363+ annotation_style : str ,
364+ ):
365+ """Add an new element to the RDF annotation
366+
367+ :param doc: main rdf document object
368+ :type doc: RDF.Graph
369+ :param subjectobj: main object being referred to
370+ :type subjectobj: URIRef or Literal
371+ :param info: dictionary of entries and their labels
372+ :type info: dict
373+ :param node_type: node type
374+ :type node_type: URIRef
375+ :param annotation_style: type of annotation
376+ :type annotation_style: str
377+ """
378+ for idf , label in info .items ():
379+ top_node = BNode ()
380+ doc .add ((subjectobj , node_type , top_node ))
381+ if annotation_style == "biosimulations" :
382+ doc .add ((top_node , DC .identifier , URIRef (idf )))
383+ doc .add ((top_node , RDFS .label , Literal (label )))
384+ elif annotation_style == "miriam" :
385+ Bag (doc , top_node , [URIRef (idf )])
386+ else :
387+ raise ValueError (
388+ "Annotation style must either be 'miriam' or 'biosimulations'"
389+ )
0 commit comments