From 01ab43013002963cd1214426290060a1efc2a068 Mon Sep 17 00:00:00 2001 From: Ettore Murabito Date: Sun, 5 Oct 2025 11:13:20 +0100 Subject: [PATCH 01/13] first commit for set 1 of rules --- .../may/1_responsible_project.ttl | 47 +++++++++++ .../must/1.requesting_agent.ttl | 80 +++++++++++++++++++ .../must/1_responsible_project.ttl | 47 +++++++++++ .../must/1_root_data_entity.ttl | 46 +++++++++++ .../should/1_requesting_agent.ttl | 46 +++++++++++ 5 files changed, 266 insertions(+) create mode 100644 rocrate_validator/profiles/five-safes-crate/may/1_responsible_project.ttl create mode 100644 rocrate_validator/profiles/five-safes-crate/must/1.requesting_agent.ttl create mode 100644 rocrate_validator/profiles/five-safes-crate/must/1_responsible_project.ttl create mode 100644 rocrate_validator/profiles/five-safes-crate/must/1_root_data_entity.ttl create mode 100644 rocrate_validator/profiles/five-safes-crate/should/1_requesting_agent.ttl diff --git a/rocrate_validator/profiles/five-safes-crate/may/1_responsible_project.ttl b/rocrate_validator/profiles/five-safes-crate/may/1_responsible_project.ttl new file mode 100644 index 000000000..3274e8928 --- /dev/null +++ b/rocrate_validator/profiles/five-safes-crate/may/1_responsible_project.ttl @@ -0,0 +1,47 @@ +# Copyright (c) 2024-2025 CRS4 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +@prefix ro: <./> . +@prefix ro-crate: . +@prefix five-safes-crate: . +@prefix rdf: . +@prefix schema: . +@prefix sh: . +@prefix validator: . +@prefix xsd: . + + +five-safes-crate:ResponsibleProject + a sh:NodeShape ; + sh:name "Responsible Project" ; + sh:target [ + a sh:SPARQLTarget ; + sh:prefixes ro-crate:sparqlPrefixes ; + sh:select """ + SELECT DISTINCT ?this WHERE { + ?action a schema:CreateAction ; + schema:agent ?agent . + ?agent schema:memberOf ?this . + } + """ + ] ; + + sh:property [ + a sh:PropertyShape ; + sh:name "funding" ; + sh:path schema:funding; + sh:minCount 1 ; + sh:severity sh:Info ; + sh:message """The Responsible Project MAY have a 'funding' property.""" ; + ] . \ No newline at end of file diff --git a/rocrate_validator/profiles/five-safes-crate/must/1.requesting_agent.ttl b/rocrate_validator/profiles/five-safes-crate/must/1.requesting_agent.ttl new file mode 100644 index 000000000..967fb9123 --- /dev/null +++ b/rocrate_validator/profiles/five-safes-crate/must/1.requesting_agent.ttl @@ -0,0 +1,80 @@ +# Copyright (c) 2024-2025 CRS4 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +@prefix ro: <./> . +@prefix ro-crate: . +@prefix five-safes-crate: . +@prefix rdf: . +@prefix schema: . +@prefix sh: . +@prefix validator: . +@prefix xsd: . + + +five-safes-crate:AgentIsMemberOf + a sh:NodeShape ; + sh:name "Requesting Agent" ; + sh:target [ + a sh:SPARQLTarget ; + sh:prefixes ro-crate:sparqlPrefixes ; + sh:select """ + SELECT DISTINCT ?this WHERE { + ?action a schema:CreateAction ; + schema:agent ?this . + } + """ + ] ; + + sh:property [ + a sh:PropertyShape ; + sh:name "memberOf" ; + sh:path schema:memberOf; + sh:class schema:Project ; + sh:severity sh:Violation ; + sh:message """The 'memberOf' property of an agent MUST be of type Project.""" ; + ] . + + +# Rule 5 +five-safes-crate:AgentProjectIntersection + a sh:NodeShape ; + sh:name "Agent" ; + sh:description "At least one Project referenced by Agent -> memberOf MUST be included in the set of Projects referenced by RootDataEntity -> sourceOrganization." ; + sh:target [ + a sh:SPARQLTarget ; + sh:prefixes ro-crate:sparqlPrefixes ; + sh:select """ + SELECT DISTINCT ?this WHERE { + ?action a schema:CreateAction ; + schema:agent ?this . + } + """ + ] ; + sh:sparql [ + a sh:SPARQLConstraint ; + sh:name "MemberOf" ; + sh:description """At least one Project referenced by Agent -> memberOf MUST be included in the set of Projects referenced by RootDataEntity -> sourceOrganization.""" ; + sh:message """At least one Project referenced by Agent -> memberOf MUST be included in the set of Projects referenced by RootDataEntity -> sourceOrganization.""" ; + sh:prefixes ro-crate:sparqlPrefixes ; + sh:select """ + SELECT $this WHERE { + FILTER NOT EXISTS { + $this schema:memberOf ?commonProject . + ?metadata schema:about ?root . + ?root schema:sourceOrganization ?commonProject . + } + } + """ ; + sh:severity sh:Violation ; + ] . \ No newline at end of file diff --git a/rocrate_validator/profiles/five-safes-crate/must/1_responsible_project.ttl b/rocrate_validator/profiles/five-safes-crate/must/1_responsible_project.ttl new file mode 100644 index 000000000..42ff0ced0 --- /dev/null +++ b/rocrate_validator/profiles/five-safes-crate/must/1_responsible_project.ttl @@ -0,0 +1,47 @@ +# Copyright (c) 2024-2025 CRS4 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +@prefix ro: <./> . +@prefix ro-crate: . +@prefix five-safes-crate: . +@prefix rdf: . +@prefix schema: . +@prefix sh: . +@prefix validator: . +@prefix xsd: . + + +five-safes-crate:ResponsibleProject + a sh:NodeShape ; + sh:name "Responsible Project" ; + sh:target [ + a sh:SPARQLTarget ; + sh:prefixes ro-crate:sparqlPrefixes ; + sh:select """ + SELECT DISTINCT ?this WHERE { + ?action a schema:CreateAction ; + schema:agent ?agent . + ?agent schema:memberOf ?this . + } + """ + ] ; + + sh:property [ + a sh:PropertyShape ; + sh:name "funding" ; + sh:path schema:funding; + sh:class schema:Grant ; + sh:severity sh:Violation ; + sh:message """The property 'funding' of the Responsible Project MUST be of type schema:Grant.""" ; + ] . \ No newline at end of file diff --git a/rocrate_validator/profiles/five-safes-crate/must/1_root_data_entity.ttl b/rocrate_validator/profiles/five-safes-crate/must/1_root_data_entity.ttl new file mode 100644 index 000000000..035c72d1a --- /dev/null +++ b/rocrate_validator/profiles/five-safes-crate/must/1_root_data_entity.ttl @@ -0,0 +1,46 @@ +# Copyright (c) 2024-2025 CRS4 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +@prefix ro: <./> . +@prefix ro-crate: . +@prefix five-safes-crate: . +@prefix rdf: . +@prefix schema: . +@prefix sh: . +@prefix validator: . +@prefix xsd: . + + +five-safes-crate:RootDataEntityRequiredProperties + a sh:NodeShape ; + sh:name "Root Data Entity" ; + sh:targetClass ro-crate:RootDataEntity ; + + sh:property [ + a sh:PropertyShape ; + sh:name "sourceOrganization" ; + sh:path schema:sourceOrganization; + sh:minCount 1 ; + sh:severity sh:Violation ; + sh:message """The Root Data Entity MUST have a `sourceOrganization` property.""" ; + ] ; + + sh:property [ + a sh:PropertyShape ; + sh:name "sourceOrganization" ; + sh:path schema:sourceOrganization ; + sh:class schema:Project ; + sh:severity sh:Violation ; + sh:message """The `sourceOrganization` property of the Root Data Entity MUST point to a Project entity.""" ; + ] . diff --git a/rocrate_validator/profiles/five-safes-crate/should/1_requesting_agent.ttl b/rocrate_validator/profiles/five-safes-crate/should/1_requesting_agent.ttl new file mode 100644 index 000000000..ee3a6cac2 --- /dev/null +++ b/rocrate_validator/profiles/five-safes-crate/should/1_requesting_agent.ttl @@ -0,0 +1,46 @@ +# Copyright (c) 2024-2025 CRS4 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +@prefix ro: <./> . +@prefix ro-crate: . +@prefix five-safes-crate: . +@prefix rdf: . +@prefix schema: . +@prefix sh: . +@prefix validator: . +@prefix xsd: . + + +five-safes-crate:AgentIsMemberOf + a sh:NodeShape ; + sh:name "Requesting Agent" ; + sh:target [ + a sh:SPARQLTarget ; + sh:prefixes ro-crate:sparqlPrefixes ; + sh:select """ + SELECT DISTINCT ?this WHERE { + ?action a schema:CreateAction ; + schema:agent ?this . + } + """ + ] ; + + sh:property [ + a sh:PropertyShape ; + sh:name "memberOf" ; + sh:path schema:memberOf; + sh:minCount 1 ; + sh:severity sh:Warning ; + sh:message """The Requesting Agent SHOULD have a `memberOf` property.""" ; + ] . \ No newline at end of file From 7ff5c9dd69cd478ca212acaf7b7c1e96f3461423 Mon Sep 17 00:00:00 2001 From: Ettore Murabito Date: Sun, 5 Oct 2025 16:42:55 +0100 Subject: [PATCH 02/13] Added extra tests --- .../may/1_responsible_project.ttl | 11 +++- .../must/1.requesting_agent.ttl | 1 - .../must/1_responsible_project.ttl | 9 +++ .../must/1_root_data_entity_metadata.ttl | 38 ------------ .../should/1_responsible_project.ttl | 60 +++++++++++++++++++ 5 files changed, 79 insertions(+), 40 deletions(-) delete mode 100644 rocrate_validator/profiles/five-safes-crate/must/1_root_data_entity_metadata.ttl create mode 100644 rocrate_validator/profiles/five-safes-crate/should/1_responsible_project.ttl diff --git a/rocrate_validator/profiles/five-safes-crate/may/1_responsible_project.ttl b/rocrate_validator/profiles/five-safes-crate/may/1_responsible_project.ttl index 3274e8928..0b199d7e7 100644 --- a/rocrate_validator/profiles/five-safes-crate/may/1_responsible_project.ttl +++ b/rocrate_validator/profiles/five-safes-crate/may/1_responsible_project.ttl @@ -43,5 +43,14 @@ five-safes-crate:ResponsibleProject sh:path schema:funding; sh:minCount 1 ; sh:severity sh:Info ; - sh:message """The Responsible Project MAY have a 'funding' property.""" ; + sh:message """The Responsible Project does not have the property 'funding'.""" ; + ] ; + + sh:property [ + a sh:PropertyShape ; + sh:name "member" ; + sh:path schema:member; + sh:minCount 1 ; + sh:severity sh:Info ; + sh:message """The Responsible Project does not have the property 'member'.""" ; ] . \ No newline at end of file diff --git a/rocrate_validator/profiles/five-safes-crate/must/1.requesting_agent.ttl b/rocrate_validator/profiles/five-safes-crate/must/1.requesting_agent.ttl index 967fb9123..220b9b06d 100644 --- a/rocrate_validator/profiles/five-safes-crate/must/1.requesting_agent.ttl +++ b/rocrate_validator/profiles/five-safes-crate/must/1.requesting_agent.ttl @@ -46,7 +46,6 @@ five-safes-crate:AgentIsMemberOf ] . -# Rule 5 five-safes-crate:AgentProjectIntersection a sh:NodeShape ; sh:name "Agent" ; diff --git a/rocrate_validator/profiles/five-safes-crate/must/1_responsible_project.ttl b/rocrate_validator/profiles/five-safes-crate/must/1_responsible_project.ttl index 42ff0ced0..adb2d7039 100644 --- a/rocrate_validator/profiles/five-safes-crate/must/1_responsible_project.ttl +++ b/rocrate_validator/profiles/five-safes-crate/must/1_responsible_project.ttl @@ -44,4 +44,13 @@ five-safes-crate:ResponsibleProject sh:class schema:Grant ; sh:severity sh:Violation ; sh:message """The property 'funding' of the Responsible Project MUST be of type schema:Grant.""" ; + ] ; + + sh:property [ + a sh:PropertyShape ; + sh:name "member" ; + sh:path schema:member; + sh:class schema:Organization ; + sh:severity sh:Violation ; + sh:message """The property 'member' of the Responsible Project MUST be of type schema:Organization.""" ; ] . \ No newline at end of file diff --git a/rocrate_validator/profiles/five-safes-crate/must/1_root_data_entity_metadata.ttl b/rocrate_validator/profiles/five-safes-crate/must/1_root_data_entity_metadata.ttl deleted file mode 100644 index 1145bb5a3..000000000 --- a/rocrate_validator/profiles/five-safes-crate/must/1_root_data_entity_metadata.ttl +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) 2024-2025 CRS4 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -@prefix ro: <./> . -@prefix ro-crate: . -@prefix five-safes-crate: . -@prefix rdf: . -@prefix schema_org: . -@prefix sh: . -@prefix validator: . -@prefix xsd: . - -five-safes-crate:RootDataEntityRequiredProperties - a sh:NodeShape ; - sh:name "Five Safes Crate Root Data Entity REQUIRED properties" ; - sh:description "The Root Data Entity MUST have a `sourceOrganisation`" ; - sh:targetClass ro-crate:RootDataEntity ; - sh:property [ - a sh:PropertyShape ; - sh:name "Root Data Entity: `sourceOrganization` property" ; - sh:description """Check if the Root Data Entity includes a `sourceOrganization` (as specified by schema.org).""" ; - sh:path schema_org:sourceOrganization; - sh:minCount 1 ; - sh:nodeKind sh:IRI; - sh:message """The Root Data Entity MUST have a `sourceOrganization` property (as specified by schema.org). - SHOULD link to a Contextual Entity in the RO-Crate Metadata File with a name.""" ; - ] . diff --git a/rocrate_validator/profiles/five-safes-crate/should/1_responsible_project.ttl b/rocrate_validator/profiles/five-safes-crate/should/1_responsible_project.ttl new file mode 100644 index 000000000..05aa79571 --- /dev/null +++ b/rocrate_validator/profiles/five-safes-crate/should/1_responsible_project.ttl @@ -0,0 +1,60 @@ +# Copyright (c) 2024-2025 CRS4 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +@prefix ro: <./> . +@prefix ro-crate: . +@prefix five-safes-crate: . +@prefix rdf: . +@prefix schema: . +@prefix sh: . +@prefix validator: . +@prefix xsd: . + + +five-safes-crate:ResponsibleProjectMemberAndSourceOrganizationIntersection + a sh:NodeShape ; + sh:name "Organizations (members of Responsible Project)" ; + sh:description """At least one of the organisations that are members of the responsible project SHOULD be included in the Requesting Agent's affiliations, if such properties exist.""" ; + sh:target [ + a sh:SPARQLTarget ; + sh:prefixes ro-crate:sparqlPrefixes ; + sh:select """ + SELECT DISTINCT ?this WHERE { + ?action a schema:CreateAction ; + schema:agent ?this . + ?this a schema:Person ; + schema:memberOf ?project ; + schema:affiliation ?someAffiliation . + ?project schema:member ?org2 . + } + """ + ] ; + sh:sparql [ + a sh:SPARQLConstraint ; + sh:name "Intersection with agent affiliations" ; + sh:description """At least one of the organisations that are members of the responsible project SHOULD be included in the Requesting Agent's affiliations, if such properties exist.""" ; + + sh:prefixes ro-crate:sparqlPrefixes ; + sh:select """ + SELECT $this WHERE { + FILTER NOT EXISTS { + $this schema:affiliation ?org . + $this schema:memberOf ?project . + ?project schema:member ?org . + } + } + """ ; + sh:severity sh:Warning ; + sh:message """At least one of the organisations that are members of the responsible project SHOULD be included in the Requesting Agent's affiliations.""" ; + ] . \ No newline at end of file From 0122c79c90f7df617c5465509faadd377d73a305 Mon Sep 17 00:00:00 2001 From: Ettore Murabito Date: Mon, 6 Oct 2025 07:51:23 +0100 Subject: [PATCH 03/13] Made test 'five-safes-crate:AgentProjectIntersection' in 'must/1_requesting_agent.ttl' more robust. --- .../five-safes-crate/must/1.requesting_agent.ttl | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/rocrate_validator/profiles/five-safes-crate/must/1.requesting_agent.ttl b/rocrate_validator/profiles/five-safes-crate/must/1.requesting_agent.ttl index 220b9b06d..481b8a5eb 100644 --- a/rocrate_validator/profiles/five-safes-crate/must/1.requesting_agent.ttl +++ b/rocrate_validator/profiles/five-safes-crate/must/1.requesting_agent.ttl @@ -48,8 +48,8 @@ five-safes-crate:AgentIsMemberOf five-safes-crate:AgentProjectIntersection a sh:NodeShape ; - sh:name "Agent" ; - sh:description "At least one Project referenced by Agent -> memberOf MUST be included in the set of Projects referenced by RootDataEntity -> sourceOrganization." ; + sh:name "Agent Project Intersection" ; + sh:description """At least one Project referenced by Agent -> memberOf MUST be included in the set of Projects referenced by RootDataEntity -> sourceOrganization.""" ; sh:target [ a sh:SPARQLTarget ; sh:prefixes ro-crate:sparqlPrefixes ; @@ -62,12 +62,14 @@ five-safes-crate:AgentProjectIntersection ] ; sh:sparql [ a sh:SPARQLConstraint ; - sh:name "MemberOf" ; - sh:description """At least one Project referenced by Agent -> memberOf MUST be included in the set of Projects referenced by RootDataEntity -> sourceOrganization.""" ; - sh:message """At least one Project referenced by Agent -> memberOf MUST be included in the set of Projects referenced by RootDataEntity -> sourceOrganization.""" ; + sh:name "Agent Project Intersection" ; + sh:description """At least one Project referenced by Agent -> memberOf MUST be included in the set of Projects referenced by RootDataEntity -> sourceOrganization.""" ; sh:prefixes ro-crate:sparqlPrefixes ; sh:select """ SELECT $this WHERE { + FILTER EXISTS { + $this schema:memberOf ?anyProject . + } FILTER NOT EXISTS { $this schema:memberOf ?commonProject . ?metadata schema:about ?root . @@ -76,4 +78,5 @@ five-safes-crate:AgentProjectIntersection } """ ; sh:severity sh:Violation ; + sh:message """At least one Project referenced by Agent -> memberOf MUST be included in the set of Projects referenced by RootDataEntity -> sourceOrganization.""" ; ] . \ No newline at end of file From ac46c7595aaffe774f3ca00ef1857171e8f12087 Mon Sep 17 00:00:00 2001 From: Ettore Murabito Date: Wed, 8 Oct 2025 15:04:09 +0100 Subject: [PATCH 04/13] Changed the Copyright --- .../may/1_responsible_project.ttl | 2 +- .../must/1.requesting_agent.ttl | 2 +- .../five-safes-crate/must/1_funder.ttl | 36 ------------------- .../must/1_responsible_project.ttl | 2 +- .../must/1_root_data_entity.ttl | 2 +- .../should/1_requesting_agent.ttl | 2 +- .../should/1_responsible_project.ttl | 2 +- 7 files changed, 6 insertions(+), 42 deletions(-) delete mode 100644 rocrate_validator/profiles/five-safes-crate/must/1_funder.ttl diff --git a/rocrate_validator/profiles/five-safes-crate/may/1_responsible_project.ttl b/rocrate_validator/profiles/five-safes-crate/may/1_responsible_project.ttl index 0b199d7e7..251ba0725 100644 --- a/rocrate_validator/profiles/five-safes-crate/may/1_responsible_project.ttl +++ b/rocrate_validator/profiles/five-safes-crate/may/1_responsible_project.ttl @@ -1,4 +1,4 @@ -# Copyright (c) 2024-2025 CRS4 +# Copyright (c) 2025 eScience Lab, The University of Manchester # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/rocrate_validator/profiles/five-safes-crate/must/1.requesting_agent.ttl b/rocrate_validator/profiles/five-safes-crate/must/1.requesting_agent.ttl index 481b8a5eb..2cc0d434d 100644 --- a/rocrate_validator/profiles/five-safes-crate/must/1.requesting_agent.ttl +++ b/rocrate_validator/profiles/five-safes-crate/must/1.requesting_agent.ttl @@ -1,4 +1,4 @@ -# Copyright (c) 2024-2025 CRS4 +# Copyright (c) 2025 eScience Lab, The University of Manchester # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/rocrate_validator/profiles/five-safes-crate/must/1_funder.ttl b/rocrate_validator/profiles/five-safes-crate/must/1_funder.ttl deleted file mode 100644 index 474fda436..000000000 --- a/rocrate_validator/profiles/five-safes-crate/must/1_funder.ttl +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) 2024-2025 CRS4 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -@prefix ro: <./> . -@prefix ro-crate: . -@prefix five-safes-crate: . -@prefix schema_org: . -@prefix sh: . -@prefix bioschemas: . - -# TODO: note that this applies to all Projects, not just those targeted by `funding` relationships -five-safes-crate:FundingBody a sh:NodeShape ; - sh:name "Funding body Project" ; - sh:description "Project which is funding this work" ; - sh:targetClass schema_org:Project ; - sh:property [ - a sh:PropertyShape ; - sh:name "Project Name" ; - sh:description """Check if the Project Entity `name` (as specified by schema.org) - to clearly identify the dataset and distinguish it from other projects.""" ; - sh:minCount 1 ; - sh:nodeKind sh:Literal ; - sh:path schema_org:name; - sh:message "The Project Entity MUST have a `name` property (as specified by schema.org)" ; - ] . diff --git a/rocrate_validator/profiles/five-safes-crate/must/1_responsible_project.ttl b/rocrate_validator/profiles/five-safes-crate/must/1_responsible_project.ttl index adb2d7039..e9668fd07 100644 --- a/rocrate_validator/profiles/five-safes-crate/must/1_responsible_project.ttl +++ b/rocrate_validator/profiles/five-safes-crate/must/1_responsible_project.ttl @@ -1,4 +1,4 @@ -# Copyright (c) 2024-2025 CRS4 +# Copyright (c) 2025 eScience Lab, The University of Manchester # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/rocrate_validator/profiles/five-safes-crate/must/1_root_data_entity.ttl b/rocrate_validator/profiles/five-safes-crate/must/1_root_data_entity.ttl index 035c72d1a..1f44073cc 100644 --- a/rocrate_validator/profiles/five-safes-crate/must/1_root_data_entity.ttl +++ b/rocrate_validator/profiles/five-safes-crate/must/1_root_data_entity.ttl @@ -1,4 +1,4 @@ -# Copyright (c) 2024-2025 CRS4 +# Copyright (c) 2025 eScience Lab, The University of Manchester # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/rocrate_validator/profiles/five-safes-crate/should/1_requesting_agent.ttl b/rocrate_validator/profiles/five-safes-crate/should/1_requesting_agent.ttl index ee3a6cac2..332c67d3d 100644 --- a/rocrate_validator/profiles/five-safes-crate/should/1_requesting_agent.ttl +++ b/rocrate_validator/profiles/five-safes-crate/should/1_requesting_agent.ttl @@ -1,4 +1,4 @@ -# Copyright (c) 2024-2025 CRS4 +# Copyright (c) 2025 eScience Lab, The University of Manchester # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/rocrate_validator/profiles/five-safes-crate/should/1_responsible_project.ttl b/rocrate_validator/profiles/five-safes-crate/should/1_responsible_project.ttl index 05aa79571..47c1e5fcb 100644 --- a/rocrate_validator/profiles/five-safes-crate/should/1_responsible_project.ttl +++ b/rocrate_validator/profiles/five-safes-crate/should/1_responsible_project.ttl @@ -1,4 +1,4 @@ -# Copyright (c) 2024-2025 CRS4 +# Copyright (c) 2025 eScience Lab, The University of Manchester # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. From 2d2d882a4dfbe6ef5f37cbaad8cd9fc91eaf415b Mon Sep 17 00:00:00 2001 From: Ettore Murabito Date: Tue, 28 Oct 2025 09:06:36 +0000 Subject: [PATCH 05/13] Added tests for ruleset 1 --- .../may/1_responsible_project.ttl | 4 +- ...sting_agent.ttl => 1_requesting_agent.ttl} | 0 .../must/1_root_data_entity.ttl | 4 +- .../test_5src_1_requesting_agent.py | 135 ++++++++++++++ .../test_5src_1_responsible_project.py | 170 ++++++++++++++++++ .../test_5src_1_root_data_entity_metadata.py | 89 +++++++++ .../five-safes-crate/test_5src_funding.py | 5 +- .../test_5src_root_data_entity_metadata.py | 75 -------- 8 files changed, 402 insertions(+), 80 deletions(-) rename rocrate_validator/profiles/five-safes-crate/must/{1.requesting_agent.ttl => 1_requesting_agent.ttl} (100%) create mode 100644 tests/integration/profiles/five-safes-crate/test_5src_1_requesting_agent.py create mode 100644 tests/integration/profiles/five-safes-crate/test_5src_1_responsible_project.py create mode 100644 tests/integration/profiles/five-safes-crate/test_5src_1_root_data_entity_metadata.py delete mode 100644 tests/integration/profiles/five-safes-crate/test_5src_root_data_entity_metadata.py diff --git a/rocrate_validator/profiles/five-safes-crate/may/1_responsible_project.ttl b/rocrate_validator/profiles/five-safes-crate/may/1_responsible_project.ttl index 251ba0725..5dd46fd27 100644 --- a/rocrate_validator/profiles/five-safes-crate/may/1_responsible_project.ttl +++ b/rocrate_validator/profiles/five-safes-crate/may/1_responsible_project.ttl @@ -43,7 +43,7 @@ five-safes-crate:ResponsibleProject sh:path schema:funding; sh:minCount 1 ; sh:severity sh:Info ; - sh:message """The Responsible Project does not have the property 'funding'.""" ; + sh:message """The Responsible Project does not have the property `funding`.""" ; ] ; sh:property [ @@ -52,5 +52,5 @@ five-safes-crate:ResponsibleProject sh:path schema:member; sh:minCount 1 ; sh:severity sh:Info ; - sh:message """The Responsible Project does not have the property 'member'.""" ; + sh:message """The Responsible Project does not have the property `member`.""" ; ] . \ No newline at end of file diff --git a/rocrate_validator/profiles/five-safes-crate/must/1.requesting_agent.ttl b/rocrate_validator/profiles/five-safes-crate/must/1_requesting_agent.ttl similarity index 100% rename from rocrate_validator/profiles/five-safes-crate/must/1.requesting_agent.ttl rename to rocrate_validator/profiles/five-safes-crate/must/1_requesting_agent.ttl diff --git a/rocrate_validator/profiles/five-safes-crate/must/1_root_data_entity.ttl b/rocrate_validator/profiles/five-safes-crate/must/1_root_data_entity.ttl index 1f44073cc..bb9a51471 100644 --- a/rocrate_validator/profiles/five-safes-crate/must/1_root_data_entity.ttl +++ b/rocrate_validator/profiles/five-safes-crate/must/1_root_data_entity.ttl @@ -24,7 +24,7 @@ five-safes-crate:RootDataEntityRequiredProperties a sh:NodeShape ; - sh:name "Root Data Entity" ; + sh:name "RootDataEntity" ; sh:targetClass ro-crate:RootDataEntity ; sh:property [ @@ -42,5 +42,5 @@ five-safes-crate:RootDataEntityRequiredProperties sh:path schema:sourceOrganization ; sh:class schema:Project ; sh:severity sh:Violation ; - sh:message """The `sourceOrganization` property of the Root Data Entity MUST point to a Project entity.""" ; + sh:message """The `sourceOrganization` property of the RootDataEntity MUST point to a Project entity.""" ; ] . diff --git a/tests/integration/profiles/five-safes-crate/test_5src_1_requesting_agent.py b/tests/integration/profiles/five-safes-crate/test_5src_1_requesting_agent.py new file mode 100644 index 000000000..a1809fb17 --- /dev/null +++ b/tests/integration/profiles/five-safes-crate/test_5src_1_requesting_agent.py @@ -0,0 +1,135 @@ +# Copyright (c) 2024-2025 CRS4 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging + +from rocrate_validator.models import Severity +from tests.ro_crates import ValidROC +from tests.shared import do_entity_test, SPARQL_PREFIXES + +# set up logging +logger = logging.getLogger(__name__) + + +# ----- MUST fails tests + + +def test_5src_agent_memberOf_not_project(): + """ + Test a Five Safes Crate where an agent's `memberOf` does NOT reference a schema:Project. + (We replace the referenced Project with a plain literal.) + """ + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + ?agent schema:memberOf ?org . + } + INSERT { + ?agent schema:memberOf "Not a project (literal replacement)" + } + WHERE { + ?action a schema:CreateAction ; + schema:agent ?agent . + ?agent schema:memberOf ?org . + ?org a schema:Project . + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_request, + requirement_severity=Severity.REQUIRED, + expected_validation_result=False, + expected_triggered_requirements=["Requesting Agent"], + expected_triggered_issues=[ + "The 'memberOf' property of an agent MUST be of type Project." + ], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +def test_5src_agent_memberOf_project_not_in_root(): + """ + Test a Five Safes Crate where NONE of the Projects referenced by Agent->memberOf are included + in the set of Projects referenced by RootDataEntity->sourceOrganization. + (We replace an agent's memberOf with a new Project that the root does not reference.) + """ + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + ?agent schema:memberOf ?org . + } + INSERT { + # assign the agent to a new Project that is not referenced by the Root Data Entity + ?agent schema:memberOf <./missing-project> . + <./missing-project> a schema:Project . + } + WHERE { + # locate a CreateAction -> agent -> memberOf that currently points to a Project + ?action a schema:CreateAction ; + schema:agent ?agent . + + ?agent schema:memberOf ?org . + ?org a schema:Project . + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_request, + requirement_severity=Severity.REQUIRED, + expected_validation_result=False, + expected_triggered_requirements=["Agent Project Intersection"], + expected_triggered_issues=[ + """At least one Project referenced by Agent -> memberOf MUST be included in the set of Projects referenced by RootDataEntity -> sourceOrganization.""" + ], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +# ----- SHOULD warns tests +def test_5src_agent_memberOf_missing_warning(): + """ + Test a Five Safes Crate where the Requesting Agent does NOT have the 'memberOf' property. + This should trigger the SHACL warning: the Requesting Agent SHOULD have a `memberOf` property. + """ + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + ?agent schema:memberOf ?org . + } + WHERE { + ?action a schema:CreateAction ; + schema:agent ?agent . + ?agent schema:memberOf ?org . + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_request, + requirement_severity=Severity.RECOMMENDED, + expected_validation_result=False, # or True if warnings are not treated as failures + expected_triggered_requirements=["Requesting Agent"], + expected_triggered_issues=[ + "The Requesting Agent SHOULD have a `memberOf` property." + ], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) diff --git a/tests/integration/profiles/five-safes-crate/test_5src_1_responsible_project.py b/tests/integration/profiles/five-safes-crate/test_5src_1_responsible_project.py new file mode 100644 index 000000000..6ed1b3a96 --- /dev/null +++ b/tests/integration/profiles/five-safes-crate/test_5src_1_responsible_project.py @@ -0,0 +1,170 @@ +# Copyright (c) 2024-2025 CRS4 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging + +from rocrate_validator.models import Severity +from tests.ro_crates import ValidROC +from tests.shared import do_entity_test, SPARQL_PREFIXES + +# set up logging +logger = logging.getLogger(__name__) + + +# ---- MUST fails tests + + +def test_5src_responsible_project_funding_not_grant(): + """ + Test a Five Safes Crate where a Responsible Project's `funding` property + is NOT of type schema:Grant. + (We replace the funding reference with a literal.) + """ + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + ?project schema:funding ?grant . + } + INSERT { + ?project schema:funding "Not a grant (literal replacement)" . + } + WHERE { + ?action a schema:CreateAction ; + schema:agent ?agent . + ?agent schema:memberOf ?project . + ?project schema:funding ?grant . + ?grant a schema:Grant . + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_request, + requirement_severity=Severity.REQUIRED, + expected_validation_result=False, + expected_triggered_requirements=["Responsible Project"], + expected_triggered_issues=[ + "The property 'funding' of the Responsible Project MUST be of type schema:Grant." + ], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +def test_5src_responsible_project_member_not_organization(): + """ + Test a Five Safes Crate where a Responsible Project's `member` property + is NOT of type schema:Organization. + (We replace the member reference with a literal.) + """ + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + ?project schema:member ?org . + } + INSERT { + ?project schema:member "Not an organization (literal replacement)" . + } + WHERE { + ?action a schema:CreateAction ; + schema:agent ?agent . + ?agent schema:memberOf ?project . + ?project schema:member ?org . + ?org a schema:Organization . + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_request, + requirement_severity=Severity.REQUIRED, + expected_validation_result=False, + expected_triggered_requirements=["Responsible Project"], + expected_triggered_issues=[ + "The property 'member' of the Responsible Project MUST be of type schema:Organization." + ], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +# ---- MAY warns tests + + +def test_5src_responsible_project_missing_funding_property(): + """ + Test a Five Safes Crate where a Responsible Project does NOT have the `funding` property. + This should trigger the SHACL info: 'The Responsible Project does not have the property `funding`.' + """ + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + ?project schema:funding ?f . + } + WHERE { + ?action a schema:CreateAction ; + schema:agent ?agent . + ?agent schema:memberOf ?project . + ?project schema:funding ?f . + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_request, + requirement_severity=Severity.OPTIONAL, + expected_validation_result=False, # or True if Info is not treated as failure + expected_triggered_requirements=["Responsible Project"], + expected_triggered_issues=[ + "The Responsible Project does not have the property `funding`." + ], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +def test_5src_responsible_project_missing_member_property(): + """ + Test a Five Safes Crate where a Responsible Project does NOT have the `member` property. + This should trigger the SHACL info: 'The Responsible Project does not have the property `member`.' + """ + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + ?project schema:member ?m . + } + WHERE { + ?action a schema:CreateAction ; + schema:agent ?agent . + ?agent schema:memberOf ?project . + ?project schema:member ?m . + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_request, + requirement_severity=Severity.OPTIONAL, + expected_validation_result=False, # or True if Info is treated as failure + expected_triggered_requirements=["Responsible Project"], + expected_triggered_issues=[ + "The Responsible Project does not have the property `member`." + ], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) diff --git a/tests/integration/profiles/five-safes-crate/test_5src_1_root_data_entity_metadata.py b/tests/integration/profiles/five-safes-crate/test_5src_1_root_data_entity_metadata.py new file mode 100644 index 000000000..54ddce7ee --- /dev/null +++ b/tests/integration/profiles/five-safes-crate/test_5src_1_root_data_entity_metadata.py @@ -0,0 +1,89 @@ +# Copyright (c) 2024-2025 CRS4 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging + +from rocrate_validator.models import Severity +from tests.ro_crates import ValidROC +from tests.shared import do_entity_test, SPARQL_PREFIXES + +# set up logging +logger = logging.getLogger(__name__) + + +# ----- MUST fails tests + + +def test_5src_root_data_entity_without_source_organization(): + """ + Test a Five Safes Crate where the Root Data Entity does NOT have the 'sourceOrganization' property. + """ + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + <./> schema:sourceOrganization ?o . + } + WHERE { + <./> schema:sourceOrganization ?o . + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_request, + requirement_severity=Severity.REQUIRED, + expected_validation_result=False, + expected_triggered_requirements=["RootDataEntity"], + expected_triggered_issues=[ + """The Root Data Entity MUST have a `sourceOrganization` property.""" + ], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +def test_5src_root_data_entity_source_organization_not_organization(): + """ + Test a Five Safes Crate where the Root Data Entity's `sourceOrganization` property + does NOT reference a `schema:Project` entity. + (We replace any existing sourceOrganization with a literal to violate the class constraint.) + """ + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + <./> schema:sourceOrganization ?o . + } + INSERT { + # insert a literal instead of an IRI + <./> schema:sourceOrganization "Investigation of cancer (TRE72 project 81)" . + } + WHERE { + <./> schema:sourceOrganization ?o . + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_request, + requirement_severity=Severity.REQUIRED, + expected_validation_result=False, + expected_triggered_requirements=["RootDataEntity"], + expected_triggered_issues=[ + """The `sourceOrganization` property of the RootDataEntity MUST point to a Project entity.""" + ], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) diff --git a/tests/integration/profiles/five-safes-crate/test_5src_funding.py b/tests/integration/profiles/five-safes-crate/test_5src_funding.py index 58b2f5746..8543a69d3 100644 --- a/tests/integration/profiles/five-safes-crate/test_5src_funding.py +++ b/tests/integration/profiles/five-safes-crate/test_5src_funding.py @@ -26,10 +26,13 @@ def test_5src_funding_project_no_name(): """\ Test a Five Safes Crate where the funding Project does not have a name. """ - sparql = SPARQL_PREFIXES + """DELETE WHERE { + sparql = ( + SPARQL_PREFIXES + + """DELETE WHERE { <#project-be6ffb55-4f5a-4c14-b60e-47e0951090c70> schema:name "Investigation of cancer (TRE72 project 81)" } """ + ) do_entity_test( rocrate_path=ValidROC().five_safes_crate_result, diff --git a/tests/integration/profiles/five-safes-crate/test_5src_root_data_entity_metadata.py b/tests/integration/profiles/five-safes-crate/test_5src_root_data_entity_metadata.py deleted file mode 100644 index 92417caa7..000000000 --- a/tests/integration/profiles/five-safes-crate/test_5src_root_data_entity_metadata.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (c) 2024-2025 CRS4 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import logging - -from rocrate_validator.models import Severity -from tests.ro_crates import ValidROC -from tests.shared import do_entity_test, SPARQL_PREFIXES - -# set up logging -logger = logging.getLogger(__name__) - - -def test_5src_root_data_entity_no_source_organization(): - """\ - Test a Five Safes Crate where the Root Data Entity it does not reference a sourceOrganization. - """ - sparql = SPARQL_PREFIXES + """DELETE WHERE { - <./> schema:sourceOrganization ?object - } - """ - - do_entity_test( - rocrate_path=ValidROC().five_safes_crate_result, - requirement_severity=Severity.REQUIRED, - expected_validation_result=False, - expected_triggered_requirements=["Five Safes Crate Root Data Entity REQUIRED properties"], - expected_triggered_issues=[ - """The Root Data Entity MUST have a `sourceOrganization` property (as specified by schema.org). - SHOULD link to a Contextual Entity in the RO-Crate Metadata File with a name.""" - ], - profile_identifier="five-safes-crate", - rocrate_entity_mod_sparql=sparql, - ) - - -def test_5src_root_data_entity_source_organization_not_entity(): - """\ - Test a Five Safes Crate where the Root Data Entity it does not reference a sourceOrganization. - """ - sparql = SPARQL_PREFIXES + """DELETE { - <./> schema:sourceOrganization ?o - } - INSERT { - <./> schema:sourceOrganization "Investigation of cancer (TRE72 project 81)" - } - WHERE { - <./> schema:sourceOrganization ?o - } - -""" - - do_entity_test( - rocrate_path=ValidROC().five_safes_crate_result, - requirement_severity=Severity.REQUIRED, - expected_validation_result=False, - expected_triggered_requirements=["Five Safes Crate Root Data Entity REQUIRED properties"], - expected_triggered_issues=[ - """The Root Data Entity MUST have a `sourceOrganization` property (as specified by schema.org). - SHOULD link to a Contextual Entity in the RO-Crate Metadata File with a name.""" - ], - profile_identifier="five-safes-crate", - rocrate_entity_mod_sparql=sparql, - ) From a12304594398b1fcc49b6170423e96165ee74797 Mon Sep 17 00:00:00 2001 From: Ettore Murabito Date: Tue, 28 Oct 2025 09:53:43 +0000 Subject: [PATCH 06/13] Made line long shorter --- .../profiles/five-safes-crate/test_5src_1_requesting_agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/profiles/five-safes-crate/test_5src_1_requesting_agent.py b/tests/integration/profiles/five-safes-crate/test_5src_1_requesting_agent.py index a1809fb17..64c8e8991 100644 --- a/tests/integration/profiles/five-safes-crate/test_5src_1_requesting_agent.py +++ b/tests/integration/profiles/five-safes-crate/test_5src_1_requesting_agent.py @@ -95,7 +95,7 @@ def test_5src_agent_memberOf_project_not_in_root(): expected_validation_result=False, expected_triggered_requirements=["Agent Project Intersection"], expected_triggered_issues=[ - """At least one Project referenced by Agent -> memberOf MUST be included in the set of Projects referenced by RootDataEntity -> sourceOrganization.""" + """Projects referenced by Agent -> memberOf MUST intersect Projects referenced by RootDataEntity -> sourceOrganization.""" ], profile_identifier="five-safes-crate", rocrate_entity_mod_sparql=sparql, From e17cececfa9d42cce0d57a997b2955c4cad43567 Mon Sep 17 00:00:00 2001 From: Ettore Murabito Date: Tue, 28 Oct 2025 09:55:29 +0000 Subject: [PATCH 07/13] Made line long shorter 2 --- .../profiles/five-safes-crate/test_5src_1_requesting_agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/profiles/five-safes-crate/test_5src_1_requesting_agent.py b/tests/integration/profiles/five-safes-crate/test_5src_1_requesting_agent.py index 64c8e8991..be1eaeace 100644 --- a/tests/integration/profiles/five-safes-crate/test_5src_1_requesting_agent.py +++ b/tests/integration/profiles/five-safes-crate/test_5src_1_requesting_agent.py @@ -95,7 +95,7 @@ def test_5src_agent_memberOf_project_not_in_root(): expected_validation_result=False, expected_triggered_requirements=["Agent Project Intersection"], expected_triggered_issues=[ - """Projects referenced by Agent -> memberOf MUST intersect Projects referenced by RootDataEntity -> sourceOrganization.""" + """Agent -> memberOf MUST intersect RootDataEntity -> sourceOrganization.""" ], profile_identifier="five-safes-crate", rocrate_entity_mod_sparql=sparql, From c184c6a3280d06c570f792d0a2c1402afb80cb3f Mon Sep 17 00:00:00 2001 From: Ettore Murabito Date: Tue, 28 Oct 2025 10:11:39 +0000 Subject: [PATCH 08/13] Amended inconsistency with names --- .../profiles/five-safes-crate/must/1_requesting_agent.ttl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rocrate_validator/profiles/five-safes-crate/must/1_requesting_agent.ttl b/rocrate_validator/profiles/five-safes-crate/must/1_requesting_agent.ttl index 2cc0d434d..4f3c03d89 100644 --- a/rocrate_validator/profiles/five-safes-crate/must/1_requesting_agent.ttl +++ b/rocrate_validator/profiles/five-safes-crate/must/1_requesting_agent.ttl @@ -78,5 +78,5 @@ five-safes-crate:AgentProjectIntersection } """ ; sh:severity sh:Violation ; - sh:message """At least one Project referenced by Agent -> memberOf MUST be included in the set of Projects referenced by RootDataEntity -> sourceOrganization.""" ; + sh:message """Agent -> memberOf MUST intersect RootDataEntity -> sourceOrganization.""" ; ] . \ No newline at end of file From 5cea22e4e68ab7fc1477acfd707bfd13da71dbca Mon Sep 17 00:00:00 2001 From: Ettore Murabito Date: Tue, 28 Oct 2025 17:51:27 +0000 Subject: [PATCH 09/13] Addresses Eli's comments --- .../should/1_responsible_project.ttl | 2 +- .../ro-crate-metadata.json | 5 ++ .../ro-crate-metadata.json | 5 ++ .../test_5src_1_requesting_agent.py | 4 +- .../test_5src_1_responsible_project.py | 51 ++++++++++++++++++- 5 files changed, 62 insertions(+), 5 deletions(-) diff --git a/rocrate_validator/profiles/five-safes-crate/should/1_responsible_project.ttl b/rocrate_validator/profiles/five-safes-crate/should/1_responsible_project.ttl index 47c1e5fcb..115a4ec12 100644 --- a/rocrate_validator/profiles/five-safes-crate/should/1_responsible_project.ttl +++ b/rocrate_validator/profiles/five-safes-crate/should/1_responsible_project.ttl @@ -56,5 +56,5 @@ five-safes-crate:ResponsibleProjectMemberAndSourceOrganizationIntersection } """ ; sh:severity sh:Warning ; - sh:message """At least one of the organisations that are members of the responsible project SHOULD be included in the Requesting Agent's affiliations.""" ; + sh:message """Responsible Project --> member SHOULD intersect Requesting Agent --> affiliation.""" ; ] . \ No newline at end of file diff --git a/tests/data/crates/valid/five-safes-crate-request/ro-crate-metadata.json b/tests/data/crates/valid/five-safes-crate-request/ro-crate-metadata.json index 9fb0d7bf5..732854019 100644 --- a/tests/data/crates/valid/five-safes-crate-request/ro-crate-metadata.json +++ b/tests/data/crates/valid/five-safes-crate-request/ro-crate-metadata.json @@ -101,6 +101,11 @@ "@type": "Organization", "name": "The University of Manchester" }, + { + "@id": "https://ror.org/01ee9ar58", + "@type": "Organization", + "name": "University of Nottingham" + }, { "@id": "#project-be6ffb55-4f5a-4c14-b60e-47e0951090c70", "@type": "Project", diff --git a/tests/data/crates/valid/five-safes-crate-result/ro-crate-metadata.json b/tests/data/crates/valid/five-safes-crate-result/ro-crate-metadata.json index a6475f63c..f1f48cd99 100644 --- a/tests/data/crates/valid/five-safes-crate-result/ro-crate-metadata.json +++ b/tests/data/crates/valid/five-safes-crate-result/ro-crate-metadata.json @@ -366,6 +366,11 @@ "@type": "Organization", "name": "The University of Manchester" }, + { + "@id": "https://ror.org/01ee9ar58", + "@type": "Organization", + "name": "University of Nottingham" + }, { "@id": "https://gtr.ukri.org/projects?ref=10038961", "@type": "Grant", diff --git a/tests/integration/profiles/five-safes-crate/test_5src_1_requesting_agent.py b/tests/integration/profiles/five-safes-crate/test_5src_1_requesting_agent.py index be1eaeace..dc67387d9 100644 --- a/tests/integration/profiles/five-safes-crate/test_5src_1_requesting_agent.py +++ b/tests/integration/profiles/five-safes-crate/test_5src_1_requesting_agent.py @@ -75,8 +75,8 @@ def test_5src_agent_memberOf_project_not_in_root(): } INSERT { # assign the agent to a new Project that is not referenced by the Root Data Entity - ?agent schema:memberOf <./missing-project> . - <./missing-project> a schema:Project . + ?agent schema:memberOf <#missing-project> . + <#missing-project> a schema:Project . } WHERE { # locate a CreateAction -> agent -> memberOf that currently points to a Project diff --git a/tests/integration/profiles/five-safes-crate/test_5src_1_responsible_project.py b/tests/integration/profiles/five-safes-crate/test_5src_1_responsible_project.py index 6ed1b3a96..db14e4519 100644 --- a/tests/integration/profiles/five-safes-crate/test_5src_1_responsible_project.py +++ b/tests/integration/profiles/five-safes-crate/test_5src_1_responsible_project.py @@ -101,6 +101,53 @@ def test_5src_responsible_project_member_not_organization(): ) +# ---- SHOULD warns tests + + +def test_5src_responsible_project_member_and_agent_affiliation_no_intersection(): + """ + Test a Five Safes Crate where none of the organisations that are members of the + Responsible Project appear in the Requesting Agent's affiliations (violates the + 'Intersection with agent affiliations' SHACL warning). + """ + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + ?agent schema:affiliation ?oldAff . + } + INSERT { + ?agent schema:affiliation <#missing-affiliation> . + <#missing-affiliation> a schema:Organization . + } + WHERE { + ?action a schema:CreateAction ; + schema:agent ?agent . + ?agent a schema:Person ; + schema:memberOf ?project ; + schema:affiliation ?oldAff . + ?project schema:member ?org2 . + + FILTER NOT EXISTS { ?project schema:member <#missing-affiliation> } + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_request, + requirement_severity=Severity.RECOMMENDED, + expected_validation_result=False, + expected_triggered_requirements=[ + "Organizations (members of Responsible Project)" + ], + expected_triggered_issues=[ + "Responsible Project --> member SHOULD intersect Requesting Agent --> affiliation." + ], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + # ---- MAY warns tests @@ -127,7 +174,7 @@ def test_5src_responsible_project_missing_funding_property(): do_entity_test( rocrate_path=ValidROC().five_safes_crate_request, requirement_severity=Severity.OPTIONAL, - expected_validation_result=False, # or True if Info is not treated as failure + expected_validation_result=False, expected_triggered_requirements=["Responsible Project"], expected_triggered_issues=[ "The Responsible Project does not have the property `funding`." @@ -160,7 +207,7 @@ def test_5src_responsible_project_missing_member_property(): do_entity_test( rocrate_path=ValidROC().five_safes_crate_request, requirement_severity=Severity.OPTIONAL, - expected_validation_result=False, # or True if Info is treated as failure + expected_validation_result=False, expected_triggered_requirements=["Responsible Project"], expected_triggered_issues=[ "The Responsible Project does not have the property `member`." From 779022a2a650a6fc130184423619588df699ec93 Mon Sep 17 00:00:00 2001 From: Ettore Murabito Date: Tue, 28 Oct 2025 18:12:29 +0000 Subject: [PATCH 10/13] removed white spaces from blank line. --- .../five-safes-crate/test_5src_1_responsible_project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/profiles/five-safes-crate/test_5src_1_responsible_project.py b/tests/integration/profiles/five-safes-crate/test_5src_1_responsible_project.py index db14e4519..fca1232de 100644 --- a/tests/integration/profiles/five-safes-crate/test_5src_1_responsible_project.py +++ b/tests/integration/profiles/five-safes-crate/test_5src_1_responsible_project.py @@ -127,7 +127,7 @@ def test_5src_responsible_project_member_and_agent_affiliation_no_intersection() schema:memberOf ?project ; schema:affiliation ?oldAff . ?project schema:member ?org2 . - + FILTER NOT EXISTS { ?project schema:member <#missing-affiliation> } } """ From 964ff31fc4548b24147443e4d9174f850033b4de Mon Sep 17 00:00:00 2001 From: Ettore Murabito Date: Wed, 29 Oct 2025 13:18:16 +0000 Subject: [PATCH 11/13] Addressed Eli's and Doug's comments. --- .../must/1_requesting_agent.ttl | 2 +- .../should/1_responsible_project.ttl | 2 +- .../test_5src_1_requesting_agent.py | 5 +- .../test_5src_1_responsible_project.py | 9 ++-- .../five-safes-crate/test_5src_funding.py | 47 ------------------- 5 files changed, 11 insertions(+), 54 deletions(-) delete mode 100644 tests/integration/profiles/five-safes-crate/test_5src_funding.py diff --git a/rocrate_validator/profiles/five-safes-crate/must/1_requesting_agent.ttl b/rocrate_validator/profiles/five-safes-crate/must/1_requesting_agent.ttl index 4f3c03d89..2cc0d434d 100644 --- a/rocrate_validator/profiles/five-safes-crate/must/1_requesting_agent.ttl +++ b/rocrate_validator/profiles/five-safes-crate/must/1_requesting_agent.ttl @@ -78,5 +78,5 @@ five-safes-crate:AgentProjectIntersection } """ ; sh:severity sh:Violation ; - sh:message """Agent -> memberOf MUST intersect RootDataEntity -> sourceOrganization.""" ; + sh:message """At least one Project referenced by Agent -> memberOf MUST be included in the set of Projects referenced by RootDataEntity -> sourceOrganization.""" ; ] . \ No newline at end of file diff --git a/rocrate_validator/profiles/five-safes-crate/should/1_responsible_project.ttl b/rocrate_validator/profiles/five-safes-crate/should/1_responsible_project.ttl index 115a4ec12..28d1c1eef 100644 --- a/rocrate_validator/profiles/five-safes-crate/should/1_responsible_project.ttl +++ b/rocrate_validator/profiles/five-safes-crate/should/1_responsible_project.ttl @@ -56,5 +56,5 @@ five-safes-crate:ResponsibleProjectMemberAndSourceOrganizationIntersection } """ ; sh:severity sh:Warning ; - sh:message """Responsible Project --> member SHOULD intersect Requesting Agent --> affiliation.""" ; + sh:message """At least one of the organisations that are members of the responsible project SHOULD be included in the Requesting Agent's affiliations, if such properties exist.""" ; ] . \ No newline at end of file diff --git a/tests/integration/profiles/five-safes-crate/test_5src_1_requesting_agent.py b/tests/integration/profiles/five-safes-crate/test_5src_1_requesting_agent.py index dc67387d9..9601f6fa3 100644 --- a/tests/integration/profiles/five-safes-crate/test_5src_1_requesting_agent.py +++ b/tests/integration/profiles/five-safes-crate/test_5src_1_requesting_agent.py @@ -95,7 +95,10 @@ def test_5src_agent_memberOf_project_not_in_root(): expected_validation_result=False, expected_triggered_requirements=["Agent Project Intersection"], expected_triggered_issues=[ - """Agent -> memberOf MUST intersect RootDataEntity -> sourceOrganization.""" + ( + "At least one Project referenced by Agent -> memberOf MUST be included " + "in the set of Projects referenced by RootDataEntity -> sourceOrganization." + ) ], profile_identifier="five-safes-crate", rocrate_entity_mod_sparql=sparql, diff --git a/tests/integration/profiles/five-safes-crate/test_5src_1_responsible_project.py b/tests/integration/profiles/five-safes-crate/test_5src_1_responsible_project.py index fca1232de..0d98ca3b0 100644 --- a/tests/integration/profiles/five-safes-crate/test_5src_1_responsible_project.py +++ b/tests/integration/profiles/five-safes-crate/test_5src_1_responsible_project.py @@ -126,9 +126,7 @@ def test_5src_responsible_project_member_and_agent_affiliation_no_intersection() ?agent a schema:Person ; schema:memberOf ?project ; schema:affiliation ?oldAff . - ?project schema:member ?org2 . - - FILTER NOT EXISTS { ?project schema:member <#missing-affiliation> } + ?project schema:member ?oldAff . } """ ) @@ -141,7 +139,10 @@ def test_5src_responsible_project_member_and_agent_affiliation_no_intersection() "Organizations (members of Responsible Project)" ], expected_triggered_issues=[ - "Responsible Project --> member SHOULD intersect Requesting Agent --> affiliation." + ( + "At least one of the organisations that are members of the responsible project SHOULD " + "be included in the Requesting Agent's affiliations, if such properties exist." + ) ], profile_identifier="five-safes-crate", rocrate_entity_mod_sparql=sparql, diff --git a/tests/integration/profiles/five-safes-crate/test_5src_funding.py b/tests/integration/profiles/five-safes-crate/test_5src_funding.py deleted file mode 100644 index 8543a69d3..000000000 --- a/tests/integration/profiles/five-safes-crate/test_5src_funding.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) 2024-2025 CRS4 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import logging - -from rocrate_validator.models import Severity -from tests.ro_crates import ValidROC -from tests.shared import do_entity_test, SPARQL_PREFIXES - -# set up logging -logger = logging.getLogger(__name__) - - -def test_5src_funding_project_no_name(): - """\ - Test a Five Safes Crate where the funding Project does not have a name. - """ - sparql = ( - SPARQL_PREFIXES - + """DELETE WHERE { - <#project-be6ffb55-4f5a-4c14-b60e-47e0951090c70> schema:name "Investigation of cancer (TRE72 project 81)" -} -""" - ) - - do_entity_test( - rocrate_path=ValidROC().five_safes_crate_result, - requirement_severity=Severity.REQUIRED, - expected_validation_result=False, - expected_triggered_requirements=["Funding body Project"], - expected_triggered_issues=[ - "The Project Entity MUST have a `name` property (as specified by schema.org)" - ], - profile_identifier="five-safes-crate", - rocrate_entity_mod_sparql=sparql, - ) From 72077b75f4edc1f7bb5e7e1faec46f47291b44cb Mon Sep 17 00:00:00 2001 From: Ettore Murabito Date: Wed, 29 Oct 2025 19:13:30 +0000 Subject: [PATCH 12/13] Addressed class of members of reponsible project. --- .../five-safes-crate/must/1_responsible_project.ttl | 7 +++++-- .../five-safes-crate/test_5src_1_responsible_project.py | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/rocrate_validator/profiles/five-safes-crate/must/1_responsible_project.ttl b/rocrate_validator/profiles/five-safes-crate/must/1_responsible_project.ttl index e9668fd07..c06c873ea 100644 --- a/rocrate_validator/profiles/five-safes-crate/must/1_responsible_project.ttl +++ b/rocrate_validator/profiles/five-safes-crate/must/1_responsible_project.ttl @@ -50,7 +50,10 @@ five-safes-crate:ResponsibleProject a sh:PropertyShape ; sh:name "member" ; sh:path schema:member; - sh:class schema:Organization ; + sh:or ( + [ sh:class schema:Organization ] + [ sh:class schema:Person ] + ) ; sh:severity sh:Violation ; - sh:message """The property 'member' of the Responsible Project MUST be of type schema:Organization.""" ; + sh:message """The property 'member' of the Responsible Project MUST be of type schema:Organization or schema:Person.""" ; ] . \ No newline at end of file diff --git a/tests/integration/profiles/five-safes-crate/test_5src_1_responsible_project.py b/tests/integration/profiles/five-safes-crate/test_5src_1_responsible_project.py index 0d98ca3b0..fe463ed10 100644 --- a/tests/integration/profiles/five-safes-crate/test_5src_1_responsible_project.py +++ b/tests/integration/profiles/five-safes-crate/test_5src_1_responsible_project.py @@ -94,7 +94,7 @@ def test_5src_responsible_project_member_not_organization(): expected_validation_result=False, expected_triggered_requirements=["Responsible Project"], expected_triggered_issues=[ - "The property 'member' of the Responsible Project MUST be of type schema:Organization." + "The property 'member' of the Responsible Project MUST be of type schema:Organization or schema:Person." ], profile_identifier="five-safes-crate", rocrate_entity_mod_sparql=sparql, From 1e5d8c66b3b951338103553762b14c94fd849fe0 Mon Sep 17 00:00:00 2001 From: Ettore Murabito Date: Wed, 29 Oct 2025 19:15:34 +0000 Subject: [PATCH 13/13] Addressed class of members of reponsible project 2. --- .../five-safes-crate/test_5src_1_responsible_project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/profiles/five-safes-crate/test_5src_1_responsible_project.py b/tests/integration/profiles/five-safes-crate/test_5src_1_responsible_project.py index fe463ed10..daa8d90ac 100644 --- a/tests/integration/profiles/five-safes-crate/test_5src_1_responsible_project.py +++ b/tests/integration/profiles/five-safes-crate/test_5src_1_responsible_project.py @@ -76,7 +76,7 @@ def test_5src_responsible_project_member_not_organization(): ?project schema:member ?org . } INSERT { - ?project schema:member "Not an organization (literal replacement)" . + ?project schema:member "Not organization or person (literal replacement)" . } WHERE { ?action a schema:CreateAction ;