-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' of https://github.com/kg-construct/rml-cc
- Loading branch information
Showing
18 changed files
with
2,553 additions
and
290 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# This workflow will install Python dependencies, run tests and lint with a single version of Python | ||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions | ||
|
||
name: Test case validation with SHACL shapes | ||
|
||
on: | ||
push: | ||
branches: [ main ] | ||
pull_request: | ||
branches: [ main ] | ||
|
||
jobs: | ||
build: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- name: Set up Python 3.9 | ||
uses: actions/setup-python@v2 | ||
with: | ||
python-version: 3.9 | ||
- name: Install dependencies | ||
run: | | ||
python -m pip install --upgrade pip | ||
cd shapes | ||
pip install -r requirements.txt | ||
- name: Run tests | ||
run: | | ||
cd shapes | ||
python3 tests.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
.DS_Store | ||
.DS_Store | ||
__pycache__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
@prefix brick: <https://brickschema.org/schema/Brick#> . | ||
@prefix csvw: <http://www.w3.org/ns/csvw#> . | ||
@prefix dc: <http://purl.org/dc/elements/1.1/> . | ||
@prefix dcam: <http://purl.org/dc/dcam/> . | ||
@prefix dcat: <http://www.w3.org/ns/dcat#> . | ||
@prefix dcmitype: <http://purl.org/dc/dcmitype/> . | ||
@prefix dcterms: <http://purl.org/dc/terms/> . | ||
@prefix doap: <http://usefulinc.com/ns/doap#> . | ||
@prefix foaf: <http://xmlns.com/foaf/0.1/> . | ||
@prefix geo: <http://www.opengis.net/ont/geosparql#> . | ||
@prefix odrl: <http://www.w3.org/ns/odrl/2/> . | ||
@prefix org: <http://www.w3.org/ns/org#> . | ||
@prefix owl: <http://www.w3.org/2002/07/owl#> . | ||
@prefix prof: <http://www.w3.org/ns/dx/prof/> . | ||
@prefix prov: <http://www.w3.org/ns/prov#> . | ||
@prefix qb: <http://purl.org/linked-data/cube#> . | ||
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . | ||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . | ||
@prefix rml: <http://w3id.org/rml/> . | ||
@prefix : <http://w3id.org/rml/shapes/> . | ||
@prefix sh: <http://www.w3.org/ns/shacl#> . | ||
@prefix skos: <http://www.w3.org/2004/02/skos/core#> . | ||
@prefix sosa: <http://www.w3.org/ns/sosa/> . | ||
@prefix ssn: <http://www.w3.org/ns/ssn/> . | ||
@prefix time: <http://www.w3.org/2006/time#> . | ||
@prefix vann: <http://purl.org/vocab/vann/> . | ||
@prefix void: <http://rdfs.org/ns/void#> . | ||
@prefix wgs: <https://www.w3.org/2003/01/geo/wgs84_pos#> . | ||
@prefix xml: <http://www.w3.org/XML/1998/namespace> . | ||
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> . | ||
|
||
<http://w3id.org/rml/shapes/RMLGatherMapShape> a sh:NodeShape ; | ||
sh:and ( <http://w3id.org/rml/shapes/RMLTermMapShape> <http://w3id.org/rml/shapes/RMLLogicalTargetPropertiesShape> [ sh:description """ | ||
rml:strategy specifies the collection strategy to use: rml:append | ||
or rml:catessianProduct. rml:append is the default strategy. | ||
""" ; | ||
sh:maxCount 1 ; | ||
sh:message """ | ||
Zero or one rml:strategy is required. | ||
""" ; | ||
sh:minCount 0 ; | ||
sh:name "strategy" ; | ||
sh:node <http://w3id.org/rml/shapes/RMLStrategyAppendShape> ; | ||
sh:nodeKind sh:IRI ; | ||
sh:path rml:strategy ] [ sh:description """ | ||
rml:gatherAs specifies how to gather the collection e.g. a rdf:Alt, | ||
rdf:List, rdf:Bag, or rdf:Seq. | ||
""" ; | ||
sh:in ( rdf:Alt rdf:List rdf:Bag rdf:Seq ) ; | ||
sh:maxCount 1 ; | ||
sh:message """ | ||
One rml:gatherAs is required. | ||
""" ; | ||
sh:minCount 1 ; | ||
sh:name "gatherAs" ; | ||
sh:nodeKind sh:IRI ; | ||
sh:path rml:gatherAs ] [ sh:datatype xsd:boolean ; | ||
sh:description """ | ||
Defines the behavior when the collection is empty. True will generate | ||
a rdf:nil for a RDF collection or a resource with no members for an RDF | ||
container. False will not generate any collection or container. | ||
Default is false. | ||
""" ; | ||
sh:maxCount 1 ; | ||
sh:message """ | ||
Zero or one rml:allowEmptyListAndContainer is required with datatype | ||
xsd:boolean. | ||
""" ; | ||
sh:minCount 0 ; | ||
sh:name "allowEmptyListAndContainer" ; | ||
sh:nodeKind sh:Literal ; | ||
sh:path rml:allowEmptyListAndContainer ] [ sh:description """ | ||
RML Term Maps to gather in the collection or container. | ||
""" ; | ||
sh:message """ | ||
one or more rml:gather properties are needed, each pointing to a | ||
RML Term Map. | ||
""" ; | ||
sh:minCount 1 ; | ||
sh:name "gather" ; | ||
sh:nodeKind sh:BlankNodeOrIRI ; | ||
sh:path rml:gather ] ) ; | ||
sh:description """ | ||
Represents a Gather Map. | ||
""" ; | ||
sh:message """ | ||
Gather Map requires one rml:strategy, one rml:gatherAs, and a list of | ||
Term Map with rml:gather. | ||
""" ; | ||
sh:name "GatherMap" . | ||
|
||
<http://w3id.org/rml/shapes/RMLLogicalTargetPropertiesShape> a sh:NodeShape . | ||
|
||
<http://w3id.org/rml/shapes/RMLStrategyAppendShape> a sh:NodeShape . | ||
|
||
<http://w3id.org/rml/shapes/RMLTermMapShape> a sh:NodeShape . | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
############################################################################### | ||
# RML FNML external resources shapes # | ||
# Copyright Dylan Van Assche, IDLab - UGent - imec (2023) # | ||
############################################################################### | ||
@prefix sh: <http://www.w3.org/ns/shacl#> . | ||
@prefix : <http://w3id.org/rml/shapes/> . | ||
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . | ||
@prefix rml: <http://w3id.org/rml/> . | ||
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> . | ||
|
||
# Shapes are defined in the corresponding repositories of each specification | ||
# Provide their IRI as stub to allow validation in CC. | ||
|
||
:RMLTermMapShape | ||
a sh:NodeShape; | ||
. | ||
|
||
:RMLLogicalTargetPropertiesShape | ||
a sh:NodeShape; | ||
. | ||
|
||
:RMLStrategyAppendShape | ||
a sh:NodeShape; | ||
. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
############################################################################### | ||
# RMLCC Gather Map shape # | ||
# Copyright Dylan Van Assche, IDLab - UGent - imec (2023) # | ||
############################################################################### | ||
@prefix sh: <http://www.w3.org/ns/shacl#> . | ||
@prefix : <http://w3id.org/rml/shapes/> . | ||
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . | ||
@prefix rml: <http://w3id.org/rml/> . | ||
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> . | ||
|
||
:RMLGatherMapShape | ||
a sh:NodeShape ; | ||
sh:name "GatherMap" ; | ||
sh:description """ | ||
Represents a Gather Map. | ||
""" ; | ||
sh:message """ | ||
Gather Map requires one rml:strategy, one rml:gatherAs, and a list of | ||
Term Map with rml:gather. | ||
""" ; | ||
|
||
sh:and ( | ||
# Inherited shapes | ||
:RMLTermMapShape | ||
:RMLLogicalTargetPropertiesShape | ||
# Gather Map specific shapes | ||
[ | ||
sh:path rml:strategy ; | ||
sh:name "strategy" ; | ||
sh:description """ | ||
rml:strategy specifies the collection strategy to use: rml:append | ||
or rml:catessianProduct. rml:append is the default strategy. | ||
""" ; | ||
sh:message """ | ||
Zero or one rml:strategy is required. | ||
""" ; | ||
sh:node :RMLStrategyAppendShape ; | ||
sh:nodeKind sh:IRI ; | ||
sh:minCount 0 ; | ||
sh:maxCount 1 ; | ||
] | ||
[ | ||
sh:path rml:gatherAs ; | ||
sh:name "gatherAs" ; | ||
sh:description """ | ||
rml:gatherAs specifies how to gather the collection e.g. a rdf:Alt, | ||
rdf:List, rdf:Bag, or rdf:Seq. | ||
""" ; | ||
sh:message """ | ||
One rml:gatherAs is required. | ||
""" ; | ||
sh:in (rdf:Alt rdf:List rdf:Bag rdf:Seq) ; | ||
sh:nodeKind sh:IRI; | ||
sh:minCount 1 ; | ||
sh:maxCount 1 ; | ||
] | ||
[ | ||
sh:path rml:allowEmptyListAndContainer ; | ||
sh:name "allowEmptyListAndContainer" ; | ||
sh:description """ | ||
Defines the behavior when the collection is empty. True will generate | ||
a rdf:nil for a RDF collection or a resource with no members for an RDF | ||
container. False will not generate any collection or container. | ||
Default is false. | ||
""" ; | ||
sh:message """ | ||
Zero or one rml:allowEmptyListAndContainer is required with datatype | ||
xsd:boolean. | ||
""" ; | ||
sh:minCount 0 ; | ||
sh:maxCount 1 ; | ||
sh:nodeKind sh:Literal ; | ||
sh:datatype xsd:boolean ; | ||
] | ||
[ | ||
sh:path rml:gather ; | ||
sh:name "gather" ; | ||
sh:description """ | ||
RML Term Maps to gather in the collection or container. | ||
""" ; | ||
sh:message """ | ||
one or more rml:gather properties are needed, each pointing to a | ||
RML Term Map. | ||
""" ; | ||
sh:nodeKind sh:BlankNodeOrIRI ; | ||
sh:minCount 1 ; | ||
] | ||
) ; | ||
. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
#!/usr/bin/env python | ||
|
||
import argparse | ||
import sys | ||
from os import path | ||
from glob import glob | ||
from rdflib import Graph, Namespace | ||
from rdflib.plugins.serializers.turtle import TurtleSerializer | ||
from rdflib import plugin | ||
|
||
SUBSHAPE_FORMAT = 'turtle' | ||
SUBSHAPE_GLOB_PATTERN = '*.ttl' | ||
SHACL = Namespace('http://www.w3.org/ns/shacl#') | ||
RML = Namespace('http://w3id.org/rml/') | ||
|
||
|
||
class TurtleWithPrefixes(TurtleSerializer): | ||
""" | ||
A turtle serializer that always emits prefixes | ||
Workaround for https://github.com/RDFLib/rdflib/issues/1048 | ||
""" | ||
roundtrip_prefixes = True # Undocumented, forces to write all prefixes | ||
|
||
|
||
class ShapeGenerator: | ||
""" | ||
Generates a complete SHACL shape of all the SHACL subshapes. | ||
Useful for using the shapes in the shacl.org Playground. | ||
""" | ||
def __init__(self, glob_pattern: str, rdf_format: str, | ||
destination: str) -> None: | ||
self._glob_pattern: str = glob_pattern | ||
self._rdf_format: str = rdf_format | ||
self._destination: str = destination | ||
self._shape = Graph() | ||
self._shape.bind('sh', SHACL) | ||
self._shape.bind('rml', RML) | ||
# Register TurtleWithPrefixes serializer as 'tortoise' format | ||
plugin.register('tortoise', | ||
plugin.Serializer, | ||
'generate_shape', | ||
'TurtleWithPrefixes') | ||
|
||
def generate(self) -> None: | ||
""" | ||
Generate a full shape from the sub shapes. | ||
""" | ||
print('Reading subshapes:') | ||
for sub_shape in glob(self._glob_pattern): | ||
if 'cc' in sub_shape or 'shacl' in sub_shape: | ||
continue | ||
print(f"\t{sub_shape}") | ||
g = Graph() | ||
g.bind('sh', SHACL) | ||
g.bind('rml', RML) | ||
self._shape += g.parse(sub_shape, format=self._rdf_format) | ||
|
||
print(f'Writing shape to {self._destination}') | ||
self._shape.serialize(destination=self._destination, format='tortoise') | ||
|
||
|
||
if __name__ == '__main__': | ||
p = argparse.ArgumentParser(description='Generate a complete SHACL shape ' | ||
'from the subshapes.') | ||
p.add_argument('destination', type=str, help='Location to write the ' | ||
'complete SHACL shape as Turtle.') | ||
# Arguments parsing | ||
args = p.parse_args() | ||
if not path.exists(args.destination): | ||
print(f'{args.destination} file path does not exist!') | ||
sys.exit(1) | ||
|
||
if path.isdir(args.destination): | ||
args.destination = path.join(args.destination, 'cc.ttl') | ||
|
||
# Generate shape | ||
generator = ShapeGenerator(SUBSHAPE_GLOB_PATTERN, SUBSHAPE_FORMAT, | ||
args.destination) | ||
generator.generate() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
from logging import debug, info, critical | ||
from pyshacl import validate | ||
from rdflib import Graph | ||
from shutil import get_terminal_size | ||
|
||
|
||
class MappingValidator: | ||
def __init__(self, shape: str) -> None: | ||
g = Graph() | ||
g.parse(shape, format='turtle') | ||
self._shape = g | ||
|
||
def validate(self, rules: Graph, print_report: bool = True) -> None: | ||
valid: bool | ||
report_graph: Graph | ||
report_text: str | ||
valid, report_graph, report_text = validate(rules, | ||
shacl_graph=self._shape) | ||
debug(f'RML rules valid: {valid}') | ||
debug(f'SHACL validation report: {report_text}') | ||
|
||
# If mapping rules are invalid, print SHACL report and raise exception | ||
if not valid and print_report: | ||
self._print_report(report_text) | ||
msg = 'RML mapping rules are invalid, a detailed explanation' \ | ||
' is available in the report' | ||
critical(msg) | ||
raise ValueError(msg) | ||
|
||
def _print_report(self, report_text: str) -> None: | ||
tty_columns: int | ||
tty_columns, _ = get_terminal_size() | ||
info('-' * tty_columns) | ||
title: str = 'RML rules validation report' | ||
white_space: int = int((tty_columns - len(title)) / 2) | ||
title = ' ' * white_space + title + ' ' * white_space | ||
info(title) | ||
info('-' * tty_columns) | ||
info(report_text) | ||
info('-' * tty_columns) |
Oops, something went wrong.