def __init__(self, shape: 'Shape', rule_node: 'rdflib.term.Identifier', **kwargs): """ :param shape: :type shape: Shape :param rule_node: :type rule_node: rdflib.term.Identifier """ super(SPARQLRule, self).__init__(shape, rule_node, **kwargs) construct_nodes = set(self.shape.sg.objects(self.node, SH_construct)) if len(construct_nodes) < 1: raise RuleLoadError("No sh:construct on SPARQLRule", "https://www.w3.org/TR/shacl-af/#SPARQLRule") self._constructs = [] for c in construct_nodes: if not isinstance(c, Literal) or not ( c.datatype == XSD_string or c.language is not None or isinstance(c.value, str) ): raise RuleLoadError( "SPARQLRule sh:construct must be an xsd:string", "https://www.w3.org/TR/shacl-af/#SPARQLRule" ) self._constructs.append(str(c.value)) SPARQLQueryHelper = get_query_helper_cls() query_helper = SPARQLQueryHelper(self.shape, self.node, None, deactivated=self._deactivated) query_helper.collect_prefixes() self._qh = query_helper
def __init__(self, shape): super(SPARQLBasedConstraint, self).__init__(shape) sg = self.shape.sg.graph sparql_node_list = set(self.shape.objects(SH_sparql)) if len(sparql_node_list) < 1: raise ConstraintLoadError( "SPARQLConstraintComponent must have at least one sh:sparql predicate.", "https://www.w3.org/TR/shacl/#SPARQLConstraintComponent", ) sparql_constraints = set() for s in iter(sparql_node_list): select_node_list = set(sg.objects(s, SH_select)) if len(select_node_list) < 1: raise ConstraintLoadError( "SPARQLConstraintComponent value for sh:select must have at least one sh:select predicate.", "https://www.w3.org/TR/shacl/#SPARQLConstraintComponent", ) elif len(select_node_list) > 1: raise ConstraintLoadError( "SPARQLConstraintComponent value for sh:select must have at most one sh:select predicate.", "https://www.w3.org/TR/shacl/#SPARQLConstraintComponent", ) select_node = next(iter(select_node_list)) if not (isinstance(select_node, rdflib.Literal) and isinstance(select_node.value, str)): raise ConstraintLoadError( "SPARQLConstraintComponent value for sh:select must be a Literal with type xsd:string.", "https://www.w3.org/TR/shacl/#SPARQLConstraintComponent", ) message_node_list = set(sg.objects(s, SH_message)) msgs = None if len(message_node_list) > 0: message = next(iter(message_node_list)) if not (isinstance(message, rdflib.Literal) and isinstance(message.value, str)): raise ConstraintLoadError( "SPARQLConstraintComponent value for sh:message must be a Literal with type xsd:string.", "https://www.w3.org/TR/shacl/#SPARQLConstraintComponent", ) msgs = message_node_list deactivated_node_list = set(sg.objects(s, SH_deactivated)) deact = False if len(deactivated_node_list) > 0: deactivated = next(iter(deactivated_node_list)) if not (isinstance(deactivated, rdflib.Literal) and isinstance(deactivated.value, bool)): raise ConstraintLoadError( "SPARQLConstraintComponent value for sh:deactivated must be " "a Literal with type xsd:boolean.", "https://www.w3.org/TR/shacl/#SPARQLConstraintComponent", ) deact = bool(deactivated.value) SPARQLQueryHelper = get_query_helper_cls() query_helper = SPARQLQueryHelper(self.shape, s, select_node.value, messages=msgs, deactivated=deact) query_helper.collect_prefixes() sparql_constraints.add(query_helper) self.sparql_constraints = sparql_constraints
def apply(self, data_graph: 'GraphLike') -> int: focus_nodes = self.shape.focus_nodes(data_graph) # uses target nodes to find focus nodes all_added = 0 SPARQLQueryHelper = get_query_helper_cls() iterate_limit = 100 while True: if iterate_limit < 1: raise ReportableRuntimeError("Local rule iteration exceeded iteration limit of 100.") iterate_limit -= 1 added = 0 applicable_nodes = self.filter_conditions(focus_nodes, data_graph) construct_graphs = set() for a in applicable_nodes: for c in self._constructs: init_bindings = {} found_this = SPARQLQueryHelper.bind_this_regex.search(c) if found_this: init_bindings['this'] = a c = self._qh.apply_prefixes(c) results = data_graph.query(c, initBindings=init_bindings) if results.type != "CONSTRUCT": raise ReportableRuntimeError("Query executed by a SHACL SPARQLRule must be CONSTRUCT query.") this_added = False for i in results.graph: if not this_added and i not in data_graph: this_added = True # We only need to know at least one triple was added, then break! break if this_added: added += 1 construct_graphs.add(results.graph) if added > 0: for g in construct_graphs: data_graph = clone_graph(g, target_graph=data_graph) all_added += added if self.iterate: continue # Jump up to iterate else: break # Don't iterate break # We've reached a local steady state return all_added
def apply(self, data_graph): focus_nodes = self.shape.focus_nodes( data_graph) # uses target nodes to find focus nodes applicable_nodes = self.filter_conditions(focus_nodes, data_graph) construct_graphs = set() SPARQLQueryHelper = get_query_helper_cls() for a in applicable_nodes: for c in self._constructs: init_bindings = {} found_this = SPARQLQueryHelper.bind_this_regex.search(c) if found_this: init_bindings['this'] = a c = self._qh.apply_prefixes(c) results = data_graph.query(c, initBindings=init_bindings) if results.type != "CONSTRUCT": raise ReportableRuntimeError( "Query executed by a SHACL SPARQLRule must be CONSTRUCT query." ) construct_graphs.add(results.graph) for g in construct_graphs: data_graph = clone_graph(g, target_graph=data_graph)
def __init__(self, constraint, shape: 'Shape', validator): """ Create a new custom constraint, by applying a ConstraintComponent and a Validator to a Shape :param constraint: The source ConstraintComponent, this is needed to bind the parameters in the query_helper :type constraint: SPARQLConstraintComponent :param shape: :type shape: Shape :param validator: :type validator: AskConstraintValidator | SelectConstraintValidator """ super(BoundShapeValidatorComponent, self).__init__(shape) self.constraint = constraint self.validator = validator params = constraint.parameters SPARQLQueryHelper = get_query_helper_cls() self.query_helper = SPARQLQueryHelper(self.shape, validator.node, validator.query_text, params, messages=validator.messages) # Setting self.shape into QueryHelper automatically applies query_helper.bind_params and bind_messages self.query_helper.collect_prefixes()