4
4
"""
5
5
6
6
import os
7
+ import string
7
8
import uuid
9
+ import warnings
8
10
9
11
from io import StringIO
10
12
from rdflib import Graph , Literal , URIRef
11
13
from rdflib .graph import Seq
12
- from rdflib .namespace import XSD , RDF
14
+ from rdflib .namespace import XSD , RDF , RDFS
13
15
14
16
import yaml
15
17
@@ -57,14 +59,32 @@ class RDFWriter(object):
57
59
"""
58
60
A writer to parse odML files into RDF documents.
59
61
62
+ Use the 'rdf_subclassing' flag to disable default usage of Section type conversion to
63
+ RDF Subclasses.
64
+ Provide a custom Section type to RDF Subclass Name mapping dictionary via the
65
+ 'custom_subclasses' attribute to add custom or overwrite default RDF Subclass mappings.
66
+
60
67
Usage:
61
68
RDFWriter(odml_docs).get_rdf_str('turtle')
62
69
RDFWriter(odml_docs).write_file("/output_path", "rdf_format")
70
+
71
+ RDFWriter(odml_docs, rdf_subclassing=False).write_file("path", "rdf_format")
72
+ RDFWriter(odml_docs, custom_subclasses=custom_dict).write_file("path", "rdf_format")
63
73
"""
64
74
65
- def __init__ (self , odml_documents ):
75
+ def __init__ (self , odml_documents , rdf_subclassing = True , custom_subclasses = None ):
66
76
"""
67
77
:param odml_documents: list of odML documents
78
+ :param rdf_subclassing: Flag whether Section types should be converted to RDF Subclasses
79
+ for enhanced SPARQL queries. Default is 'True'.
80
+ :param custom_subclasses: A dict where the keys reference a Section type and the
81
+ corresponding values reference an RDF Class Name. When exporting
82
+ a Section of a type contained in this dict, the resulting RDF
83
+ Instance will be of the corresponding Class and this Class will
84
+ be added as a Subclass of RDF Class "odml:Section" to the
85
+ RDF document.
86
+ Key:value pairs of the "custom_subclasses" dict will overwrite
87
+ existing key:value pairs of the default subclassing dict.
68
88
"""
69
89
if not isinstance (odml_documents , list ):
70
90
odml_documents = [odml_documents ]
@@ -74,7 +94,13 @@ def __init__(self, odml_documents):
74
94
self .graph = Graph ()
75
95
self .graph .bind ("odml" , ODML_NS )
76
96
97
+ self .rdf_subclassing = rdf_subclassing
98
+
77
99
self .section_subclasses = load_rdf_subclasses ()
100
+ # If a custom Section type to RDF Subclass dict has been provided,
101
+ # parse it and update the default section_subclasses dict with the content.
102
+ if custom_subclasses and isinstance (custom_subclasses , dict ):
103
+ self ._parse_custom_subclasses (custom_subclasses )
78
104
79
105
def convert_to_rdf (self ):
80
106
"""
@@ -221,10 +247,16 @@ def save_section(self, sec, curr_node):
221
247
222
248
# Add type of current node to the RDF graph
223
249
curr_type = fmt .rdf_type
250
+
224
251
# Handle section subclass types
225
- sub_sec = self ._get_section_subclass (sec )
226
- if sub_sec :
227
- curr_type = sub_sec
252
+ if self .rdf_subclassing :
253
+ sub_sec = self ._get_section_subclass (sec )
254
+ if sub_sec :
255
+ curr_type = sub_sec
256
+ self .graph .add ((URIRef (fmt .rdf_type ), RDF .type , RDFS .Class ))
257
+ self .graph .add ((URIRef (curr_type ), RDF .type , RDFS .Class ))
258
+ self .graph .add ((URIRef (curr_type ), RDFS .subClassOf , URIRef (fmt .rdf_type )))
259
+
228
260
self .graph .add ((curr_node , RDF .type , URIRef (curr_type )))
229
261
230
262
for k in fmt .rdf_map_keys :
@@ -294,6 +326,33 @@ class Section.
294
326
295
327
return None
296
328
329
+ def _parse_custom_subclasses (self , custom_subclasses ):
330
+ """
331
+ Parses a provided dictionary of "Section type": "RDF Subclass name"
332
+ key value pairs and adds the pairs to the parsers' 'section_subclasses'
333
+ default dictionary. Existing key:value pairs will be overwritten
334
+ with provided custom key:value pairs and a Warning will be issued.
335
+ Dictionary values containing whitespaces will raise a ValueError.
336
+
337
+ :param custom_subclasses: dictionary of "Section type": "RDF Subclass name" key value pairs.
338
+ Values must not contain whitespaces, a ValueError will be raised
339
+ otherwise.
340
+ """
341
+
342
+ # Do not allow any whitespace characters in values
343
+ vals = "" .join (custom_subclasses .values ()).encode ()
344
+ if vals != vals .translate (None , string .whitespace .encode ()):
345
+ msg = "Custom RDF Subclass names must not contain any whitespace characters."
346
+ raise ValueError (msg )
347
+
348
+ for k in custom_subclasses :
349
+ val = custom_subclasses [k ]
350
+ if k in self .section_subclasses :
351
+ msg = "RDFWriter custom subclasses: Key '%s' already exists. " % k
352
+ msg += "Value '%s' replaces default value '%s'." % (val , self .section_subclasses [k ])
353
+ warnings .warn (msg , stacklevel = 2 )
354
+ self .section_subclasses [k ] = val
355
+
297
356
def __str__ (self ):
298
357
return self .convert_to_rdf ().serialize (format = 'turtle' ).decode ("utf-8" )
299
358
0 commit comments