Skip to content

Commit b1e35f5

Browse files
authored
Merge pull request #107 from eScienceLab/106-add-shacl-warnings-for-five-safes-profile-conformance-conformsto-datepublished-licence
Add SHACL warnings for five-safes-profile conformance, datePublished, and licence
2 parents b4c33bd + 38243b3 commit b1e35f5

2 files changed

Lines changed: 272 additions & 0 deletions

File tree

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Copyright (c) 2025 eScience Lab, The University of Manchester
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
@prefix ro: <./> .
16+
@prefix ro-crate: <https://github.com/crs4/rocrate-validator/profiles/ro-crate/> .
17+
@prefix five-safes-crate: <https://github.com/eScienceLab/rocrate-validator/profiles/five-safes-crate/> .
18+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
19+
@prefix schema: <http://schema.org/> .
20+
@prefix dct: <http://purl.org/dc/terms/> .
21+
@prefix sh: <http://www.w3.org/ns/shacl#> .
22+
@prefix validator: <https://github.com/crs4/rocrate-validator/> .
23+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
24+
@prefix shp: <https://w3id.org/shp#> .
25+
26+
27+
# Root Dataset SHOULD declare conformsTo Five Safes profile
28+
five-safes-crate:RootDatasetConformsToFiveSafes
29+
a sh:NodeShape ;
30+
sh:name "RootDataEntity" ;
31+
sh:targetClass ro-crate:RootDataEntity ;
32+
33+
sh:property [
34+
a sh:PropertyShape ;
35+
sh:name "conformsTo Five Safes profile" ;
36+
sh:path dct:conformsTo ;
37+
sh:hasValue <https://w3id.org/5s-crate/0.4> ;
38+
sh:severity sh:Warning ;
39+
sh:message "Root Dataset SHOULD include `conformsTo` https://w3id.org/5s-crate/0.4" ;
40+
] .
41+
42+
five-safes-crate:RootDatasetDatePublishedWhenPublished
43+
a sh:NodeShape ;
44+
sh:name "datePublished present on published crates" ;
45+
sh:description "If the root dataset is published (has schema:publisher), it SHOULD have schema:datePublished." ;
46+
sh:targetClass ro-crate:RootDataEntity ;
47+
sh:severity sh:Warning ;
48+
sh:message "A crate SHOULD have a publishedDate if and only if it has a publisher." ;
49+
sh:xone (
50+
# datePublished SHOULD be present if and only if the publisher is specified:
51+
[ sh:not [ sh:property [
52+
sh:path schema:publisher ;
53+
sh:minCount 1 ;
54+
]]]
55+
[ sh:property [
56+
sh:path schema:datePublished ;
57+
sh:minCount 1 ;
58+
]]
59+
) .
60+
61+
five-safes-crate:RootDatasetLicenseWhenPublished
62+
a sh:NodeShape ;
63+
sh:name "License present on published crates" ;
64+
sh:description "If the root dataset is published (has schema:publisher), it SHOULD declare a license." ;
65+
sh:targetClass ro-crate:RootDataEntity ;
66+
sh:severity sh:Warning ;
67+
sh:message "Profile Conformance: Published crates SHOULD include a license." ;
68+
sh:or (
69+
# license not required if no publisher:
70+
[ sh:not [ sh:property [
71+
sh:path schema:publisher ;
72+
sh:minCount 1 ;
73+
]]]
74+
# license required if publisher present:
75+
[ sh:property [
76+
sh:path schema:license ;
77+
sh:minCount 1 ;
78+
]]
79+
) .
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
# Copyright (c) 2024-2025 CRS4
2+
# Copyright (c) 2025-2026 eScience Lab, The University of Manchester
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
import logging
17+
18+
from rocrate_validator.models import Severity
19+
from tests.ro_crates import ValidROC
20+
from tests.shared import do_entity_test, SPARQL_PREFIXES
21+
22+
# set up logging
23+
logger = logging.getLogger(__name__)
24+
25+
26+
# ----- MUST fails tests
27+
28+
29+
# ----- SHOULD fails tests
30+
31+
32+
def test_5src_root_data_entity_missing_conformsto_property():
33+
"""
34+
Test a Five Safes Crate where the RootDataEntity does not have the conformsTo property.
35+
"""
36+
sparql = (
37+
SPARQL_PREFIXES
38+
+ """
39+
DELETE {
40+
?rootdataentity dct:conformsTo ?profile .
41+
}
42+
WHERE {
43+
?metadatafile a schema:CreativeWork ;
44+
schema:about ?rootdataentity .
45+
?rootdataentity dct:conformsTo ?profile .
46+
}
47+
"""
48+
)
49+
50+
do_entity_test(
51+
rocrate_path=ValidROC().five_safes_crate_request,
52+
requirement_severity=Severity.RECOMMENDED,
53+
expected_validation_result=False,
54+
expected_triggered_requirements=["RootDataEntity"],
55+
expected_triggered_issues=[
56+
"Root Dataset SHOULD include `conformsTo` https://w3id.org/5s-crate/0.4"
57+
],
58+
profile_identifier="five-safes-crate",
59+
rocrate_entity_mod_sparql=sparql,
60+
)
61+
62+
63+
def test_5src_root_data_entity_conforms_to_wrong_profile():
64+
"""
65+
Test a Five Safes Crate where the RootDataEntity does not conform to the expected profile.
66+
"""
67+
sparql = (
68+
SPARQL_PREFIXES
69+
+ """
70+
DELETE {
71+
?rootdataentity dct:conformsTo ?profile .
72+
}
73+
INSERT {
74+
?rootdataentity dct:conformsTo "This is not the IRI to the 5sc profile"
75+
}
76+
WHERE {
77+
?metadatafile a schema:CreativeWork ;
78+
schema:about ?rootdataentity .
79+
?rootdataentity dct:conformsTo ?profile .
80+
}
81+
"""
82+
)
83+
84+
do_entity_test(
85+
rocrate_path=ValidROC().five_safes_crate_request,
86+
requirement_severity=Severity.RECOMMENDED,
87+
expected_validation_result=False,
88+
expected_triggered_requirements=["RootDataEntity"],
89+
expected_triggered_issues=[
90+
"Root Dataset SHOULD include `conformsTo` https://w3id.org/5s-crate/0.4"
91+
],
92+
profile_identifier="five-safes-crate",
93+
rocrate_entity_mod_sparql=sparql,
94+
)
95+
96+
97+
def test_5src_root_data_entity_has_publisher_but_not_date_published():
98+
"""
99+
Test a Five Safes Crate where the RootDataEntity has published but not date published.
100+
"""
101+
sparql = (
102+
SPARQL_PREFIXES
103+
+ """
104+
DELETE {
105+
?rootdataentity schema:datePublished ?datePublished .
106+
}
107+
WHERE {
108+
?metadatafile a schema:CreativeWork ;
109+
schema:about ?rootdataentity .
110+
?rootdataentity schema:publisher ?publisher ;
111+
schema:datePublished ?datePublished .
112+
}
113+
"""
114+
)
115+
116+
do_entity_test(
117+
rocrate_path=ValidROC().five_safes_crate_result,
118+
requirement_severity=Severity.RECOMMENDED,
119+
expected_validation_result=False,
120+
expected_triggered_requirements=["datePublished present on published crates"],
121+
expected_triggered_issues=[
122+
"A crate SHOULD have a publishedDate if and only if it has a publisher."
123+
],
124+
profile_identifier="five-safes-crate",
125+
rocrate_entity_mod_sparql=sparql,
126+
)
127+
128+
129+
def test_5src_root_data_entity_has_date_published_but_not_publisher():
130+
"""
131+
Test a Five Safes Crate where the RootDataEntity has published but not date published.
132+
"""
133+
sparql = (
134+
SPARQL_PREFIXES
135+
+ """
136+
DELETE {
137+
?rootdataentity schema:publisher ?publisher .
138+
}
139+
WHERE {
140+
?metadatafile a schema:CreativeWork ;
141+
schema:about ?rootdataentity .
142+
?rootdataentity schema:publisher ?publisher ;
143+
schema:datePublished ?datePublished .
144+
}
145+
"""
146+
)
147+
148+
do_entity_test(
149+
rocrate_path=ValidROC().five_safes_crate_result,
150+
requirement_severity=Severity.RECOMMENDED,
151+
expected_validation_result=False,
152+
expected_triggered_requirements=["datePublished present on published crates"],
153+
expected_triggered_issues=[
154+
"A crate SHOULD have a publishedDate if and only if it has a publisher."
155+
],
156+
profile_identifier="five-safes-crate",
157+
rocrate_entity_mod_sparql=sparql,
158+
)
159+
160+
161+
def test_5src_root_data_entity_has_publisher_but_not_license():
162+
"""
163+
Test a Five Safes Crate where the RootDataEntity has publisher but not license.
164+
"""
165+
sparql = (
166+
SPARQL_PREFIXES
167+
+ """
168+
DELETE {
169+
?rootdataentity schema:license ?license .
170+
}
171+
WHERE {
172+
?metadatafile a schema:CreativeWork ;
173+
schema:about ?rootdataentity .
174+
?rootdataentity schema:publisher ?publisher ;
175+
schema:license ?license .
176+
}
177+
"""
178+
)
179+
180+
do_entity_test(
181+
rocrate_path=ValidROC().five_safes_crate_result,
182+
requirement_severity=Severity.RECOMMENDED,
183+
expected_validation_result=False,
184+
expected_triggered_requirements=["License present on published crates"],
185+
expected_triggered_issues=[
186+
"Profile Conformance: Published crates SHOULD include a license."
187+
],
188+
profile_identifier="five-safes-crate",
189+
rocrate_entity_mod_sparql=sparql,
190+
)
191+
192+
193+
# ----- MAY fails tests

0 commit comments

Comments
 (0)