Skip to content

Commit

Permalink
Report list of focus nodes for SHACL shapes
Browse files Browse the repository at this point in the history
  • Loading branch information
avillar committed Aug 1, 2023
1 parent 42f8e4f commit 2201845
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 8 deletions.
6 changes: 6 additions & 0 deletions ogc/bblocks/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import jsonschema
from ogc.na.util import load_yaml

d = load_yaml(filename='/home/alx5000/work/Proyectos/ogc/3d-csdm-schema/_sources/csdm/test/example.json')
schema = load_yaml(filename='/home/alx5000/work/Proyectos/ogc/3d-csdm-schema/build-local/annotated/csdm/test/schema.json')
jsonschema.validate(d, schema)
44 changes: 37 additions & 7 deletions ogc/bblocks/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@
import jsonschema
import requests
from jsonschema.validators import validator_for
from ogc.na.annotate_schema import SchemaResolver
from ogc.na.util import validate as shacl_validate, load_yaml, is_url
from ogc.na.util import load_yaml, is_url
from pyparsing import ParseBaseException
from rdflib import Graph
from rdflib.term import Node, URIRef, BNode
from yaml import MarkedYAMLError

from ogc.bblocks.util import BuildingBlock
import traceback
import pyshacl

OUTPUT_SUBDIR = 'output'
FORMAT_ALIASES = {
Expand Down Expand Up @@ -197,11 +198,23 @@ def validate_inner():
'Using SHACL files for validation:\n - ' + '\n - '.join(str(f) for f in shacl_files)
)
try:
shacl_report = shacl_validate(graph, shacl_graph)
if shacl_report.result:
report.add_info('SHACL', shacl_report.text)
else:
report.add_error('SHACL', shacl_report.text)
shacl_conforms, shacl_result, shacl_report, focus_nodes = shacl_validate(graph, shacl_graph)
report_add = report.add_info if shacl_conforms else report.add_error
report_add('SHACL', shacl_report)
if focus_nodes:
focus_nodes_report = ''
for shape, shape_focus_nodes in focus_nodes.items():
focus_nodes_report += f" - Shape {format_node(shape.node)}"
shape_path = shape.path()
if shape_path:
focus_nodes_report += f" (path {shape_path})"
focus_nodes_report += ": "
if not shape_focus_nodes:
focus_nodes_report += '*none*'
else:
focus_nodes_report += ','.join(format_node(x) for x in shape_focus_nodes)
focus_nodes_report += "\n"
report.add_info('SHACL', 'Focus nodes:\n' + focus_nodes_report)
except ParseBaseException as e:
if e.args:
query_lines = e.args[0].splitlines()
Expand Down Expand Up @@ -396,3 +409,20 @@ def validate_json(instance: Any, validator: jsonschema.Validator):
error = jsonschema.exceptions.best_match(validator.iter_errors(instance))
if error is not None:
raise error


def shacl_validate(g: Graph, s: Graph) -> tuple[bool, Graph, str, dict[pyshacl.Shape, Node]]:
validator = pyshacl.Validator(g, shacl_graph=s, options={
'advanced': True
})
focus_nodes: dict[pyshacl.Shape, Node] = {shape: shape.focus_nodes(g) for shape in validator.shacl_graph.shapes}
conforms, shacl_result, shacl_report = validator.run()
return conforms, shacl_result, shacl_report, focus_nodes


def format_node(n: Node):
if isinstance(n, URIRef):
return f"<{n}>"
if isinstance(n, BNode):
return f"_:{n}"
return str(n)
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ jsonschema==4.17.3
ogc-na>=0.3.2
rdflib @ git+https://github.com/avillar/[email protected]#egg=rdflib
requests~=2.31.0

pyparsing~=3.0.9
pyshacl~=0.21.0
rdflib~=6.3.2

0 comments on commit 2201845

Please sign in to comment.