Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
852fb23
started work on isa-ro-crate profile
floWetzels Nov 7, 2024
bd1defa
added test arc
floWetzels Nov 7, 2024
7709b57
mystery commit - remove?
elichad Mar 18, 2025
7a87cc5
add ontology labels
elichad Nov 5, 2025
221e210
add note on data entity
elichad Nov 5, 2025
d6e0f3b
remove half-baked checks
elichad Nov 5, 2025
cc6059a
add sh prefix to ontology
elichad Nov 5, 2025
003bc74
poach sparql_mod testing setup from https://github.com/eScienceLab/ro…
elichad Nov 5, 2025
fe1f777
add initial tests
elichad Nov 5, 2025
30b42af
swap out example crate for a simpler one
elichad Nov 7, 2025
3204bd1
fix query syntax
elichad Nov 7, 2025
11e0176
mark valid test as xfail until bug is resolved
elichad Nov 7, 2025
d930c8f
WIP adding checks for Study
elichad Nov 25, 2025
36efe64
continue making some demonstrative checks; add test crates; set up un…
elichad Nov 27, 2025
17299b6
add several LabProcess checks for isa profile
HLWeil Dec 2, 2025
7f689bb
add isa study and process test crates
HLWeil Dec 2, 2025
fcf3080
add isa process and study tests and some fixes for checks
HLWeil Dec 2, 2025
9b87db2
set non-working isa study test to pending
HLWeil Dec 2, 2025
60fe759
ISA profile - Investigation and Assay checks
floWetzels Dec 4, 2025
4a7342e
ISA profile - updated test crates
floWetzels Dec 4, 2025
e2d073c
ISA profile - updated tests
floWetzels Dec 4, 2025
31ebc36
add checks about process objects and results having the correct type
HLWeil Dec 9, 2025
daebea1
refactor(core): :recycle: rename flag to `disable_inherited_profiles_…
kikkomep Jan 9, 2026
c8ad7b9
refactor(core): :building_construction: skip inherited profile event …
kikkomep Jan 9, 2026
cd03486
docs(core): :bulb: improve code documentation with additional comments
kikkomep Jan 9, 2026
cd3af57
docs(core): :bulb: clarify the proper usage of the `enable_profile_in…
kikkomep Jan 9, 2026
d6f9192
fix(cli): :building_construction: Make the `disable_profile_inheritan…
kikkomep Jan 9, 2026
f6e15b7
fix(core): :adhesive_bandage: remove inherited checks from stats when…
kikkomep Jan 9, 2026
4a27eb9
fix(core): :adhesive_bandage: exclude skipped checks from stats
kikkomep Jan 9, 2026
9141a72
fix(core): :bug: enable skipping of SHACL checks
kikkomep Jan 9, 2026
efcad8b
fix(core): :bug: enable skipping of Python checks
kikkomep Jan 9, 2026
04988b1
fix(core): :bug: ensure skip_checks is defined before use
kikkomep Jan 9, 2026
38f22ff
fix(cli): :bug: wrong param name
kikkomep Jan 9, 2026
c8e7de7
refactor(core): :wrench: set `disable_inherited_profiles_issue_report…
kikkomep Jan 9, 2026
471745b
fix(core): :adhesive_bandage: ensure skipped checks are reported prop…
kikkomep Jan 9, 2026
103c647
test(core): :white_check_mark: test behaviour of `disable_inherited_p…
kikkomep Jan 9, 2026
a5d9d0a
test(core): :white_check_mark: test behaviour of skip check functiona…
kikkomep Jan 9, 2026
0c722b5
ISA profile: added protocol checks and updated process checks
floWetzels Jan 20, 2026
807ed77
ISA profile: added tests for protocol checks and updated process tests
floWetzels Jan 20, 2026
0bff8d9
ISA profile: added should checks for investigation with tests
floWetzels Jan 20, 2026
3b17e8b
ISA profile: added checks for study should properties and tests
floWetzels Jan 20, 2026
ed0ad77
ISA profile: added should checks (no tests yet)
floWetzels Jan 21, 2026
8615d99
ISA profile: added test using sparql modification of valid ro-crate
floWetzels Jan 21, 2026
7d0d959
fixed merge conflicts
floWetzels Jan 21, 2026
9f01d29
ISA profile: Small fix in assay checks and added tests for Assay shoulds
floWetzels Jan 22, 2026
727b6f0
ISA profile: added checks for Sample, Data, and Person
floWetzels Jan 22, 2026
d74bd21
ISA profile: added tests for Sample, Data, and Personn
floWetzels Jan 22, 2026
7b4a2dc
ISA profile: checks and tests for article, comment, defined term
floWetzels Jan 29, 2026
bf4f621
ISA profile: checks and tests for PropertyValue (base attributes)
floWetzels Jan 30, 2026
8b6f7f7
#146 Wrap sys.stdout in nullcontext to avoid close on ctx exit
pmslavin Feb 5, 2026
d62e214
ISA profile: new definitions of Assay and Study classes, added subcla…
floWetzels Feb 10, 2026
02947b3
ISA profile: cleanup of tests for investigation
floWetzels Feb 10, 2026
2a82b7e
ISA profile: fixed check for linked investigation in study and assay
floWetzels Feb 10, 2026
161cde6
ISA profile: cleanup of study and assay tests
floWetzels Feb 10, 2026
6e3e9a3
ISA profile: modified test with valid ro-crate to use severity recomm…
floWetzels Feb 10, 2026
f1c6860
formatting
floWetzels Feb 10, 2026
40fe1e8
formatting
floWetzels Feb 10, 2026
cd549aa
ISA profile: test cleanup for process and protocol
floWetzels Feb 11, 2026
ffecee2
ISA profile: removed hardcoded invalid test crates
floWetzels Feb 11, 2026
b4d3136
formatting
floWetzels Feb 11, 2026
5f9ca89
spelling in profiles
floWetzels Feb 11, 2026
81e50dd
ISA profile: updated checks for root link of study and assay with cor…
floWetzels Feb 12, 2026
4bdc10b
ISA profile: changed test case for valid isa-ro-crate to severity req…
floWetzels Feb 12, 2026
fde97be
build(poetry): :arrow_up: allow using the latest PySHACL version
kikkomep Feb 18, 2026
9944712
build(poetry): :wrench: update `poetry.lock` file
kikkomep Feb 18, 2026
c6da40a
chore: :mute: filter `rdflib` JSON-LD deprecation warnings
kikkomep Feb 18, 2026
a0f5e56
chore(poetry): :art: fix missing whitespaces
kikkomep Feb 18, 2026
dbdfae5
fix(cli): :adhesive_bandage: fix `rich` deprecation warning
kikkomep Feb 18, 2026
e01ee92
Merge pull request #147 from pmslavin/146-atexit-writes-to-closed-file
kikkomep Feb 18, 2026
0c58ed6
Merge pull request #141 from kikkomep/fix/reporting-inherited-profile…
kikkomep Feb 18, 2026
fc68cca
Merge pull request #151 from kikkomep/fix/rdflib-warnings
kikkomep Feb 18, 2026
4f54135
docs: :memo: initialise `CHANGELOG.md`
kikkomep Feb 18, 2026
b56a65f
chore: :arrow_up: bump version number to 0.8.1
kikkomep Feb 18, 2026
2251168
docs: :memo: update CHANGELOG (v0.8.1)
kikkomep Feb 18, 2026
e66a363
ISA profile: updated copyright in all files related to the profile
floWetzels Feb 19, 2026
fbdacb5
ISA profile: fixed some issues and removed leftover code
floWetzels Feb 19, 2026
91e417d
updated CITATION.cff
floWetzels Feb 19, 2026
dbf2eef
ISA profile: updates description and message for dateCreated in inves…
floWetzels Feb 19, 2026
dea328d
ISA profile: updated copyright in all files related to the profile
floWetzels Feb 19, 2026
01a5c5c
ISA profile: small change in copyright for profile.ttl
floWetzels Feb 19, 2026
70ae91d
ISA profile: updated copyright in all files related to the profile
floWetzels Feb 19, 2026
7277042
Merge pull request #149 from nfdi4plants/isa-profile-setup
simleo Feb 19, 2026
6cde639
Merge remote-tracking branch 'upstream/develop' into develop-testmerge
douglowe Feb 23, 2026
168fac2
5S crate integration test, rename inherited profiles flag
douglowe Feb 23, 2026
5e40c60
5S crate integration test, rename inherited profiles flag (2)
douglowe Feb 23, 2026
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
753 changes: 753 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ authors:
- family-names: Bauer
given-names: Daniel
orcid: https://orcid.org/0000-0001-9447-460X
- family-names: "Wetzels"
given-names: "Florian"
orcid: https://orcid.org/0000-0002-5526-7138
- family-names: "Weil"
given-names: "Heinrich Lukas"
orcid: https://orcid.org/0000-0003-1945-6342
repository-code: "https://github.com/crs4/rocrate-validator"
url: "https://github.com/crs4/rocrate-validator"
keywords:
Expand Down
962 changes: 645 additions & 317 deletions poetry.lock

Large diffs are not rendered by default.

14 changes: 11 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "roc-validator"
version = "0.8.0"
version = "0.8.1"
description = "A Python package to validate RO-Crates"
authors = [
"Marco Enrico Piras <kikkomep@crs4.it>",
Expand Down Expand Up @@ -60,7 +60,7 @@ include = [
[tool.poetry.dependencies]
python = ">=3.10,<4.0"
rdflib = ">=7.1,<8.0"
pyshacl = ">=0.26,<0.31"
pyshacl = ">=0.26"
click = ">=8.2,<9.0"
rich = ">=13.9,<14.0"
toml = ">=0.10.2,<1.0"
Expand Down Expand Up @@ -106,9 +106,17 @@ skip_dirs = [".git", ".github", ".vscode"]

[tool.pytest.ini_options]
testpaths = ["tests"]
filterwarnings = [
# Ignore deprecation warnings from rdflib's JSON-LD parser,
# used internally by pyshacl. These warnings are unrelated to
# our code and currently noisy.
# See: https://github.com/RDFLib/rdflib/issues/3064
# Fix in progress: https://github.com/RDFLib/rdflib/issues/3302
"ignore::DeprecationWarning:rdflib.plugins.parsers.jsonld",
]

[tool.typos.files]
extend-exclude = ["tests/data","docs/diagrams","*.json","*.html","*__init__.py"]
extend-exclude = ["tests/data", "docs/diagrams", "*.json", "*.html", "*__init__.py"]

[tool.typos.default.extend-words]
TRE = "TRE"
8 changes: 7 additions & 1 deletion pytest.ini
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,10 @@
; log_cli=true
; log_level=DEBUG
addopts = -n auto
; filterwarnings =
filterwarnings =
# Ignore deprecation warnings from rdflib's JSON-LD parser,
# used internally by pyshacl. These warnings are unrelated to
# our code and currently noisy.
# See: https://github.com/RDFLib/rdflib/issues/3064
# Fix in progress: https://github.com/RDFLib/rdflib/issues/3302
ignore::DeprecationWarning:rdflib.plugins.parsers.jsonld
5 changes: 3 additions & 2 deletions rocrate_validator/cli/commands/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import os
import sys
from contextlib import nullcontext
from pathlib import Path
from typing import Optional

Expand Down Expand Up @@ -276,7 +277,7 @@ def validate(ctx,
"profile_identifier": profile_identifier,
"requirement_severity": requirement_severity,
"requirement_severity_only": requirement_severity_only,
"enable_profile_inheritance": not disable_profile_inheritance,
"disable_inherited_profiles_issue_reporting": disable_profile_inheritance,
"rocrate_uri": rocrate_uri,
"rocrate_relative_root_path": relative_root_path,
"abort_on_first": fail_fast,
Expand Down Expand Up @@ -472,7 +473,7 @@ def validate(ctx,
console.print(f"\n{' '*2}📋 [bold]The validation report in JSON format: [/bold]\n")

# Generate the JSON output and write it to the specified output file or to stdout
with open(output_file, "w", encoding="utf-8") if output_file else sys.stdout as f:
with open(output_file, "w", encoding="utf-8") if output_file else nullcontext(sys.stdout) as f:
out = Console(width=output_line_width, file=f)
out.register_formatter(JSONOutputFormatter())
out.print(results)
Expand Down
2 changes: 1 addition & 1 deletion rocrate_validator/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@


@click.group(invoke_without_command=True)
@click.rich_config(help_config=click.RichHelpConfiguration(use_rich_markup=True))
@click.rich_config(help_config=click.RichHelpConfiguration(text_markup="rich"))
@click.option(
'--debug',
is_flag=True,
Expand Down
67 changes: 52 additions & 15 deletions rocrate_validator/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1007,26 +1007,43 @@ def _do_validate_(self, context: ValidationContext) -> bool:

logger.debug("Running %s checks for Requirement '%s'", len(self._checks), self.name)
all_passed = True
for check in [_ for _ in self._checks
if not context.settings.skip_checks
or _.identifier not in context.settings.skip_checks]:

checks_to_perform = [
_ for _ in self._checks
if not context.settings.skip_checks
or _.identifier not in context.settings.skip_checks
]
for check in checks_to_perform:
try:
if check.overridden and not check.requirement.profile.identifier == context.profile_identifier:
logger.debug("Skipping check '%s' because overridden by '%r'",
check.identifier, [_.identifier for _ in check.overridden_by])
continue
context.validator.notify(RequirementCheckValidationEvent(
EventType.REQUIREMENT_CHECK_VALIDATION_START, check))
# Determine whether to skip event notification for inherited profiles
skip_event_notify = False
if check.requirement.profile.identifier != context.profile_identifier and \
context.settings.disable_inherited_profiles_issue_reporting:
logger.debug("Inherited profiles reporting disabled. "
"Skipping requirement %s as it belongs to an inherited profile %s",
check.requirement.identifier, check.requirement.profile.identifier)
skip_event_notify = True
# Notify the start of the check execution if not skip_event_notify is set to True
if not skip_event_notify:
context.validator.notify(RequirementCheckValidationEvent(
EventType.REQUIREMENT_CHECK_VALIDATION_START, check))
# Execute the check
check_result = check.execute_check(context)
logger.debug("Result of check %s: %s", check.identifier, check_result)
context.result._add_executed_check(check, check_result)
context.validator.notify(RequirementCheckValidationEvent(
EventType.REQUIREMENT_CHECK_VALIDATION_END, check, validation_result=check_result))
# Notify the end of the check execution if not skip_event_notify is set to True
if not skip_event_notify:
context.validator.notify(RequirementCheckValidationEvent(
EventType.REQUIREMENT_CHECK_VALIDATION_END, check, validation_result=check_result))
logger.debug("Ran check '%s'. Got result %s", check.identifier, check_result)
# Ensure the check result is a boolean
if not isinstance(check_result, bool):
logger.warning("Ignoring the check %s as it returned the value %r instead of a boolean", check.name)
raise RuntimeError(f"Ignoring invalid result from check {check.name}")
# Aggregate the check result
all_passed = all_passed and check_result
if not all_passed and context.fail_fast:
break
Expand All @@ -1040,7 +1057,8 @@ def _do_validate_(self, context: ValidationContext) -> bool:
logger.warning("Consider reporting this as a bug.")
if logger.isEnabledFor(logging.DEBUG):
logger.exception(e)

skipped_checks = set(self._checks) - set(checks_to_perform)
context.result.skipped_checks.update(skipped_checks)
logger.debug("Checks for Requirement '%s' completed. Checks passed? %s", self.name, all_passed)
return all_passed

Expand Down Expand Up @@ -1625,7 +1643,7 @@ def __initialise__(cls, validation_settings: ValidationSettings):
profiles = [profile]

# add inherited profiles if enabled
if validation_settings.enable_profile_inheritance:
if not validation_settings.disable_inherited_profiles_issue_reporting:
profiles.extend(profile.inherited_profiles)
logger.debug("Inherited profiles: %r", profile.inherited_profiles)

Expand Down Expand Up @@ -1656,9 +1674,20 @@ def __initialise__(cls, validation_settings: ValidationSettings):
if severity < severity_validation:
continue
# count the checks
requirement_checks = [_ for _ in requirement.get_checks_by_level(LevelCollection.get(severity.name))
if not _.overridden or
_.requirement.profile.identifier == target_profile_identifier]
requirement_checks = [
_
for _ in requirement.get_checks_by_level(
LevelCollection.get(severity.name)
)
if (
not validation_settings.skip_checks
or _.identifier not in validation_settings.skip_checks
)
and (
not _.overridden
or _.requirement.profile.identifier == target_profile_identifier
)
]
num_checks = len(requirement_checks)
requirement_checks_count += num_checks
if num_checks > 0:
Expand Down Expand Up @@ -2325,12 +2354,20 @@ class ValidationSettings:
#: The profile identifier to validate against
profile_identifier: str = DEFAULT_PROFILE_IDENTIFIER
#: Flag to enable profile inheritance
# Use the `enable_profile_inheritance` flag with caution: disable inheritance only if the
# target validation profile is fully self-contained and does not rely on definitions
# from inherited profiles (e.g., entities defined upstream). For modularization
# purposes, some base entities and properties are defined in the base RO-Crate
# profile and are intentionally not redefined in specialized profiles; they are
# required for validations targeting those specializations and therefore cannot be skipped.
# Nevertheless, the validator can still suppress issue reporting for checks defined
# in inherited profiles by setting disable_inherited_profiles_issue_reporting to `True`.
enable_profile_inheritance: bool = True
# Validation settings
#: Flag to abort on first error
abort_on_first: Optional[bool] = False
#: Flag to disable inherited profiles reporting
disable_inherited_profiles_reporting: bool = False
#: Flag to disable reporting of issues related to inherited profiles
disable_inherited_profiles_issue_reporting: bool = False
#: Flag to disable remote crate download
disable_remote_crate_download: bool = True
# Requirement settings
Expand Down
136 changes: 136 additions & 0 deletions rocrate_validator/profiles/isa-ro-crate/0_investigation.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# Copyright (c) 2026 DataPLANT
# Copyright (c) 2026 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.
# 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: <https://github.com/crs4/rocrate-validator/profiles/ro-crate/> .
@prefix isa-ro-crate: <https://github.com/crs4/rocrate-validator/profiles/isa-ro-crate/> .
@prefix bioschemas: <https://bioschemas.org/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix schema: <http://schema.org/> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix validator: <https://github.com/crs4/rocrate-validator/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

isa-ro-crate:RootDataEntityMustBeInvestigation a sh:NodeShape ;
sh:name "Root Data Entity must be Investigation" ;
sh:description "The root data entity must follow the investigation profile" ;
sh:targetClass ro-crate:RootDataEntity ;
sh:property [
a sh:PropertyShape ;
sh:path schema:additionalType ;
sh:minCount 1 ;
sh:hasValue "Investigation";
sh:description "Check if the root data entity is specified as an investigation through additionalType" ;
sh:message "The root data entity must have additionalType of `Investigation`" ;
sh:severity sh:Violation ;
] ;
sh:property [
a sh:PropertyShape ;
sh:path schema:identifier ;
sh:minCount 1 ;
sh:datatype xsd:string ;
sh:not [
sh:hasValue ""
] ;
sh:description "Check if the root data entity (investigation) has an identifier" ;
sh:message "The root data entity must have a non-empty identifier" ;
sh:severity sh:Violation ;
] ;
# sh:property [
# a sh:PropertyShape ;
# sh:path schema:name ;
# sh:minCount 1 ;
# sh:not [
# sh:hasValue ""
# ] ;
# sh:description "Check if the root data entity (investigation) has a name" ;
# sh:message "The root data entity must have a non-empty name" ;
# sh:severity sh:Violation ;
# ] ;
# sh:property [
# a sh:PropertyShape ;
# sh:path schema:description ;
# sh:minCount 1 ;
# sh:not [
# sh:hasValue ""
# ] ;
# sh:description "Check if the root data entity (investigation) has a description" ;
# sh:message "The root data entity must have a non-empty description" ;
# sh:severity sh:Violation ;
# ] ;
# sh:property [
# a sh:PropertyShape ;
# sh:path schema:license ;
# sh:minCount 1 ;
# sh:description "Check if the root data entity (investigation) specifies a license" ;
# sh:message "The root data entity must specify a license" ;
# sh:severity sh:Violation ;
# ] ;
# sh:property [
# a sh:PropertyShape ;
# sh:path schema:datePublished ;
# sh:minCount 1 ;
# sh:nodeKind sh:Literal ;
# sh:pattern "^([\\+-]?\\d{4})((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))|W([0-4]\\d|5[0-2])(-?[1-7])|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)?[0-5]\\d)?|24:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)$" ;
# sh:description "Check if the root data entity (investigation) has a datePublished" ;
# sh:message "The root data entity must have a datePublished" ;
# sh:severity sh:Violation ;
# ] ;
.

isa-ro-crate:InvestigationShouldHaveCreator a sh:NodeShape ;
sh:name "Investigation SHOULD have creator" ;
sh:description "An Investigation SHOULD have a creator" ;
sh:targetClass ro-crate:RootDataEntity ;
sh:property [
a sh:PropertyShape ;
sh:path schema:creator ;
sh:minCount 1 ;
sh:description "Check that investigation does have at least one creator" ;
sh:message "Investigation entity SHOULD have a creator" ;
sh:severity sh:Warning ;
] ;
sh:property [
a sh:PropertyShape ;
sh:path schema:creator ;
sh:class schema:Person ;
sh:description "Check that if investigation does have at least one creator, it MUST be of type Person" ;
sh:message "Investigation creator MUST be of type Person" ;
sh:severity sh:Violation ;
]
.

isa-ro-crate:InvestigationShouldHaveDateCreated a sh:NodeShape ;
sh:name "Investigation SHOULD have dateCreated" ;
sh:description "An Investigation SHOULD have a dateCreated" ;
sh:targetClass ro-crate:RootDataEntity ;
sh:property [
a sh:PropertyShape ;
sh:path schema:dateCreated ;
sh:minCount 1 ;
sh:description "Check that investigation does have at least one dateCreated" ;
sh:message "Investigation entity SHOULD have a dateCreated" ;
sh:severity sh:Warning ;
] ;
sh:property [
a sh:PropertyShape ;
sh:path schema:dateCreated ;
sh:nodeKind sh:Literal ;
sh:pattern "^([\\+-]?\\d{4})((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))|W([0-4]\\d|5[0-2])(-?[1-7])|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)?[0-5]\\d)?|24:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)$" ;
sh:description "Check that if investigation does have at least one dateCreated, it MUST be a valid ISO 8601 date." ;
sh:message "Investigation dateCreated MUST be a valid ISO 8601 date" ;
sh:severity sh:Violation ;
]
.
Loading