Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Defining the SHACL shapes for code lists. #489

Merged
merged 2 commits into from
May 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions csvcubed/shacl/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# SHACL Shapes

**N.B. SHACL Shapes are a work-in-progress.**

This directory contains a versioned history of the RDF shape of csvcubed's outputs.

The purpose of these SHACL shape definitions is to provide a well-defined and versioned description of the RDF which is
produced by csvcubed. These definition specify the interface (or API) between csvcubed and applications which may wish to
process, consume or display the contents of CSV-Ws generated by the application.

The SHACL shapes defined herein can also be used to check/validate whether a given CSV-W matches the csvcubed
specifications. It is also possible to use [visualise]() these SHACL constraints to get an overview of the key
RDF relationships.

## Versioning

Versioning of csvcubed's outputs uses a `MAJOR.minor` approach to versioning.

**Any changes to the RDF structure of said output files must be accompanied by a new version in this directory.**

### Major Versions

Major versions are for any and all breaking changes, including defining new triples which fundamentally change
how existing triples should be interpreted.

### Minor Versions

Minor versions may only **add** triple definitions which are **optional** and can safely be ignored by applications
reading and interpreting CSV-Ws.

## Folder Structure

Each major version should have a `v*` folder with folders for each minor version inside, e.g. for version `1.0`,
the following folder structure exists:

```
v1 - MAJOR version
└── 0 - Minor version
└── ...SHACL shape files...
```

### Files

Each version folder should:

* Contain one JSON-LD file (at the top level) for each type of CSV-W output that csvcubed is capable of generating.
* For example a user should be able to locate the file defining a code-list's shape and validate their RDF directly against that.
* Any shared or extraneous files should be defined in a sub-directory so as not to clutter up the definitions.

## Visualisation

The SHACL shapes defined in turtle format can be visualised using the [SHACL Play!](https://shacl-play.sparna.fr/play/draw) tool.

## Validation

### Apache Jena

You can use the [Apache Jena shacl command](https://jena.apache.org/documentation/tools/#other-handy-command-line-tools)
to validate SHACL shapes in turtle format against RDF data.


```bash
shacl validate --shapes shapes.ttl --data subsector.ttl
```

### pySHACL

[pySHACL](https://github.com/RDFLib/pySHACL) supports directly validating RDF against SON-LD definitions:

```bash
pyshacl -s code-list.json subsector.ttl --imports --metashacl
```

N.B. The `imports` flag above is important when using the JSON-LD definitions since they are spread across multiple files.
100 changes: 100 additions & 0 deletions csvcubed/shacl/v1/0/code-list.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
{
"@context": [
"deps/shacl-context.json",
{
"this": {
"@id": "https://purl.org/csv-cubed/shacl/v1.0/code-list.json#",
"prefix": true
},
"ui": "http://www.w3.org/ns/ui#",
"xkos": "http://rdf-vocabulary.ddialliance.org/xkos#",
"dcatshapes": {
"@id": "https://purl.org/csv-cubed/shacl/v1.0/deps/dcat.json#",
"prefix": true
}
}
],
"@id": "https://purl.org/csv-cubed/shacl/v1.0/code-list.json#ontology",
"@type": "owl:Ontology",
"owl:imports": "deps/dcat.json",
"shape": [
{
"@id": "this:ConceptSchemeShape",
"@type": "sh:NodeShape",
"targetClass": "skos:ConceptScheme",
"property": [
{
"path": "rdfs:label",
"name": "Code list's label",
"description": "The code list's label must be a non-zero length `rdf:languageString` with no duplicate languages present. Each label has a matching `dct:title` triple.",
"datatype": "rdf:langString",
"uniqueLang": true,
"minCount": 1,
"minLength": 1
},
{
"path": "xkos:variant",
"name": "Variant of",
"description": "The concept scheme is a variant of another existing code list",
"minCount": 0,
"nodeKind": "sh:IRI",
"class": "skos:ConceptScheme",
"node": "this:ConceptSchemeShape"
}
],
"and": [
"dcatshapes:CatalogDatasetShape"
]
},
{
"@id": "this:ConceptShape",
"@type": "sh:NodeShape",
"targetClass": "skos:Concept",
"property": [
{
"path": "rdfs:label",
"name": "Label",
"description": "A concept's label must be a non-zero length `xsd:string`.",
"minCount": 1,
"datatype": "xsd:string",
"minLength": 1
},
{
"path": "skos:notation",
"name": "Notation",
"description": "A concept must have a single notation. The notation must be a non-zero length `xsd:string`.",
"minCount": 1,
"maxCount": 1,
"datatype": "xsd:string",
"minLength": 1
},
{
"path": "skos:inScheme",
"name": "In Scheme",
"description": "A concept must be part of at least one concept scheme (code list). The concept scheme must be referred to by IRI.",
"minCount": 1,
"nodeKind": "sh:IRI",
"class": "skos:ConceptScheme",
"node": "this:ConceptSchemeShape"
},
{
"path": "ui:sortPriority",
"name": "Sort Priority",
"description": "A concept must have a single integer sort priority to guide user interfaces on the order in which to sort concepts for display.",
"minCount": 1,
"maxCount": 1,
"datatype": "xsd:integer"
},
{
"path": "skos:broader",
"name": "Broader Concept",
"description": "A concept may have one or more concepts which are broader than itself. These concepts must be referred to by IRI.",
"minCount": 0,
"nodeKind": "sh:IRI",
"class": "skos:Concept",
"node": "this:ConceptShape"
}
]
}
]
}
133 changes: 133 additions & 0 deletions csvcubed/shacl/v1/0/deps/dcat.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
{
"@context": [
"shacl-context.json",
{
"this": {
"@id": "https://purl.org/csv-cubed/shacl/v1.0/deps/dcat.json#",
"prefix": true
}
}
],
"@id": "https://purl.org/csv-cubed/shacl/v1.0/deps/dcat.json#ontology",
"@type": "owl:Ontology",
"shape": [
{
"@id": "this:CatalogDatasetShape",
"@type": "sh:NodeShape",
"targetClass": "dcat:Dataset",
"property": [
{
"path": "dct:title",
"name": "Title",
"equals": "rdfs:label",
"description": "A short title describing the catalogued item.",
"datatype": "rdf:langString",
"uniqueLang": true,
"minCount": 1,
"maxCount": 1,
"minLength": 1
},
{
"path": "dct:identifier",
"name": "Identifier",
"description": "An identifier for this catalogued item.",
"datatype": "xsd:string",
"minCount": 1,
"maxCount": 1,
"minLength": 1
},
{
"path": "dct:issued",
"name": "Date-time Issued",
"description": "The point in time when the catalogued item was first issued.",
"datatype": "xsd:dateTime",
"minCount": 1,
"maxCount": 1
},
{
"path": "dct:modified",
"name": "Date-time Modified",
"description": "The point in time when the catalogued item was last updated.",
"datatype": "xsd:dateTime",
"minCount": 1,
"maxCount": 1
},
{
"path": "rdfs:comment",
"name": "Comment",
"description": "Short sentence describing the item being catalogued.",
"datatype": "rdf:langString",
"uniqueLang": true,
"minCount": 0,
"maxCount": 1,
"minLength": 1
},
{
"path": "dct:description",
"name": "Description",
"description": "Longer multi-paragraph description of what this catalogued item represents.",
canwaf marked this conversation as resolved.
Show resolved Hide resolved
"datatype": "rdf:langString",
"uniqueLang": true,
"minCount": 0,
"maxCount": 1,
"minLength": 1
},
{
"path": "dct:license",
"name": "License",
"description": "The license under which the catalogue item is available.",
"nodeKind": "sh:IRI",
"minCount": 0,
"maxCount": 1
},
{
"path": "dct:creator",
"name": "Creator",
"description": "The original creator of the catalogued item.",
"nodeKind": "sh:IRI",
"minCount": 0,
"maxCount": 1
},
{
"path": "dct:publisher",
"name": "Publisher",
"description": "The publisher of the catalogued item.",
"nodeKind": "sh:IRI",
"minCount": 0,
"maxCount": 1
},
{
"path": "dcat:landingPage",
"name": "Landing Page",
"description": "Landing page where a copy of the catalogued item's data can be found for download.",
"nodeKind": "sh:IRI",
"minCount": 0
},
{
"path": "dcat:theme",
"name": "Theme",
"description": "Pre-defined theme URIs which the catalogued item sits within.",
"nodeKind": "sh:IRI",
"minCount": 0
},
{
"path": "dcat:keyword",
"name": "Keyword",
"description": "Free-text key words which catagorise the catalogued item.",
"datatype": "rdf:langString",
"uniqueLang": true,
"minCount": 0,
"minLength": 1
},
{
"path": "dcat:contactPoint",
"name": "Contact Point",
"description": "A URI which can be used to get in contact to discuss the catalogued item.",
"nodeKind": "sh:IRI",
"minCount": 0,
"maxCount": 1
}
]
}
]
}
44 changes: 44 additions & 0 deletions csvcubed/shacl/v1/0/deps/shacl-context.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"@context": [
"https://raw.githubusercontent.com/w3c/shacl/master/shacl-jsonld-context/shacl.context.ld.json",
{
"uniqueLang": {
"@id": "sh:uniqueLang",
"@type": "xsd:boolean"
},
"description": {
"@id": "sh:description",
"@type": "xsd:string"
},
"name": {
"@id": "sh:name",
"@type": "xsd:string"
},

"skos": "http://www.w3.org/2004/02/skos/core#",
"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
"rdfs": "http://www.w3.org/2000/01/rdf-schema#",
"xsd": "http://www.w3.org/2001/XMLSchema#",
"dct": "http://purl.org/dc/terms/",
"owl": "http://www.w3.org/2002/07/owl#",
"dcat": "http://www.w3.org/ns/dcat#",
robons marked this conversation as resolved.
Show resolved Hide resolved

"@base": "https://purl.org/csv-cubed/shacl/v1.0/deps/shacl-context.json",
"@vocab": "https://purl.org/csv-cubed/shacl/v1.0/deps/shacl-context.json#",
"shape": {
"@type": "@id"
},
"owl:imports": {
"@type": "@id"
}
}
],
"@id": "https://purl.org/csv-cubed/shacl/v1.0/deps/shacl-context.json",
"rdfs:seeAlso": [
{
"@id": "#shape",
"@type": "rdf:Property",
"rdfs:label": "Defines a SHACL shape."
}
]
}
Loading