diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..5d21599 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,26 @@ +name: check rdf syntax + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + name: checkout the repository + - uses: actions/setup-python@v4 + name: install python + with: + python-version: '3.12' + cache: 'pip' + - run: pip install -r requirements.txt + name: install dependencies + shell: sh + - run: python check_rdf.py + name: run the script to check for valid rdf + shell: sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b694934 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.venv \ No newline at end of file diff --git a/README.md b/README.md index a212688..f96c445 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,34 @@ -# uitwisselingsplatform-codelijsten -OSLO codelijsten voor data uitwisseling op uitwisselingsplatform +# codelijsten Uitwisselingsplatform + +IN PROGRESS: de codelijsten zijn nog niet in gebruik. + +Deze repository omvat een aantal codelijsten voor het uitwisselingsplatform https://data.uitwisselingsplatform.be. + +Per codelijst wordt een RDF file in de turtle syntax volgens het [SKOS vocabularium](https://www.w3.org/TR/skos-primer/) toegevoegd aan de directory 'conceptschemes'. + +# vormafspraken over de codelijsten + + +- eigenschappen te gebruiken met conceptschemes: + +|eigenschap | nota | +|-----------|------| +skos:prefLabel | een unieke naam voor het conceptscheme +skos:definition | een definitie van de codelijst + +- eigenschappen te gebruiken met concepts: + +|eigenschap | nota | +|-----------|------| +skos:prefLabel | een unieke naam voor het concept +skos:definition | een definitie van het concept +skos:inscheme | het conceptscheme waartoe dit concept behoort +skos:topConceptOf | het conceptscheme waarvan dit concept een top concept is +skos:broader | de concepten die boven dit concept hangen in de conceptscheme boom. +owl:sameAs | verwijzing naar een identiek concept. + +- gebruik language-tagged strings i.p.v. plain literals +Dus `"mijn label@nl` i.p.v. `"mijn label"` + +- We verwachten een hierarchie in 1 richting. Het is voldoende om de hierarchie in 1 richting uit te drukken. Het is aan de afnemers om indien voor hen nodig ook de omgekeerde relatie te berekenen. Dat maakt de data ook overzichtelijk. + diff --git a/check_rdf.py b/check_rdf.py new file mode 100644 index 0000000..b3a2358 --- /dev/null +++ b/check_rdf.py @@ -0,0 +1,27 @@ +from rdflib import Graph +from pyshacl import validate +import os + +schemes_dir = './conceptschemes' + +def validate_shape(graph): + return validate(data_graph=graph, shacl_graph="shacl_codelists.ttl") + +def validate_turtle(file_path): + try: + g = Graph() + g.parse(file_path, format="turtle") + except Exception as e: + print(f"Error validating {file_path}: {e}") + return False + (is_valid, _, failure_reason)= validate_shape(g) + if is_valid: + return True + else: + print(f"Error validating shape {file_path}: {failure_reason}") + return False + +for filename_codelist in os.listdir(schemes_dir): + print("### Parsing file ", filename_codelist, "###") + file = os.path.join(schemes_dir, filename_codelist) + validate_turtle(file) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..9040fdb --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +rdflib==7.0.0 +pyshacl==0.25.0 \ No newline at end of file diff --git a/shacl_codelists.ttl b/shacl_codelists.ttl new file mode 100644 index 0000000..344f56f --- /dev/null +++ b/shacl_codelists.ttl @@ -0,0 +1,207 @@ +@prefix : . +@prefix sh: . +@prefix owl: . +@prefix rdf: . +@prefix xml: . +@prefix xsd: . +@prefix rdfs: . +@prefix skos: . +@prefix dct: . +@prefix sdo: . +@prefix vann: . + +# ConceptScheme +:ConceptSchemeShape + a sh:NodeShape ; + sh:targetClass skos:ConceptScheme ; + sh:property [ + sh:path skos:prefLabel ; + sh:minCount 1 ; + sh:datatype xsd:string ; + sh:message "Each skos:ConceptScheme has to provide a skos:prefLabel" ; + sh:severity: sh:Violation ; + ] ; + sh:property [ + sh:path skos:definition ; + sh:minCount 1 ; + sh:datatype xsd:string ; + sh:message "Each skos:ConceptScheme has to provide a skos:definition" ; + sh:severity: sh:Violation ; + ] ; + . + +# Concept +:ConceptShape + a sh:NodeShape ; + sh:targetClass skos:Concept ; + sh:property [ + sh:path skos:prefLabel ; + sh:minCount 1 ; + sh:datatype rdf:langString ; + sh:uniqueLang true ; + sh:message "Each skos:Concept has to provide a skos:prefLabel in a unique language" ; + sh:severity: sh:Violation ; + + ] ; + sh:property [ + sh:path skos:prefLabel ; + sh:qualifiedValueShape [ + sh:datatype rdf:langString ; + sh:languageIn ("nl") ; + ] ; + sh:qualifiedMinCount 1 ; + sh:message "Each skos:Concept has to provide a skos:prefLabel in at least @nl language" ; + ] ; + sh:property [ + sh:path skos:definition ; + sh:qualifiedValueShape [ + sh:datatype rdf:langString ; + sh:languageIn ("nl") ; + ] ; + sh:qualifiedMinCount 1 ; + sh:message "Each skos:Concept has to provide a skos:definition in at least @nl language" ; + ] ; + sh:property [ + sh:path skos:altLabel ; + sh:severity sh:Violation ; + sh:datatype rdf:langString ; + sh:message "Alternative label should be provided with a language tag." ; + + ] ; + sh:property [ + sh:path skos:hiddenLabel ; + sh:severity sh:Violation ; + sh:datatype rdf:langString ; + sh:message "Hidden label should be provided with a language tag." ; + + ] ; + sh:property [ + sh:path skos:definition ; + sh:severity sh:Violation ; + sh:minCount 1 ; + sh:uniqueLang true ; + sh:datatype rdf:langString ; + sh:message "At least one definition should be provided with a unique language tag." ; + + ] ; + sh:property [ + sh:path skos:scopeNote ; + sh:severity sh:Violation ; + sh:datatype rdf:langString ; + sh:message "Scope Note should be provided with a language tag." ; + + ] ; + sh:property [ + sh:path skos:note ; + sh:severity sh:Violation ; + sh:datatype rdf:langString ; + sh:message "Note should be provided with a language tag." ; + + ] ; + sh:property [ + sh:path skos:notation ; + sh:severity sh:Violation ; + sh:datatype xsd:string ; + sh:message "Notation should be provided as a string." ; + + ] ; + sh:property [ + sh:path skos:example ; + sh:severity sh:Violation ; + sh:datatype rdf:langString ; + sh:message "Example should be provided with a language tag." ; + + ] ; + sh:property [ + sh:path skos:narrower ; + sh:class skos:Concept ; + sh:severity sh:Violation ; + sh:message "Target class of skos:narrower should be a skos:Concept" ; + + ] ; + sh:property [ + sh:class skos:Concept ; + sh:path skos:narrowerTransitive ; + sh:severity sh:Violation ; + sh:message "Target class of skos:narrowerTransitive should be a skos:Concept" ; + + ] ; + sh:property [ + sh:path skos:narrowMatch ; + sh:nodeKind sh:IRI ; + sh:severity sh:Violation ; + sh:message "Target class of skos:narrowMatch should be a URI" ; + + ] ; + sh:property [ + sh:path skos:broader ; + sh:class skos:Concept ; + sh:severity sh:Violation ; + sh:message "Target class of skos:broader should be a skos:Concept" ; + + ] ; + sh:property [ + sh:path skos:broaderTransitive ; + sh:class skos:Concept ; + sh:severity sh:Violation ; + sh:message "Target class of skos:broaderTransitive should be a skos:Concept" ; + + ] ; + sh:property [ + sh:path skos:broadMatch ; + sh:nodeKind sh:IRI ; + sh:severity sh:Violation ; + sh:message "Target class of skos:broadMatch should be a URI" ; + + ] ; + sh:property [ + sh:path skos:related ; + sh:class skos:Concept ; + sh:severity sh:Violation ; + sh:message "Target class of skos:related should be a skos:Concept" ; + + ] ; + sh:property [ + sh:path skos:relatedMatch ; + sh:nodeKind sh:IRI ; + sh:severity sh:Violation ; + sh:message "Target class of skos:relatedMatch should be a URI" ; + + ] ; + sh:property [ + sh:path skos:closeMatch ; + sh:nodeKind sh:IRI ; + sh:severity sh:Violation ; + sh:message "Target class of skos:closeMatch should be a URI" ; + + ] ; + sh:property [ + sh:path skos:exactMatch ; + sh:nodeKind sh:IRI ; + sh:severity sh:Violation ; + sh:message "Target class of skos:exactMatch should be a URI" ; + + ] ; + sh:property [ + sh:path skos:inScheme ; + sh:class skos:ConceptScheme ; + sh:minCount 1 ; + sh:severity sh:Violation ; + sh:message "Each skos:inScheme should have at least one skos:inScheme of type skos:ConceptScheme" ; + + ] ; + sh:property [ + sh:path skos:topConceptOf ; + sh:class skos:ConceptScheme ; + sh:severity sh:Violation ; + sh:message "Target class of skos:topConceptOf should be a skos:ConceptScheme" ; + + ] ; + sh:property [ + sh:path owl:sameAs ; + sh:nodeKind sh:IRI ; + sh:severity sh:Violation ; + sh:message "Target class of owl:sameAs should be a URI" ; + + ] ; + . \ No newline at end of file