22from shexer .model .shape import STARTING_CHAR_FOR_SHAPE_NAME
33from rdflib import Graph , Namespace , URIRef , RDF , BNode , XSD , Literal
44from shexer .model .statement import POSITIVE_CLOSURE , KLEENE_CLOSURE , OPT_CARDINALITY
5- from shexer .utils .literal import XSD_NAMESPACE , LANG_STRING_TYPE
5+ from shexer .utils .literal import XSD_NAMESPACE , LANG_STRING_TYPE , decide_literal_type
66from shexer .model .const_elem_types import IRI_ELEM_TYPE , LITERAL_ELEM_TYPE , DOT_ELEM_TYPE , BNODE_ELEM_TYPE
77from shexer .io .wikidata import wikidata_annotation
88from wlighter import TURTLE_FORMAT
9- from shexer .model .node_selector import NodeSelectorSparql , NodeSelectorNoSparql
9+ from shexer .model .node_selector import NodeSelectorSparql
1010from shexer .utils .log import log_msg
11+ from shexer .consts import RATIO_INSTANCES , EXAMPLE_CONFORMANCE_PROP , ABSOLUTE_COUNT_PROP , EXTRA_INFO_PROP , FREQ_PROP , \
12+ SHAPE_EXAMPLES , ALL_EXAMPLES , MIXED_INSTANCES , ABSOLUTE_INSTANCES , CONSTRAINT_EXAMPLES
1113
1214_EXPECTED_SHAPE_BEGINING = STARTING_CHAR_FOR_SHAPE_NAME + "<"
1315_EXPECTED_SHAPE_ENDING = ">"
@@ -56,7 +58,11 @@ class ShaclSerializer(object):
5658
5759 def __init__ (self , target_file , shapes_list , namespaces_dict = None , string_return = False ,
5860 instantiation_property_str = RDF_TYPE_STR , wikidata_annotation = False ,
59- detect_minimal_iri = False , shape_example_features = None , shape_map = None , verbose = False ):
61+ detect_minimal_iri = False , shape_example_features = None , shape_map = None , verbose = False ,
62+ instances_report_mode = RATIO_INSTANCES , examples_mode = None , inverse_paths = False ,
63+ example_constraint_prop = EXAMPLE_CONFORMANCE_PROP , comments_to_annotations = False ,
64+ absolute_counts_prop = ABSOLUTE_COUNT_PROP , extra_info_prop = EXTRA_INFO_PROP ,
65+ frequency_prop = FREQ_PROP ):
6066 self ._target_file = target_file
6167 self ._namespaces_dict = namespaces_dict if namespaces_dict is not None else {}
6268 self ._shapes_list = shapes_list
@@ -68,6 +74,15 @@ def __init__(self, target_file, shapes_list, namespaces_dict=None, string_return
6874 self ._shape_map = shape_map
6975 self ._verbose = verbose
7076
77+ self ._instances_report_mode = instances_report_mode
78+ self ._examples_mode = examples_mode
79+ self ._inverse_paths = inverse_paths
80+ self ._example_constraint_prop = URIRef (example_constraint_prop )
81+ self ._generate_annotations = comments_to_annotations
82+ self ._absolute_counts_prop = URIRef (absolute_counts_prop )
83+ self ._extra_info_prop = URIRef (extra_info_prop )
84+ self ._frequency_prop = URIRef (frequency_prop )
85+
7186 self ._g_shapes = Graph ()
7287
7388 # self._uri_dict = {}
@@ -128,8 +143,26 @@ def _add_shape(self, shape):
128143 shape = shape )
129144 self ._add_shape_constraints (shape = shape ,
130145 r_shape_uri = r_shape_uri )
146+ self ._add_shape_annotations (shape = shape ,
147+ r_shape_uri = r_shape_uri )
131148
132-
149+ def _add_shape_annotations (self , shape , r_shape_uri ):
150+ if not self ._generate_annotations :
151+ return
152+ if self ._examples_mode in [SHAPE_EXAMPLES , ALL_EXAMPLES ]:
153+ self ._add_shape_example (shape , r_shape_uri )
154+ if self ._instances_report_mode in [MIXED_INSTANCES , ABSOLUTE_INSTANCES ]:
155+ self ._add_shape_counts (shape , r_shape_uri )
156+
157+ def _add_shape_example (self , shape , r_shape_uri ):
158+ self ._add_triple (r_shape_uri ,
159+ self ._example_constraint_prop ,
160+ URIRef (self ._shape_example_features .shape_example (shape_id = shape .class_uri )))
161+
162+ def _add_shape_counts (self , shape , r_shape_uri ):
163+ self ._add_triple (r_shape_uri ,
164+ self ._absolute_counts_prop ,
165+ Literal (shape .n_instances ))
133166 def _add_target_class (self , shape , r_shape_uri ):
134167 if self ._shape_map is None :
135168 if shape .class_uri is not None :
@@ -152,18 +185,58 @@ def _literal_iri_pattern(self, shape):
152185 def _add_shape_constraints (self , shape , r_shape_uri ):
153186 for a_statement in shape .yield_statements ():
154187 self ._add_constraint (statement = a_statement ,
155- r_shape_uri = r_shape_uri )
188+ r_shape_uri = r_shape_uri ,
189+ shape = shape )
156190
157191 def _is_instantiation_property (self , str_property ):
158192 return str_property == self ._instantiation_property_str
159193
160- def _add_constraint (self , statement , r_shape_uri ):
194+ def _add_constraint (self , statement , r_shape_uri , shape ):
195+ r_constraint_node = self ._generate_bnode ()
161196 if self ._is_instantiation_property (statement .st_property ):
162197 self ._add_instantiation_constraint (statement = statement ,
163- r_shape_uri = r_shape_uri )
198+ r_shape_uri = r_shape_uri ,
199+ r_constraint_node = r_constraint_node )
164200 else :
165201 self ._add_regular_constraint (statement = statement ,
166- r_shape_uri = r_shape_uri )
202+ r_shape_uri = r_shape_uri ,
203+ r_constraint_node = r_constraint_node ,
204+ shape = shape )
205+
206+ if not self ._generate_annotations :
207+ return
208+ if self ._instances_report_mode in [MIXED_INSTANCES , RATIO_INSTANCES ]:
209+ self ._add_constraint_ratio_annotation (statement , r_constraint_node )
210+ if self ._instances_report_mode in [MIXED_INSTANCES , ABSOLUTE_INSTANCES ]:
211+ self ._add_constraint_absolutes_annotation (statement , r_constraint_node )
212+
213+ def _add_constraint_ratio_annotation (self , statement , r_constraint_node ):
214+ self ._add_triple (r_constraint_node ,
215+ self ._frequency_prop ,
216+ Literal (statement .probability , datatype = XSD .decimal ))
217+
218+ def _add_constraint_absolutes_annotation (self , statement , r_constraint_node ):
219+ self ._add_triple (r_constraint_node ,
220+ self ._absolute_counts_prop ,
221+ Literal (statement .n_occurences ))
222+
223+ def _add_constraint_example (self , statement , r_constraint_node , shape ):
224+ triple_obj = self ._get_example_of_triple_obj (statement , shape )
225+ self ._add_triple (r_constraint_node ,
226+ self ._example_constraint_prop ,
227+ triple_obj )
228+
229+ def _get_example_of_triple_obj (self , statement , shape ):
230+ if self ._inverse_paths :
231+ candidate = self ._shape_example_features .get_constraint_example (shape_id = shape .class_uri ,
232+ prop = statement .st_property ,
233+ inverse = statement .is_inverse )
234+ else :
235+ candidate = self ._shape_example_features .get_constraint_example (shape_id = shape .class_uri ,
236+ prop = statement .st_property )
237+ if candidate .startswith ("http://" ):
238+ return URIRef (candidate )
239+ return Literal (candidate , datatype = decide_literal_type (candidate ))
167240
168241 def _add_exactly_one_cardinality (self , r_constraint_node ):
169242 self ._add_min_occurs (r_constraint_node = r_constraint_node ,
@@ -178,8 +251,7 @@ def _add_in_instance(self, r_constraint_node, statement):
178251 self ._add_triple (list_seed_node , RDF .first , target_node )
179252 self ._add_triple (list_seed_node , RDF .rest , RDF .nil )
180253
181- def _add_instantiation_constraint (self , statement , r_shape_uri ):
182- r_constraint_node = self ._generate_bnode ()
254+ def _add_instantiation_constraint (self , statement , r_shape_uri , r_constraint_node ):
183255 self ._add_bnode_property (r_shape_uri = r_shape_uri ,
184256 r_constraint_node = r_constraint_node )
185257 self ._add_direct_path (statement = statement ,
@@ -188,8 +260,7 @@ def _add_instantiation_constraint(self, statement, r_shape_uri):
188260 self ._add_in_instance (statement = statement ,
189261 r_constraint_node = r_constraint_node )
190262
191- def _add_regular_constraint (self , statement , r_shape_uri ):
192- r_constraint_node = self ._generate_bnode ()
263+ def _add_regular_constraint (self , statement , r_shape_uri , r_constraint_node , shape ):
193264 self ._add_bnode_property (r_shape_uri = r_shape_uri ,
194265 r_constraint_node = r_constraint_node )
195266 self ._add_node_type (statement = statement ,
@@ -198,6 +269,17 @@ def _add_regular_constraint(self, statement, r_shape_uri):
198269 r_constraint_node = r_constraint_node )
199270 self ._add_path (statement = statement ,
200271 r_constraint_node = r_constraint_node )
272+ if self ._generate_annotations :
273+ self ._add_statement_comments (statement = statement ,
274+ r_constraint_node = r_constraint_node )
275+ if self ._examples_mode in [ALL_EXAMPLES , CONSTRAINT_EXAMPLES ]:
276+ self ._add_constraint_example (statement , r_constraint_node , shape )
277+
278+ def _add_statement_comments (self , statement , r_constraint_node ):
279+ for a_comment in statement .comments :
280+ self ._add_triple (r_constraint_node ,
281+ self ._extra_info_prop ,
282+ Literal (a_comment ))
201283
202284 def _add_path (self , statement , r_constraint_node ):
203285 if not statement .is_inverse :
0 commit comments