From 4d0402a56aa2f6d6d342b753053cf4fd7d7a405d Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Sat, 13 Apr 2024 13:12:31 -0700 Subject: [PATCH 01/52] add metaschema and code to validate it. --- .../metaschema/associations.schema.json | 44 ++++++++++ src/schema/metaschema/checks.schema.json | 31 +++++++ src/schema/metaschema/formats.schema.json | 23 ++++++ src/schema/metaschema/objects.schema.yaml | 80 +++++++++++++++++++ src/schema/metaschema/raw.schema.json | 31 +++++++ src/validate_schema.py | 76 ++++++++++++++++++ 6 files changed, 285 insertions(+) create mode 100644 src/schema/metaschema/associations.schema.json create mode 100644 src/schema/metaschema/checks.schema.json create mode 100644 src/schema/metaschema/formats.schema.json create mode 100644 src/schema/metaschema/objects.schema.yaml create mode 100644 src/schema/metaschema/raw.schema.json create mode 100644 src/validate_schema.py diff --git a/src/schema/metaschema/associations.schema.json b/src/schema/metaschema/associations.schema.json new file mode 100644 index 0000000000..35b9a67709 --- /dev/null +++ b/src/schema/metaschema/associations.schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9]+$": { + "type": "object", + "properties": { + "selectors": { + "type": "array", + "items": { + "type": "string" + } + }, + "target": { + "type": "object", + "properties": { + "suffix": { + "type": "string" + }, + "extension": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + }, + "required": ["extension"] + }, + "inherit": { + "type": "boolean" + } + }, + "required": ["selectors", "target", "inherit"], + "additionalProperties": false + } + } +} diff --git a/src/schema/metaschema/checks.schema.json b/src/schema/metaschema/checks.schema.json new file mode 100644 index 0000000000..48707011f8 --- /dev/null +++ b/src/schema/metaschema/checks.schema.json @@ -0,0 +1,31 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "description": "Schema for validating modalities supported by BIDS", + "type": "object", + "patternProperties": { + "^[a-zA-Z_][a-zA-Z0-9_]*$": { + "type": "object", + "properties": { + "issue": { + "type": "object", + "properties": { + "code": {"type": "string"}, + "message": {"type": "string"}, + "level": {"enum": ["error", "warning"]} + }, + "required": ["code", "message", "level"], + "additionalProperties": false + }, + "selectors": { + "type": "array", + "item": {"type": "string"} + }, + "checks": { + "type": "array", + "item": {"type": "string"} + } + }, + "required": ["selectors", "checks"] + } + } +} \ No newline at end of file diff --git a/src/schema/metaschema/formats.schema.json b/src/schema/metaschema/formats.schema.json new file mode 100644 index 0000000000..e15d4a16bf --- /dev/null +++ b/src/schema/metaschema/formats.schema.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "description": "Schema for validating various entity patterns and metadata types with specific regular expressions", + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9_]+$": { + "type": "object", + "properties": { + "display_name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "pattern": { + "type": "string" + } + }, + "required": ["display_name", "description", "pattern"] + } + }, + "additionalProperties": false +} \ No newline at end of file diff --git a/src/schema/metaschema/objects.schema.yaml b/src/schema/metaschema/objects.schema.yaml new file mode 100644 index 0000000000..558489f19d --- /dev/null +++ b/src/schema/metaschema/objects.schema.yaml @@ -0,0 +1,80 @@ +"objects/columns": + required: + - name + - display_name + optional: + - description + - type + - format + - unit + - enum + - anyOf + - pattern + - maximum + - minimum + +"objects/common_principles": + required: + - display_name + - description + + +"objects/datatypes": + required: + - value + - display_name + - description + +"objects/entities": + required: + - name + - display_name + - description + - type + - format + optional: + - enum + +"objects/enums": + optional: + - tags + - enum + - display_name + - description + - type + - value + +"objects/extensions": + required: + - value + - display_name + - description + +"objects/files": + required: + - display_name + - file_type + - description + +"objects/formats": + required: + - pattern + - display_name + - description + +"objects/modalities": + required: + - display_name + - description + +"objects/suffixes": + required: + - value + - display_name + - description + optional: + - unit + - minValue + - maxValue + - anyOf + diff --git a/src/schema/metaschema/raw.schema.json b/src/schema/metaschema/raw.schema.json new file mode 100644 index 0000000000..890114dcd1 --- /dev/null +++ b/src/schema/metaschema/raw.schema.json @@ -0,0 +1,31 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "description": "Schema for validating modalities supported by BIDS", + "type": "object", + "patternProperties": { + "^[a-zA-Z_][a-zA-Z0-9_]*$": { + "type": "object", + "properties": { + "suffixes": {"type": "array", "items": {"type": "string"}}, + "extensions": {"type": "array", "items": {"pattern": "^.[a-zA-Z_.*]*(/)?$"}}, + "entities": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9_]*$": { + "anyOf": [ + {"enum": ["required", "optional"]}, + {"type": "object"} + ] + + } + } + }, + "datatypes": { + "type": "array", + "items": {"type": "string"} + } + } + }, + "additionalProperties": false + } +} \ No newline at end of file diff --git a/src/validate_schema.py b/src/validate_schema.py new file mode 100644 index 0000000000..c3eee154d9 --- /dev/null +++ b/src/validate_schema.py @@ -0,0 +1,76 @@ +import glob +import json +from pathlib import Path + +import jsonschema +import yaml +from jsonschema import Draft7Validator + +objects_schema_path = Path(__file__).resolve().parent / "schema" / "metaschema" / "objects.schema.yaml" +schema_path = Path(__file__).resolve().parent / "schema" + +with open(objects_schema_path, "r") as f: + objects_schema = yaml.safe_load(f) + +for rel_path, spec in objects_schema.items(): + + with open(schema_path / f"{rel_path}.yaml", "r") as f: + instance = yaml.safe_load(f) + + # Validate keys + for key, fields in instance.items(): + required_fields = spec.get("required", []) + all_fields = required_fields + spec.get("optional", []) + + # Check if all required fields are present + missing_required = [field for field in required_fields if field not in fields] + if missing_required: + raise AssertionError(f"Missing required fields {missing_required} in {key}") + + # Check if all fields are valid (either required or optional) + invalid_fields = [field for field in fields if field not in all_fields] + if invalid_fields: + raise AssertionError(f"Invalid fields {invalid_fields} in {key}") + + # Validate values against json schema + instance_schema = { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": instance, + } + + jsonschema.validate(instance_schema, Draft7Validator.META_SCHEMA) + print(f"Schema {rel_path} is valid.") + + +# validate associations +with open(schema_path / "meta" / "associations.yaml", "r") as f: + associations = yaml.safe_load(f) + +with open(schema_path / "metaschema" / "associations.schema.json", "r") as f: + associations_schema = json.load(f) + +jsonschema.validate(associations, associations_schema) + + +def validate_dir(dir, schema_path): + with open(schema_path, "r") as f: + schema = json.load(f) + + for fpath in glob.glob(str(dir / "*.yaml")): + with open(fpath, "r") as f: + instance = yaml.safe_load(f) + jsonschema.validate(instance, schema) + + +validate_dir( + schema_path / "rules" / "checks", + schema_path / "metaschema" / "checks.schema.json" +) + +validate_dir( + schema_path / "rules" / "files" / "raw", + schema_path / "metaschema" / "raw.schema.json", +) + + From fbd3363d3626e8f6ab9db08c148a7e1ce76ff1fb Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Mon, 15 Apr 2024 09:03:44 -0500 Subject: [PATCH 02/52] WIP metaschema and code to validate --- src/__init__.py | 0 src/metaschema.json | 208 ++++++++++++++++++ .../metaschema/associations.schema.json | 44 ---- src/schema/metaschema/checks.schema.json | 31 --- src/schema/metaschema/formats.schema.json | 23 -- src/schema/metaschema/objects.schema.yaml | 80 ------- src/schema/metaschema/raw.schema.json | 31 --- src/validate_schema.py | 86 ++------ 8 files changed, 230 insertions(+), 273 deletions(-) create mode 100644 src/__init__.py create mode 100644 src/metaschema.json delete mode 100644 src/schema/metaschema/associations.schema.json delete mode 100644 src/schema/metaschema/checks.schema.json delete mode 100644 src/schema/metaschema/formats.schema.json delete mode 100644 src/schema/metaschema/objects.schema.yaml delete mode 100644 src/schema/metaschema/raw.schema.json diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/metaschema.json b/src/metaschema.json new file mode 100644 index 0000000000..e420e4738d --- /dev/null +++ b/src/metaschema.json @@ -0,0 +1,208 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "meta": { + "type": "object", + "properties": { + "associations": { + "type": "object", + "patternProperties": + { + "^[a-zA-Z0-9_]+$": { + "type": "object", + "properties": { + "selectors": { + "type": "array", + "items": { + "type": "string" + } + }, + "target": { + "type": "object", + "properties": { + "suffix": { + "type": "string" + }, + "extension": { + "anyOf": [ + { + "type": "string" + }, { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + }, + "required": ["extension"], + "additionalProperties": false + }, + "inherit": { + "type": "boolean" + } + }, + "required": ["target"], + "additionalProperties": false + } + } + }, + "context": { + "type": "object" + }, + "expression_tests": { + "type": "array", + "items": { + "type": "object", + "properties": { + "expression": { + "type": "string" + }, + "result": {} + }, + "required": ["expression", "result"], + "additionalProperties": false + } + }, + "versions": { + "type": "array", + "items": { + "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$" + } + } + }, + "required": ["associations", "context", "expression_tests", "versions"] + }, + "objects": { + "type": "object", + "properties": { + "columns": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9_]+$": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "type": { + "type": "string" + }, + "format": { + "type": "string" + }, + "pattern": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "enum": { + "type": "array" + }, + "anyOf": { + "type": "array" + }, + "maximum": { + "type": "number" + }, + "minimum": { + "type": "number" + } + }, + "required": ["name", "display_name"], + "additionalProperties": false + } + } + }, + "common_principles": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9_]+$": { + "type": "object", + "properties": { + "display_name": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": ["display_name", "description"], + "additionalProperties": false + } + } + }, + "datatypes": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9_]+$": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": ["value", "display_name", "description"], + "additionalProperties": false + } + } + }, + "entities": { + "type": "object" + }, + "enums": { + "type": "object" + }, + "extensions": { + "type": "object" + }, + "files": { + "type": "object" + }, + "formats": { + "type": "object" + }, + "metadata": { + "type": "object" + }, + "modalities": { + "type": "object" + }, + "suffixes": { + "type": "object" + } + }, + "required": ["columns", "common_principles", "datatypes", "entities", "enums", "extensions", "files", "formats", "metadata", "modalities", "suffixes"], + "additionalProperties": false + }, + "rules": { + "type": "object" + }, + "BIDS_VERSION": { + "type": "string" + }, + "SCHEMA_VERSION": { + "type": "string" + }, + "README": { + "type": "string" + } + }, + "required": ["meta", "objects", "rules", "BIDS_VERSION", "SCHEMA_VERSION"], + "additionalProperties": false +} \ No newline at end of file diff --git a/src/schema/metaschema/associations.schema.json b/src/schema/metaschema/associations.schema.json deleted file mode 100644 index 35b9a67709..0000000000 --- a/src/schema/metaschema/associations.schema.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "patternProperties": { - "^[a-zA-Z0-9]+$": { - "type": "object", - "properties": { - "selectors": { - "type": "array", - "items": { - "type": "string" - } - }, - "target": { - "type": "object", - "properties": { - "suffix": { - "type": "string" - }, - "extension": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - } - }, - "required": ["extension"] - }, - "inherit": { - "type": "boolean" - } - }, - "required": ["selectors", "target", "inherit"], - "additionalProperties": false - } - } -} diff --git a/src/schema/metaschema/checks.schema.json b/src/schema/metaschema/checks.schema.json deleted file mode 100644 index 48707011f8..0000000000 --- a/src/schema/metaschema/checks.schema.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "description": "Schema for validating modalities supported by BIDS", - "type": "object", - "patternProperties": { - "^[a-zA-Z_][a-zA-Z0-9_]*$": { - "type": "object", - "properties": { - "issue": { - "type": "object", - "properties": { - "code": {"type": "string"}, - "message": {"type": "string"}, - "level": {"enum": ["error", "warning"]} - }, - "required": ["code", "message", "level"], - "additionalProperties": false - }, - "selectors": { - "type": "array", - "item": {"type": "string"} - }, - "checks": { - "type": "array", - "item": {"type": "string"} - } - }, - "required": ["selectors", "checks"] - } - } -} \ No newline at end of file diff --git a/src/schema/metaschema/formats.schema.json b/src/schema/metaschema/formats.schema.json deleted file mode 100644 index e15d4a16bf..0000000000 --- a/src/schema/metaschema/formats.schema.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "description": "Schema for validating various entity patterns and metadata types with specific regular expressions", - "type": "object", - "patternProperties": { - "^[a-zA-Z0-9_]+$": { - "type": "object", - "properties": { - "display_name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "pattern": { - "type": "string" - } - }, - "required": ["display_name", "description", "pattern"] - } - }, - "additionalProperties": false -} \ No newline at end of file diff --git a/src/schema/metaschema/objects.schema.yaml b/src/schema/metaschema/objects.schema.yaml deleted file mode 100644 index 558489f19d..0000000000 --- a/src/schema/metaschema/objects.schema.yaml +++ /dev/null @@ -1,80 +0,0 @@ -"objects/columns": - required: - - name - - display_name - optional: - - description - - type - - format - - unit - - enum - - anyOf - - pattern - - maximum - - minimum - -"objects/common_principles": - required: - - display_name - - description - - -"objects/datatypes": - required: - - value - - display_name - - description - -"objects/entities": - required: - - name - - display_name - - description - - type - - format - optional: - - enum - -"objects/enums": - optional: - - tags - - enum - - display_name - - description - - type - - value - -"objects/extensions": - required: - - value - - display_name - - description - -"objects/files": - required: - - display_name - - file_type - - description - -"objects/formats": - required: - - pattern - - display_name - - description - -"objects/modalities": - required: - - display_name - - description - -"objects/suffixes": - required: - - value - - display_name - - description - optional: - - unit - - minValue - - maxValue - - anyOf - diff --git a/src/schema/metaschema/raw.schema.json b/src/schema/metaschema/raw.schema.json deleted file mode 100644 index 890114dcd1..0000000000 --- a/src/schema/metaschema/raw.schema.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "description": "Schema for validating modalities supported by BIDS", - "type": "object", - "patternProperties": { - "^[a-zA-Z_][a-zA-Z0-9_]*$": { - "type": "object", - "properties": { - "suffixes": {"type": "array", "items": {"type": "string"}}, - "extensions": {"type": "array", "items": {"pattern": "^.[a-zA-Z_.*]*(/)?$"}}, - "entities": { - "type": "object", - "patternProperties": { - "^[a-zA-Z0-9_]*$": { - "anyOf": [ - {"enum": ["required", "optional"]}, - {"type": "object"} - ] - - } - } - }, - "datatypes": { - "type": "array", - "items": {"type": "string"} - } - } - }, - "additionalProperties": false - } -} \ No newline at end of file diff --git a/src/validate_schema.py b/src/validate_schema.py index c3eee154d9..8ed16d4218 100644 --- a/src/validate_schema.py +++ b/src/validate_schema.py @@ -1,76 +1,34 @@ -import glob import json from pathlib import Path +from typing import Any, Dict, Union -import jsonschema import yaml -from jsonschema import Draft7Validator -objects_schema_path = Path(__file__).resolve().parent / "schema" / "metaschema" / "objects.schema.yaml" -schema_path = Path(__file__).resolve().parent / "schema" +from jsonschema.exceptions import ValidationError +from jsonschema.validators import validate -with open(objects_schema_path, "r") as f: - objects_schema = yaml.safe_load(f) +schema_path = Path(__file__).resolve().parent.parent / "schema" -for rel_path, spec in objects_schema.items(): - with open(schema_path / f"{rel_path}.yaml", "r") as f: - instance = yaml.safe_load(f) +def load_schema(schema_path: Union[str, Path]) -> Union[Dict[str, Any], str]: + if Path(schema_path).is_dir(): + return {f.stem: load_schema(f) for f in Path(schema_path).iterdir()} + elif Path(schema_path).is_file() and (str(schema_path).endswith(".yaml") or str(schema_path).endswith(".yml")): + with open(schema_path, "r") as f: + return yaml.safe_load(f) + else: + with open(schema_path, "r") as f: + return f.read() - # Validate keys - for key, fields in instance.items(): - required_fields = spec.get("required", []) - all_fields = required_fields + spec.get("optional", []) - # Check if all required fields are present - missing_required = [field for field in required_fields if field not in fields] - if missing_required: - raise AssertionError(f"Missing required fields {missing_required} in {key}") - - # Check if all fields are valid (either required or optional) - invalid_fields = [field for field in fields if field not in all_fields] - if invalid_fields: - raise AssertionError(f"Invalid fields {invalid_fields} in {key}") - - # Validate values against json schema - instance_schema = { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "properties": instance, - } - - jsonschema.validate(instance_schema, Draft7Validator.META_SCHEMA) - print(f"Schema {rel_path} is valid.") - - -# validate associations -with open(schema_path / "meta" / "associations.yaml", "r") as f: - associations = yaml.safe_load(f) - -with open(schema_path / "metaschema" / "associations.schema.json", "r") as f: - associations_schema = json.load(f) - -jsonschema.validate(associations, associations_schema) - - -def validate_dir(dir, schema_path): - with open(schema_path, "r") as f: - schema = json.load(f) - - for fpath in glob.glob(str(dir / "*.yaml")): - with open(fpath, "r") as f: - instance = yaml.safe_load(f) - jsonschema.validate(instance, schema) - - -validate_dir( - schema_path / "rules" / "checks", - schema_path / "metaschema" / "checks.schema.json" -) - -validate_dir( - schema_path / "rules" / "files" / "raw", - schema_path / "metaschema" / "raw.schema.json", -) +schema = load_schema(schema_path) +with open("metaschema.json", "r") as f: + metaschema = json.load(f) +try: + validate(instance=schema, schema=metaschema) +except ValidationError as e: + with open("validation/error_log.txt", "w") as file: + file.write(str(e)) + raise e From 7e8526b844c194b03f024f0b0b8e85fb9f4bcf9a Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Mon, 15 Apr 2024 13:18:34 -0500 Subject: [PATCH 03/52] update schema --- src/metaschema.json | 57 ++++++++++++++++++++++++++++++++++++++++-- src/validate_schema.py | 2 +- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/metaschema.json b/src/metaschema.json index e420e4738d..2e19b944b6 100644 --- a/src/metaschema.json +++ b/src/metaschema.json @@ -163,10 +163,63 @@ } }, "entities": { - "type": "object" + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9_]+$": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "type": { + "type": "string" + }, + "format": { + "type": "string" + }, + "enum": { + "type": "array" + } + }, + "required": ["name", "display_name", "description", "type", "format"], + "additionalProperties": false + } + } }, "enums": { - "type": "object" + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9_]+$": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "enum": { + "type": "array" + }, + "value": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "tags": { + "type": "array" + } + }, + "additionalProperties": false + } + } }, "extensions": { "type": "object" diff --git a/src/validate_schema.py b/src/validate_schema.py index 8ed16d4218..f3c7246394 100644 --- a/src/validate_schema.py +++ b/src/validate_schema.py @@ -7,7 +7,7 @@ from jsonschema.exceptions import ValidationError from jsonschema.validators import validate -schema_path = Path(__file__).resolve().parent.parent / "schema" +schema_path = Path(__file__).resolve().parent.parent / "src" / "schema" def load_schema(schema_path: Union[str, Path]) -> Union[Dict[str, Any], str]: From ea887ebfaff346f67bb1aaa80f617fa51ef56d01 Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Mon, 15 Apr 2024 13:51:47 -0500 Subject: [PATCH 04/52] objects finished --- src/metaschema.json | 118 ++++++++++++++++++++++++++++++++++++++--- src/validate_schema.py | 2 +- 2 files changed, 113 insertions(+), 7 deletions(-) diff --git a/src/metaschema.json b/src/metaschema.json index 2e19b944b6..c4603d2148 100644 --- a/src/metaschema.json +++ b/src/metaschema.json @@ -222,22 +222,128 @@ } }, "extensions": { - "type": "object" + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9_]+$": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": ["display_name", "description", "value"], + "additionalProperties": false + } + } }, "files": { - "type": "object" + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9_]+$": { + "type": "object", + "properties": { + "display_name": { + "type": "string" + }, + "file_type": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": ["display_name", "file_type", "description"], + "additionalProperties": false + } + } }, "formats": { - "type": "object" + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9_]+$": { + "type": "object", + "properties": { + "display_name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "pattern": { + "format": "regex" + } + }, + "required": ["display_name", "description", "pattern"], + "additionalProperties": false + } + } }, "metadata": { - "type": "object" + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9_]+$": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": ["name","display_name", "description"] + } + } }, "modalities": { - "type": "object" + "type": "object", + "patternProperties": { + "^[a-z]+$": { + "type": "object", + "properties": { + "display_name": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": ["display_name", "description"], + "additionalProperties": false + } + } }, "suffixes": { - "type": "object" + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9_]+$": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": ["value", "display_name", "description"] + } + } } }, "required": ["columns", "common_principles", "datatypes", "entities", "enums", "extensions", "files", "formats", "metadata", "modalities", "suffixes"], diff --git a/src/validate_schema.py b/src/validate_schema.py index f3c7246394..8dc0d264cd 100644 --- a/src/validate_schema.py +++ b/src/validate_schema.py @@ -29,6 +29,6 @@ def load_schema(schema_path: Union[str, Path]) -> Union[Dict[str, Any], str]: try: validate(instance=schema, schema=metaschema) except ValidationError as e: - with open("validation/error_log.txt", "w") as file: + with open("error_log.txt", "w") as file: file.write(str(e)) raise e From 229e4b6bbc11ea4c3ee17a110c57451ce4a21ed8 Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Mon, 15 Apr 2024 15:30:44 -0500 Subject: [PATCH 05/52] add rules --- src/metaschema.json | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/metaschema.json b/src/metaschema.json index c4603d2148..218b0dce96 100644 --- a/src/metaschema.json +++ b/src/metaschema.json @@ -350,8 +350,40 @@ "additionalProperties": false }, "rules": { - "type": "object" - }, + "type": "object", + "properties": { + "checks": { + "type": "object" + }, + "files": { + "type": "object" + }, + "sidecars": { + "type": "object" + }, + "tabular_data": { + "type": "object" + }, + "common_principles": { + "type": "array" + }, + "data_metadata": { + "type": "object" + }, + "directories": { + "type": "object" + }, + "entities": { + "type": "array" + }, + "errors": { + "type": "object" + }, + "modalities": { + "type": "object" + } + } + }, "BIDS_VERSION": { "type": "string" }, From 533485c88bcfa1fca46ae8346e3d2399de56938a Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Mon, 15 Apr 2024 15:37:06 -0500 Subject: [PATCH 06/52] add checks --- src/metaschema.json | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/metaschema.json b/src/metaschema.json index 218b0dce96..32f11f1ba9 100644 --- a/src/metaschema.json +++ b/src/metaschema.json @@ -353,7 +353,38 @@ "type": "object", "properties": { "checks": { - "type": "object" + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9_]+$": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9_]+$": { + "type": "object", + "properties": { + "issues": { + "type": "object", + "properties": { + "code": {"type": "string"}, + "message": {"type": "string"}, + "level": {"enum": ["error", "warning"]} + }, + "required": ["code", "message", "level"], + "additionalProperties": false + }, + "selectors": { + "type": "array", + "items": {"type": "string"} + }, + "checks": { + "type": "array", + "items": {"type": "string"} + } + }, + "required": ["checks", "selectors"] + } + } + } + } }, "files": { "type": "object" From d5e9e649d083f59d090400788bb55ca37132c8ff Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Mon, 15 Apr 2024 15:42:29 -0500 Subject: [PATCH 07/52] add rules.files.common --- src/metaschema.json | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/metaschema.json b/src/metaschema.json index 32f11f1ba9..20df3538b6 100644 --- a/src/metaschema.json +++ b/src/metaschema.json @@ -387,7 +387,21 @@ } }, "files": { - "type": "object" + "type": "object", + "properties": { + "common": { + "type": "object", + "properties": { + "core": {"type": "object"}, + "tables": {"type": "object"} + }, + "required": ["core", "tables"], + "additionalProperties": false + }, + "deriv": {"type": "object"}, + "raw": {"type": "object"} + }, + "required": ["common", "deriv", "raw"] }, "sidecars": { "type": "object" From a5f9301f1e7e90c3dceb73955f61fbf502f32266 Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Mon, 15 Apr 2024 15:54:45 -0500 Subject: [PATCH 08/52] add files.common.core and files.common.tables --- src/metaschema.json | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/metaschema.json b/src/metaschema.json index 20df3538b6..0fa54599f0 100644 --- a/src/metaschema.json +++ b/src/metaschema.json @@ -392,8 +392,40 @@ "common": { "type": "object", "properties": { - "core": {"type": "object"}, - "tables": {"type": "object"} + "core": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9_]+$": { + "type": "object", + "properties": { + "level": {"type": "string"}, + "path": {"type": "string"}, + "extensions": {"type": "array"}, + "stem": {"type": "string"} + }, + "required": ["level"], + "additionalProperties": false + } + } + }, + "tables": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9_]+$": { + "type": "object", + "properties": { + "level": {"type": "string"}, + "path": {"type": "string"}, + "extensions": {"type": "array"}, + "stem": {"type": "string"}, + "entities": {"type": "object"}, + "suffixes": {"type": "array"} + }, + "required": ["level", "extensions"], + "additionalProperties": false + } + } + } }, "required": ["core", "tables"], "additionalProperties": false From 5784dc2caca214f7baedae8be11aaa3ee947abd5 Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Mon, 15 Apr 2024 16:05:19 -0500 Subject: [PATCH 09/52] add rules.files.deriv --- src/metaschema.json | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/metaschema.json b/src/metaschema.json index 0fa54599f0..d8161e322d 100644 --- a/src/metaschema.json +++ b/src/metaschema.json @@ -430,7 +430,36 @@ "required": ["core", "tables"], "additionalProperties": false }, - "deriv": {"type": "object"}, + "deriv": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9_]+$": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9_]+$": { + "type": "object", + "properties": { + "$ref": {"type": "string"}, + "entities": { + "type": "object", + "properties": { + "$ref": {"type": "string"}, + "space": {"enum": ["optional"]}, + "resolution": {"enum": ["optional"]}, + "density": {"enum": ["optional"]}, + "description": {"enum": ["optional"]}, + "label": {"enum": ["optional"]}, + "hemisphere": {"enum": ["optional"]} + }, + "required": ["$ref", "space", "description"], + "additionalProperties": false + } + } + } + } + } + } + }, "raw": {"type": "object"} }, "required": ["common", "deriv", "raw"] From 6d034b83ccf22f9520f07d2ef5eb63267dd6d910 Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Mon, 15 Apr 2024 20:07:35 -0500 Subject: [PATCH 10/52] dereferencing working --- src/metaschema.json | 61 ++++++++++++++++++++++++++++++++---------- src/validate_schema.py | 23 ++++++++++++++++ 2 files changed, 70 insertions(+), 14 deletions(-) diff --git a/src/metaschema.json b/src/metaschema.json index d8161e322d..f5bad44562 100644 --- a/src/metaschema.json +++ b/src/metaschema.json @@ -439,20 +439,26 @@ "^[a-zA-Z0-9_]+$": { "type": "object", "properties": { - "$ref": {"type": "string"}, "entities": { "type": "object", - "properties": { - "$ref": {"type": "string"}, - "space": {"enum": ["optional"]}, - "resolution": {"enum": ["optional"]}, - "density": {"enum": ["optional"]}, - "description": {"enum": ["optional"]}, - "label": {"enum": ["optional"]}, - "hemisphere": {"enum": ["optional"]} - }, - "required": ["$ref", "space", "description"], - "additionalProperties": false + "patternProperties": { + "^[a-z]+$": { + "anyOf": [ + {"enum": ["optional", "required"]}, + { + "type": "object", + "properties": { + "level": {"enum": ["optional", "required"]}, + "enum": { + "type": "array", + "items": {"type": "string"} + } + }, + "required": ["level", "enum"] + } + ] + } + } } } } @@ -460,9 +466,36 @@ } } }, - "raw": {"type": "object"} + "raw": { + "type": "object", + "patternProperties": { + "^[a-z]+$": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9_]+$": { + "type": "object", + "properties": { + "suffixes": { + "type": "array" + }, + "extensions": { + "type": "array" + }, + "datatypes": { + "type": "array" + }, + "entities": { + "type": "object" + } + } + } + } + } + } + } }, - "required": ["common", "deriv", "raw"] + "required": ["common", "deriv", "raw"], + "additionalProperties": false }, "sidecars": { "type": "object" diff --git a/src/validate_schema.py b/src/validate_schema.py index 8dc0d264cd..ff71da4d8a 100644 --- a/src/validate_schema.py +++ b/src/validate_schema.py @@ -21,7 +21,30 @@ def load_schema(schema_path: Union[str, Path]) -> Union[Dict[str, Any], str]: return f.read() +def derefence_schema(obj): + if isinstance(obj, dict): + out = dict() + for k, v in obj.items(): + if k == "$ref": + #print(v) + address = v.split(".") + here = schema + for part in address: + here = here[part] + if isinstance(here, dict): + out.update(derefence_schema(here)) + else: + return derefence_schema(here) + else: + out[k] = derefence_schema(v) + return out + if isinstance(obj, list): + return [derefence_schema(x) for x in obj] + return obj + + schema = load_schema(schema_path) +schema = derefence_schema(schema) with open("metaschema.json", "r") as f: metaschema = json.load(f) From f809e196535b3dbe843be13d50d44f8513701801 Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Mon, 15 Apr 2024 20:21:26 -0500 Subject: [PATCH 11/52] checkpoint --- src/metaschema.json | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/metaschema.json b/src/metaschema.json index f5bad44562..0320545b4c 100644 --- a/src/metaschema.json +++ b/src/metaschema.json @@ -476,13 +476,16 @@ "type": "object", "properties": { "suffixes": { - "type": "array" + "type": "array", + "items": {"type": "string"} }, "extensions": { - "type": "array" + "type": "array", + "items": {"pattern": "^[./][a-z.]+|"} }, "datatypes": { - "type": "array" + "type": "array", + "items": {"type": "string"} }, "entities": { "type": "object" From 598fddf58fd081586769003ebfb54719eca81fad Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Mon, 15 Apr 2024 21:01:16 -0500 Subject: [PATCH 12/52] checkpoint --- src/metaschema.json | 135 +++++++++++++++++++++++++++-------------- src/validate_schema.py | 3 +- 2 files changed, 91 insertions(+), 47 deletions(-) diff --git a/src/metaschema.json b/src/metaschema.json index 0320545b4c..35d1a992dd 100644 --- a/src/metaschema.json +++ b/src/metaschema.json @@ -47,7 +47,8 @@ "required": ["target"], "additionalProperties": false } - } + }, + "additionalProperties": false }, "context": { "type": "object" @@ -121,7 +122,8 @@ "required": ["name", "display_name"], "additionalProperties": false } - } + }, + "additionalProperties": false }, "common_principles": { "type": "object", @@ -138,7 +140,8 @@ }, "required": ["display_name", "description"], "additionalProperties": false - } + }, + "additionalProperties": false } }, "datatypes": { @@ -160,7 +163,8 @@ "required": ["value", "display_name", "description"], "additionalProperties": false } - } + }, + "additionalProperties": false }, "entities": { "type": "object", @@ -190,12 +194,13 @@ "required": ["name", "display_name", "description", "type", "format"], "additionalProperties": false } - } + }, + "additionalProperties": false }, "enums": { "type": "object", "patternProperties": { - "^[a-zA-Z0-9_]+$": { + "^[a-zA-Z0-9_-]+$": { "type": "object", "properties": { "type": { @@ -219,7 +224,8 @@ }, "additionalProperties": false } - } + }, + "additionalProperties": false }, "extensions": { "type": "object", @@ -240,7 +246,8 @@ "required": ["display_name", "description", "value"], "additionalProperties": false } - } + }, + "additionalProperties": false }, "files": { "type": "object", @@ -261,7 +268,8 @@ "required": ["display_name", "file_type", "description"], "additionalProperties": false } - } + }, + "additionalProperties": false }, "formats": { "type": "object", @@ -282,7 +290,8 @@ "required": ["display_name", "description", "pattern"], "additionalProperties": false } - } + }, + "additionalProperties": false }, "metadata": { "type": "object", @@ -305,7 +314,8 @@ }, "required": ["name","display_name", "description"] } - } + }, + "additionalProperties": false }, "modalities": { "type": "object", @@ -323,7 +333,8 @@ "required": ["display_name", "description"], "additionalProperties": false } - } + }, + "additionalProperties": false }, "suffixes": { "type": "object", @@ -342,7 +353,8 @@ } }, "required": ["value", "display_name", "description"] - } + }, + "additionalProperties": false } } }, @@ -384,7 +396,8 @@ } } } - } + }, + "additionalProperties": false }, "files": { "type": "object", @@ -406,7 +419,8 @@ "required": ["level"], "additionalProperties": false } - } + }, + "additionalProperties": false }, "tables": { "type": "object", @@ -418,13 +432,14 @@ "path": {"type": "string"}, "extensions": {"type": "array"}, "stem": {"type": "string"}, - "entities": {"type": "object"}, + "entities": {"$ref": "#/definitions/entities"}, "suffixes": {"type": "array"} }, "required": ["level", "extensions"], "additionalProperties": false } - } + }, + "additionalProperties": false } }, "required": ["core", "tables"], @@ -440,31 +455,15 @@ "type": "object", "properties": { "entities": { - "type": "object", - "patternProperties": { - "^[a-z]+$": { - "anyOf": [ - {"enum": ["optional", "required"]}, - { - "type": "object", - "properties": { - "level": {"enum": ["optional", "required"]}, - "enum": { - "type": "array", - "items": {"type": "string"} - } - }, - "required": ["level", "enum"] - } - ] - } - } + "$ref": "#/definitions/entities" } } - } + }, + "additionalProperties": false } } - } + }, + "additionalProperties": false }, "raw": { "type": "object", @@ -485,23 +484,43 @@ }, "datatypes": { "type": "array", - "items": {"type": "string"} + "items": {"pattern": "^[a-z]+$"} }, - "entities": { - "type": "object" - } + "entities": {"$ref": "#/definitions/entities"} } } - } + }, + "additionalProperties": false } - } + }, + "additionalProperties": false } }, "required": ["common", "deriv", "raw"], "additionalProperties": false }, "sidecars": { - "type": "object" + "type": "object", + "patternProperties": { + "^derivatives$": {}, + "^(?!derivatives$)[a-z_]+$": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9_]+$": { + "type": "object", + "properties": { + "selectors": {}, + "fields": {} + }, + "required": ["selectors", "fields"], + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "additionalProperties": false + }, + "additionalProperties": false }, "tabular_data": { "type": "object" @@ -537,5 +556,29 @@ } }, "required": ["meta", "objects", "rules", "BIDS_VERSION", "SCHEMA_VERSION"], - "additionalProperties": false + "additionalProperties": false, + "definitions": { + "entities": { + "type": "object", + "patternProperties": { + "^[a-z]+$": { + "anyOf": [ + {"enum": ["optional", "required"]}, + { + "type": "object", + "properties": { + "level": {"enum": ["optional", "required"]}, + "enum": { + "type": "array", + "items": {"type": "string"} + } + }, + "required": ["level", "enum"] + } + ] + } + }, + "additionalProperties": false + } + } } \ No newline at end of file diff --git a/src/validate_schema.py b/src/validate_schema.py index ff71da4d8a..0af7922098 100644 --- a/src/validate_schema.py +++ b/src/validate_schema.py @@ -26,7 +26,6 @@ def derefence_schema(obj): out = dict() for k, v in obj.items(): if k == "$ref": - #print(v) address = v.split(".") here = schema for part in address: @@ -49,6 +48,8 @@ def derefence_schema(obj): with open("metaschema.json", "r") as f: metaschema = json.load(f) +# validate is put in this try/except clause because the error is sometimes too long to +# print in the terminal try: validate(instance=schema, schema=metaschema) except ValidationError as e: From 08d256fb4b8494909efe5ad2bcacce02e13c0f84 Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Mon, 15 Apr 2024 21:14:46 -0500 Subject: [PATCH 13/52] add rules.sidecars --- src/metaschema.json | 27 +++++++++++++++++++++++++-- src/schema/rules/sidecars/ieeg.yaml | 2 +- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/metaschema.json b/src/metaschema.json index 35d1a992dd..a900c7ebdf 100644 --- a/src/metaschema.json +++ b/src/metaschema.json @@ -509,8 +509,31 @@ "^[a-zA-Z0-9_]+$": { "type": "object", "properties": { - "selectors": {}, - "fields": {} + "selectors": { + "type": "array", + "items": {"type": "string"} + }, + "fields": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9_]+$": { + "anyOf": [ + {"enum": ["recommended", "optional", "required", "DEPRECATED"]}, + { + "type": "object", + "properties": { + "level": {"enum": ["recommended", "optional", "required", "DEPRECATED"]}, + "level_addendum": {"type": "string"} + }, + "required": ["level", "level_addendum"], + "additionalProperties": false + }, + {"pattern": "recommended.*"} + ] + } + }, + "additionalProperties": false + } }, "required": ["selectors", "fields"], "additionalProperties": false diff --git a/src/schema/rules/sidecars/ieeg.yaml b/src/schema/rules/sidecars/ieeg.yaml index 066e17a8bf..7d2655a1d7 100644 --- a/src/schema/rules/sidecars/ieeg.yaml +++ b/src/schema/rules/sidecars/ieeg.yaml @@ -66,7 +66,7 @@ iEEGRecommended: - datatype == "ieeg" - suffix == "ieeg" fields: - DCOffsetCorrection: deprecated + DCOffsetCorrection: DEPRECATED HardwareFilters: recommended ElectrodeManufacturer: recommended ElectrodeManufacturersModelName: recommended From 6caa965e15cd96b5c88bb881d4c7308e2668caea Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Mon, 15 Apr 2024 21:21:35 -0500 Subject: [PATCH 14/52] sidecar derivatives checkpoint --- src/metaschema.json | 80 +++++++++++-------- .../derivatives/common_derivatives.yaml | 4 +- 2 files changed, 47 insertions(+), 37 deletions(-) diff --git a/src/metaschema.json b/src/metaschema.json index a900c7ebdf..e8da997b85 100644 --- a/src/metaschema.json +++ b/src/metaschema.json @@ -502,45 +502,17 @@ "sidecars": { "type": "object", "patternProperties": { - "^derivatives$": {}, - "^(?!derivatives$)[a-z_]+$": { + "^derivatives$": { "type": "object", - "patternProperties": { - "^[a-zA-Z0-9_]+$": { - "type": "object", - "properties": { - "selectors": { - "type": "array", - "items": {"type": "string"} - }, - "fields": { - "type": "object", - "patternProperties": { - "^[a-zA-Z0-9_]+$": { - "anyOf": [ - {"enum": ["recommended", "optional", "required", "DEPRECATED"]}, - { - "type": "object", - "properties": { - "level": {"enum": ["recommended", "optional", "required", "DEPRECATED"]}, - "level_addendum": {"type": "string"} - }, - "required": ["level", "level_addendum"], - "additionalProperties": false - }, - {"pattern": "recommended.*"} - ] - } - }, - "additionalProperties": false - } - }, - "required": ["selectors", "fields"], - "additionalProperties": false - } + "properties": { + "common_derivatives": {"$ref": "#/definitions/sidecar"} }, + "required": ["common_derivatives"], "additionalProperties": false }, + "^(?!derivatives$)[a-z_]+$": { + "$ref": "#/definitions/sidecar" + }, "additionalProperties": false }, "additionalProperties": false @@ -602,6 +574,44 @@ } }, "additionalProperties": false + }, + "sidecar": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9_]+$": { + "type": "object", + "properties": { + "selectors": { + "type": "array", + "items": {"type": "string"} + }, + "fields": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9_]+$": { + "anyOf": [ + {"enum": ["recommended", "optional", "required", "DEPRECATED"]}, + { + "type": "object", + "properties": { + "level": {"enum": ["recommended", "optional", "required", "DEPRECATED"]}, + "level_addendum": {"type": "string"} + }, + "required": ["level", "level_addendum"], + "additionalProperties": false + }, + {"pattern": "recommended.*"} + ] + } + }, + "additionalProperties": false + } + }, + "required": ["selectors", "fields"], + "additionalProperties": false + } + }, + "additionalProperties": false } } } \ No newline at end of file diff --git a/src/schema/rules/sidecars/derivatives/common_derivatives.yaml b/src/schema/rules/sidecars/derivatives/common_derivatives.yaml index fbc05937bc..a550ca2b79 100644 --- a/src/schema/rules/sidecars/derivatives/common_derivatives.yaml +++ b/src/schema/rules/sidecars/derivatives/common_derivatives.yaml @@ -7,7 +7,7 @@ CommonDerivativeFields: level: recommended description_addendum: This describes the nature of the file. Sources: optional - RawSources: deprecated + RawSources: DEPRECATED SpatialReferenceEntity: selectors: @@ -36,7 +36,7 @@ MaskDerivatives: fields: Type: recommended Sources: recommended - RawSources: deprecated + RawSources: DEPRECATED SegmentationCommon: selectors: From 5d4f8e93dcff2111ca2ecd60a9902a386ff16732 Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Mon, 15 Apr 2024 21:33:28 -0500 Subject: [PATCH 15/52] tabular_data checkpoint --- src/metaschema.json | 66 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/src/metaschema.json b/src/metaschema.json index e8da997b85..0b7817d7b3 100644 --- a/src/metaschema.json +++ b/src/metaschema.json @@ -518,7 +518,25 @@ "additionalProperties": false }, "tabular_data": { - "type": "object" + "type": "object", + "patternProperties": { + "^derivatives$": { + "type": "object", + "properties": { + "common_derivatives": { + "$ref": "#/definitions/tabular_data" + } + }, + "required": [ + "common_derivatives" + ], + "additionalProperties": false + }, + "^(?!derivatives$)[a-z_]+$": { + "$ref": "#/definitions/tabular_data" + } + }, + "additionalProperties": false }, "common_principles": { "type": "array" @@ -612,6 +630,52 @@ } }, "additionalProperties": false + }, + "tabular_data": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9_]+$": { + "type": "object", + "properties": { + "selectors": { + "type": "array", + "items": {"type": "string"} + }, + "columns": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9_]+$": { + "anyOf": [ + {"enum": ["recommended", "optional", "required", "DEPRECATED"]}, + { + "type": "object", + "properties": { + "level": {"enum": ["recommended", "optional", "required", "DEPRECATED"]}, + "level_addendum": {"type": "string"}, + "description_addendum": {"type": "string"} + }, + "required": ["level"], + "additionalProperties": false + }, + {"pattern": "recommended.*"} + ] + } + }, + "additionalProperties": false + }, + "additional_columns": { + "type": "string" + }, + "index_columns": {"type": "array", "items": {"type": "string"}}, + "initial_columns": { + "type": "array", + "items": {"type": "string"} + } + }, + "required": ["selectors", "columns"], + "additionalProperties": false + } + } } } } \ No newline at end of file From 6283a25961abb7997d0f174f74dc898186500b20 Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Mon, 15 Apr 2024 21:40:12 -0500 Subject: [PATCH 16/52] DONE! --- src/metaschema.json | 58 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/src/metaschema.json b/src/metaschema.json index 0b7817d7b3..05f5b3ac10 100644 --- a/src/metaschema.json +++ b/src/metaschema.json @@ -539,24 +539,70 @@ "additionalProperties": false }, "common_principles": { - "type": "array" + "type": "array", + "items": {"type": "string"} }, - "data_metadata": { + "dataset_metadata": { "type": "object" }, "directories": { "type": "object" }, "entities": { - "type": "array" + "type": "array", + "items": {"type": "string"} }, "errors": { - "type": "object" + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9_]+$": { + "type": "object", + "properties": { + "code": {"type": "string"}, + "message": {"type": "string"}, + "level": {"enum": ["error", "warning"]}, + "selectors": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": ["message", "level"], + "additionalProperties": false + } + }, + "additionalProperties": false }, "modalities": { - "type": "object" + "type": "object", + "patternProperties": { + "^[a-z]+$": { + "type": "object", + "properties": { + "datatypes": { + "type": "array", + "items": {"pattern": "^[a-z]+$"} + } + }, + "required": ["datatypes"], + "additionalProperties": false + } + } } - } + }, + "required": [ + "entities", + "files", + "sidecars", + "tabular_data", + "common_principles", + "dataset_metadata", + "directories", + "errors", + "modalities" + ], + "additionalProperties": false }, "BIDS_VERSION": { "type": "string" From 9d2923f5030a0ca218dcf7b27ea241c0a86d28bb Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Mon, 15 Apr 2024 21:43:34 -0500 Subject: [PATCH 17/52] DONE! --- src/validate_schema.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/validate_schema.py b/src/validate_schema.py index 0af7922098..7d595a2994 100644 --- a/src/validate_schema.py +++ b/src/validate_schema.py @@ -11,6 +11,7 @@ def load_schema(schema_path: Union[str, Path]) -> Union[Dict[str, Any], str]: + """Load a schema from a file or directory""" if Path(schema_path).is_dir(): return {f.stem: load_schema(f) for f in Path(schema_path).iterdir()} elif Path(schema_path).is_file() and (str(schema_path).endswith(".yaml") or str(schema_path).endswith(".yml")): @@ -22,6 +23,7 @@ def load_schema(schema_path: Union[str, Path]) -> Union[Dict[str, Any], str]: def derefence_schema(obj): + """Derefence a schema by replacing $ref with the actual schema it points to""" if isinstance(obj, dict): out = dict() for k, v in obj.items(): From 68abc619e9d267537de914342701212a5b64bbf0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 02:45:33 +0000 Subject: [PATCH 18/52] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/metaschema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/metaschema.json b/src/metaschema.json index 05f5b3ac10..58fce23a01 100644 --- a/src/metaschema.json +++ b/src/metaschema.json @@ -724,4 +724,4 @@ } } } -} \ No newline at end of file +} From b4a0af2dd12275c786a085b5d0cb9433a3b4907f Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Mon, 15 Apr 2024 21:48:49 -0500 Subject: [PATCH 19/52] fix typo --- src/validate_schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/validate_schema.py b/src/validate_schema.py index 7d595a2994..d30903c2c3 100644 --- a/src/validate_schema.py +++ b/src/validate_schema.py @@ -23,7 +23,7 @@ def load_schema(schema_path: Union[str, Path]) -> Union[Dict[str, Any], str]: def derefence_schema(obj): - """Derefence a schema by replacing $ref with the actual schema it points to""" + """Dereference a schema by replacing $ref with the actual schema it points to""" if isinstance(obj, dict): out = dict() for k, v in obj.items(): From 67783946bd21c3f0885662a93013af92fd239a46 Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Mon, 15 Apr 2024 21:55:56 -0500 Subject: [PATCH 20/52] blackify --- src/validate_schema.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/validate_schema.py b/src/validate_schema.py index d30903c2c3..c193c97b2e 100644 --- a/src/validate_schema.py +++ b/src/validate_schema.py @@ -14,7 +14,9 @@ def load_schema(schema_path: Union[str, Path]) -> Union[Dict[str, Any], str]: """Load a schema from a file or directory""" if Path(schema_path).is_dir(): return {f.stem: load_schema(f) for f in Path(schema_path).iterdir()} - elif Path(schema_path).is_file() and (str(schema_path).endswith(".yaml") or str(schema_path).endswith(".yml")): + elif Path(schema_path).is_file() and ( + str(schema_path).endswith(".yaml") or str(schema_path).endswith(".yml") + ): with open(schema_path, "r") as f: return yaml.safe_load(f) else: From f2712946bc245f611fcef188e8cb19a69c6e0de9 Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Tue, 16 Apr 2024 09:53:22 -0500 Subject: [PATCH 21/52] include references to draft7 metaschema --- src/metaschema.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/metaschema.json b/src/metaschema.json index 58fce23a01..8426daf2be 100644 --- a/src/metaschema.json +++ b/src/metaschema.json @@ -95,7 +95,7 @@ "type": "string" }, "type": { - "type": "string" + "$ref": "http://json-schema.org/draft-07/schema#/properties/type" }, "format": { "type": "string" @@ -110,7 +110,7 @@ "type": "array" }, "anyOf": { - "type": "array" + "$ref": "http://json-schema.org/draft-07/schema#/properties/anyOf" }, "maximum": { "type": "number" @@ -182,7 +182,7 @@ "type": "string" }, "type": { - "type": "string" + "$ref": "http://json-schema.org/draft-07/schema#/properties/type" }, "format": { "type": "string" @@ -204,7 +204,7 @@ "type": "object", "properties": { "type": { - "type": "string" + "$ref": "http://json-schema.org/draft-07/schema#/properties/type" }, "enum": { "type": "array" @@ -309,7 +309,7 @@ "type": "string" }, "type": { - "type": "string" + "$ref": "http://json-schema.org/draft-07/schema#/properties/type" } }, "required": ["name","display_name", "description"] From ee4ece8d7a1db035940d957e4a6933a8a69c7305 Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Tue, 16 Apr 2024 09:54:18 -0500 Subject: [PATCH 22/52] move metaschema --- src/{ => schema}/metaschema.json | 0 src/validate_schema.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/{ => schema}/metaschema.json (100%) diff --git a/src/metaschema.json b/src/schema/metaschema.json similarity index 100% rename from src/metaschema.json rename to src/schema/metaschema.json diff --git a/src/validate_schema.py b/src/validate_schema.py index c193c97b2e..dd64010348 100644 --- a/src/validate_schema.py +++ b/src/validate_schema.py @@ -49,7 +49,7 @@ def derefence_schema(obj): schema = load_schema(schema_path) schema = derefence_schema(schema) -with open("metaschema.json", "r") as f: +with open("schema/metaschema.json", "r") as f: metaschema = json.load(f) # validate is put in this try/except clause because the error is sometimes too long to From 3ddf673c376fb49eb22f23fba65c423e61be0726 Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Tue, 16 Apr 2024 12:01:27 -0500 Subject: [PATCH 23/52] add tests --- src/{schema => }/metaschema.json | 0 src/schema_validation/__init__.py | 0 .../test_validate_metaschema.py | 53 +++++++++++++ src/schema_validation/validate_schema.py | 76 +++++++++++++++++++ src/validate_schema.py | 62 --------------- 5 files changed, 129 insertions(+), 62 deletions(-) rename src/{schema => }/metaschema.json (100%) create mode 100644 src/schema_validation/__init__.py create mode 100644 src/schema_validation/test_validate_metaschema.py create mode 100644 src/schema_validation/validate_schema.py delete mode 100644 src/validate_schema.py diff --git a/src/schema/metaschema.json b/src/metaschema.json similarity index 100% rename from src/schema/metaschema.json rename to src/metaschema.json diff --git a/src/schema_validation/__init__.py b/src/schema_validation/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/schema_validation/test_validate_metaschema.py b/src/schema_validation/test_validate_metaschema.py new file mode 100644 index 0000000000..2f7a579d0a --- /dev/null +++ b/src/schema_validation/test_validate_metaschema.py @@ -0,0 +1,53 @@ +from pathlib import Path + +import pytest +from jsonschema.exceptions import ValidationError + +from .validate_schema import load_schema, derefence_schema, validate_schema + +schema_path = Path(__file__).resolve().parent.parent / "schema" + + +def test_load_schema(): + """Test that a valid schema does not raise an error.""" + load_schema(schema_path) + + +def test_derefence_schema(): + """Test that a valid schema does not raise an error.""" + schema = load_schema(schema_path) + derefence_schema(schema) + + +def test_valid_schema(): + """Test that a valid schema does not raise an error.""" + schema = load_schema(schema_path) + validate_schema(schema) + + +def test_broken_reference(): + """Test that a broken reference raises an error.""" + schema = load_schema(schema_path) + schema["rules"]["files"]["deriv"]["preprocessed_data"]["anat_nonparametric_common"]["$ref"] = "broken.reference" + with pytest.raises(KeyError) as e: + derefence_schema(schema) + + assert "'broken'" in str(e.value) + + +def test_add_legal_field(): + """Test that adding a legal field does not raise an error.""" + schema = load_schema(schema_path) + schema["rules"]["files"]["deriv"]["preprocessed_data"]["anat_nonparametric_common"]["entities"]["density"] = "optional" + validate_schema(schema) + + +def test_invalid_value(): + """Test that an invalid value raises an error.""" + schema = load_schema(schema_path) + schema["rules"]["files"]["deriv"]["preprocessed_data"]["anat_nonparametric_common"]["entities"]["density"] = "invalid" + with pytest.raises(ValidationError) as e: + validate_schema(schema) + + assert "invalid" in str(e.value) + diff --git a/src/schema_validation/validate_schema.py b/src/schema_validation/validate_schema.py new file mode 100644 index 0000000000..821f1a81bc --- /dev/null +++ b/src/schema_validation/validate_schema.py @@ -0,0 +1,76 @@ +import json +from pathlib import Path +from typing import Any, Dict, Union + +import yaml + +from jsonschema.exceptions import ValidationError +from jsonschema.validators import validate + +schema_path = Path(__file__).resolve().parent.parent / "schema" + + +def load_schema(schema_path: Union[str, Path]) -> Union[Dict[str, Any], str]: + """Load a schema from a file or directory""" + if Path(schema_path).is_dir(): + return {f.stem: load_schema(f) for f in Path(schema_path).iterdir()} + elif Path(schema_path).is_file() and ( + str(schema_path).endswith(".yaml") or str(schema_path).endswith(".yml") + ): + with open(schema_path, "r") as f: + return yaml.safe_load(f) + else: + with open(schema_path, "r") as f: + return f.read() + + +def derefence_schema(schema): + """Dereference a schema by replacing $ref with the actual schema it points to""" + + def _dereference_obj(obj): + if isinstance(obj, dict): + out = dict() + for k, v in obj.items(): + if k == "$ref": + address = v.split(".") + here = schema + for part in address: + here = here[part] + if isinstance(here, dict): + out.update(_dereference_obj(here)) + else: + return _dereference_obj(here) + else: + out[k] = _dereference_obj(v) + return out + if isinstance(obj, list): + return [_dereference_obj(x) for x in obj] + return obj + + return _dereference_obj(schema) + + +def validate_schema(schema: dict): + schema = derefence_schema(schema) + with open("metaschema.json", "r") as f: + metaschema = json.load(f) + + # validate is put in this try/except clause because the error is sometimes too long to + # print in the terminal + try: + validate(instance=schema, schema=metaschema) + except ValidationError as e: + with open("error_log.txt", "w") as file: + file.write(str(e)) + raise e + + +def main(): + schema = load_schema(schema_path) + validate_schema(schema) + + +if __name__ == "__main__": + main() + + diff --git a/src/validate_schema.py b/src/validate_schema.py deleted file mode 100644 index dd64010348..0000000000 --- a/src/validate_schema.py +++ /dev/null @@ -1,62 +0,0 @@ -import json -from pathlib import Path -from typing import Any, Dict, Union - -import yaml - -from jsonschema.exceptions import ValidationError -from jsonschema.validators import validate - -schema_path = Path(__file__).resolve().parent.parent / "src" / "schema" - - -def load_schema(schema_path: Union[str, Path]) -> Union[Dict[str, Any], str]: - """Load a schema from a file or directory""" - if Path(schema_path).is_dir(): - return {f.stem: load_schema(f) for f in Path(schema_path).iterdir()} - elif Path(schema_path).is_file() and ( - str(schema_path).endswith(".yaml") or str(schema_path).endswith(".yml") - ): - with open(schema_path, "r") as f: - return yaml.safe_load(f) - else: - with open(schema_path, "r") as f: - return f.read() - - -def derefence_schema(obj): - """Dereference a schema by replacing $ref with the actual schema it points to""" - if isinstance(obj, dict): - out = dict() - for k, v in obj.items(): - if k == "$ref": - address = v.split(".") - here = schema - for part in address: - here = here[part] - if isinstance(here, dict): - out.update(derefence_schema(here)) - else: - return derefence_schema(here) - else: - out[k] = derefence_schema(v) - return out - if isinstance(obj, list): - return [derefence_schema(x) for x in obj] - return obj - - -schema = load_schema(schema_path) -schema = derefence_schema(schema) - -with open("schema/metaschema.json", "r") as f: - metaschema = json.load(f) - -# validate is put in this try/except clause because the error is sometimes too long to -# print in the terminal -try: - validate(instance=schema, schema=metaschema) -except ValidationError as e: - with open("error_log.txt", "w") as file: - file.write(str(e)) - raise e From 6d7fe5df7fc2aaa28466a8593f70f8207cc4d976 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 17:01:50 +0000 Subject: [PATCH 24/52] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/schema_validation/test_validate_metaschema.py | 1 - src/schema_validation/validate_schema.py | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/schema_validation/test_validate_metaschema.py b/src/schema_validation/test_validate_metaschema.py index 2f7a579d0a..e86c2cc96d 100644 --- a/src/schema_validation/test_validate_metaschema.py +++ b/src/schema_validation/test_validate_metaschema.py @@ -50,4 +50,3 @@ def test_invalid_value(): validate_schema(schema) assert "invalid" in str(e.value) - diff --git a/src/schema_validation/validate_schema.py b/src/schema_validation/validate_schema.py index 821f1a81bc..674585ae20 100644 --- a/src/schema_validation/validate_schema.py +++ b/src/schema_validation/validate_schema.py @@ -72,5 +72,3 @@ def main(): if __name__ == "__main__": main() - - From 49a332bc910f47ff27b7206ddde1f24ec30e3a28 Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Tue, 16 Apr 2024 14:59:55 -0500 Subject: [PATCH 25/52] move things around --- src/__init__.py | 0 src/schema_validation/__init__.py | 0 .../bidsschematools/tests}/test_validate_metaschema.py | 4 ++-- .../schemacode/bidsschematools}/validate_schema.py | 0 4 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 src/__init__.py delete mode 100644 src/schema_validation/__init__.py rename {src/schema_validation => tools/schemacode/bidsschematools/tests}/test_validate_metaschema.py (89%) rename {src/schema_validation => tools/schemacode/bidsschematools}/validate_schema.py (100%) diff --git a/src/__init__.py b/src/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/schema_validation/__init__.py b/src/schema_validation/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/schema_validation/test_validate_metaschema.py b/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py similarity index 89% rename from src/schema_validation/test_validate_metaschema.py rename to tools/schemacode/bidsschematools/tests/test_validate_metaschema.py index 2f7a579d0a..8eb89a4002 100644 --- a/src/schema_validation/test_validate_metaschema.py +++ b/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py @@ -3,9 +3,9 @@ import pytest from jsonschema.exceptions import ValidationError -from .validate_schema import load_schema, derefence_schema, validate_schema +from tools.schemacode.bidsschematools.validate_schema import load_schema, derefence_schema, validate_schema -schema_path = Path(__file__).resolve().parent.parent / "schema" +schema_path = Path(__file__).resolve().parents[4] / "src" / "schema" def test_load_schema(): diff --git a/src/schema_validation/validate_schema.py b/tools/schemacode/bidsschematools/validate_schema.py similarity index 100% rename from src/schema_validation/validate_schema.py rename to tools/schemacode/bidsschematools/validate_schema.py From 53a2ef3f0825c16e8236adbee21c7f1294a399fa Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 20:00:16 +0000 Subject: [PATCH 26/52] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../tests/test_validate_metaschema.py | 19 ++++++++++++++----- .../bidsschematools/validate_schema.py | 1 - 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py b/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py index 8969ef80be..dfe2d4c48a 100644 --- a/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py +++ b/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py @@ -2,8 +2,11 @@ import pytest from jsonschema.exceptions import ValidationError - -from tools.schemacode.bidsschematools.validate_schema import load_schema, derefence_schema, validate_schema +from tools.schemacode.bidsschematools.validate_schema import ( + derefence_schema, + load_schema, + validate_schema, +) schema_path = Path(__file__).resolve().parents[4] / "src" / "schema" @@ -28,7 +31,9 @@ def test_valid_schema(): def test_broken_reference(): """Test that a broken reference raises an error.""" schema = load_schema(schema_path) - schema["rules"]["files"]["deriv"]["preprocessed_data"]["anat_nonparametric_common"]["$ref"] = "broken.reference" + schema["rules"]["files"]["deriv"]["preprocessed_data"]["anat_nonparametric_common"][ + "$ref" + ] = "broken.reference" with pytest.raises(KeyError) as e: derefence_schema(schema) @@ -38,14 +43,18 @@ def test_broken_reference(): def test_add_legal_field(): """Test that adding a legal field does not raise an error.""" schema = load_schema(schema_path) - schema["rules"]["files"]["deriv"]["preprocessed_data"]["anat_nonparametric_common"]["entities"]["density"] = "optional" + schema["rules"]["files"]["deriv"]["preprocessed_data"]["anat_nonparametric_common"][ + "entities" + ]["density"] = "optional" validate_schema(schema) def test_invalid_value(): """Test that an invalid value raises an error.""" schema = load_schema(schema_path) - schema["rules"]["files"]["deriv"]["preprocessed_data"]["anat_nonparametric_common"]["entities"]["density"] = "invalid" + schema["rules"]["files"]["deriv"]["preprocessed_data"]["anat_nonparametric_common"][ + "entities" + ]["density"] = "invalid" with pytest.raises(ValidationError) as e: validate_schema(schema) diff --git a/tools/schemacode/bidsschematools/validate_schema.py b/tools/schemacode/bidsschematools/validate_schema.py index 674585ae20..8456b26e6c 100644 --- a/tools/schemacode/bidsschematools/validate_schema.py +++ b/tools/schemacode/bidsschematools/validate_schema.py @@ -3,7 +3,6 @@ from typing import Any, Dict, Union import yaml - from jsonschema.exceptions import ValidationError from jsonschema.validators import validate From e3bf5c022bc8ba9ea30725a2d2dc6e9e71351d75 Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Tue, 16 Apr 2024 19:24:03 -0400 Subject: [PATCH 27/52] Update tools/schemacode/bidsschematools/validate_schema.py Co-authored-by: Taylor Salo --- tools/schemacode/bidsschematools/validate_schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/schemacode/bidsschematools/validate_schema.py b/tools/schemacode/bidsschematools/validate_schema.py index 8456b26e6c..8618f54b33 100644 --- a/tools/schemacode/bidsschematools/validate_schema.py +++ b/tools/schemacode/bidsschematools/validate_schema.py @@ -23,7 +23,7 @@ def load_schema(schema_path: Union[str, Path]) -> Union[Dict[str, Any], str]: return f.read() -def derefence_schema(schema): +def dereference_schema(schema): """Dereference a schema by replacing $ref with the actual schema it points to""" def _dereference_obj(obj): From b50e677d8a1a0c3779f3c568683adad588292598 Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Tue, 16 Apr 2024 18:25:00 -0500 Subject: [PATCH 28/52] fix typo --- .../bidsschematools/tests/test_validate_metaschema.py | 6 +++--- tools/schemacode/bidsschematools/validate_schema.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py b/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py index dfe2d4c48a..00906c1744 100644 --- a/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py +++ b/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py @@ -3,7 +3,7 @@ import pytest from jsonschema.exceptions import ValidationError from tools.schemacode.bidsschematools.validate_schema import ( - derefence_schema, + dereference_schema, load_schema, validate_schema, ) @@ -19,7 +19,7 @@ def test_load_schema(): def test_derefence_schema(): """Test that a valid schema does not raise an error.""" schema = load_schema(schema_path) - derefence_schema(schema) + dereference_schema(schema) def test_valid_schema(): @@ -35,7 +35,7 @@ def test_broken_reference(): "$ref" ] = "broken.reference" with pytest.raises(KeyError) as e: - derefence_schema(schema) + dereference_schema(schema) assert "'broken'" in str(e.value) diff --git a/tools/schemacode/bidsschematools/validate_schema.py b/tools/schemacode/bidsschematools/validate_schema.py index 8618f54b33..5674ea1b88 100644 --- a/tools/schemacode/bidsschematools/validate_schema.py +++ b/tools/schemacode/bidsschematools/validate_schema.py @@ -50,7 +50,7 @@ def _dereference_obj(obj): def validate_schema(schema: dict): - schema = derefence_schema(schema) + schema = dereference_schema(schema) with open("metaschema.json", "r") as f: metaschema = json.load(f) From 7ddcea6383651837873e1868e16f41cfc1b15f6b Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Tue, 16 Apr 2024 18:29:41 -0500 Subject: [PATCH 29/52] add jsonschema to deps --- tools/schemacode/setup.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/schemacode/setup.cfg b/tools/schemacode/setup.cfg index 27159dee84..98e4e9f9c4 100644 --- a/tools/schemacode/setup.cfg +++ b/tools/schemacode/setup.cfg @@ -24,6 +24,7 @@ install_requires = click pyyaml importlib_resources; python_version < "3.9" + jsonschema packages = find: include_package_data = false zip_safe = false From fb9facc4a37d7e369dda8762503d01c9974a1fc6 Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Tue, 16 Apr 2024 18:36:49 -0500 Subject: [PATCH 30/52] fixing up checks --- .../bidsschematools/tests/test_validate_metaschema.py | 2 +- tools/schemacode/bidsschematools/validate_schema.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py b/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py index 00906c1744..e02ea68d53 100644 --- a/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py +++ b/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py @@ -2,7 +2,7 @@ import pytest from jsonschema.exceptions import ValidationError -from tools.schemacode.bidsschematools.validate_schema import ( +from bidsschematools.validate_schema import ( dereference_schema, load_schema, validate_schema, diff --git a/tools/schemacode/bidsschematools/validate_schema.py b/tools/schemacode/bidsschematools/validate_schema.py index 5674ea1b88..2fa1b229cd 100644 --- a/tools/schemacode/bidsschematools/validate_schema.py +++ b/tools/schemacode/bidsschematools/validate_schema.py @@ -23,7 +23,7 @@ def load_schema(schema_path: Union[str, Path]) -> Union[Dict[str, Any], str]: return f.read() -def dereference_schema(schema): +def dereference_schema(schema: dict): """Dereference a schema by replacing $ref with the actual schema it points to""" def _dereference_obj(obj): From cfcff31e88bd1db13a2a5e047f756f2fa47b7eb6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 23:37:12 +0000 Subject: [PATCH 31/52] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../schemacode/bidsschematools/tests/test_validate_metaschema.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py b/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py index e02ea68d53..8c2da6a783 100644 --- a/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py +++ b/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py @@ -2,6 +2,7 @@ import pytest from jsonschema.exceptions import ValidationError + from bidsschematools.validate_schema import ( dereference_schema, load_schema, From 5fdb2532ed44201fb8b498c485feeb2004f45077 Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Tue, 16 Apr 2024 18:40:32 -0500 Subject: [PATCH 32/52] install jsonschema types --- .pre-commit-config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fcf475045e..0e2da338cb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -62,5 +62,6 @@ repos: - pytest - types-PyYAML - types-tabulate + - types-jsonschema args: ["tools/schemacode/bidsschematools"] pass_filenames: false From 5b860054226abb2c73f8d0e8cf91454cfbed5d68 Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Tue, 16 Apr 2024 18:55:15 -0500 Subject: [PATCH 33/52] fix pathing --- .../bidsschematools/tests/test_validate_metaschema.py | 3 +-- tools/schemacode/bidsschematools/validate_schema.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py b/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py index 8c2da6a783..dd8ee455c6 100644 --- a/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py +++ b/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py @@ -7,10 +7,9 @@ dereference_schema, load_schema, validate_schema, + schema_path ) -schema_path = Path(__file__).resolve().parents[4] / "src" / "schema" - def test_load_schema(): """Test that a valid schema does not raise an error.""" diff --git a/tools/schemacode/bidsschematools/validate_schema.py b/tools/schemacode/bidsschematools/validate_schema.py index 2fa1b229cd..b213bdda7e 100644 --- a/tools/schemacode/bidsschematools/validate_schema.py +++ b/tools/schemacode/bidsschematools/validate_schema.py @@ -6,7 +6,7 @@ from jsonschema.exceptions import ValidationError from jsonschema.validators import validate -schema_path = Path(__file__).resolve().parent.parent / "schema" +schema_path = Path(__file__).resolve().parents[3] / "src" / "schema" def load_schema(schema_path: Union[str, Path]) -> Union[Dict[str, Any], str]: From 6f7659ee22c3774bd87b52112a0cb0e6424f5a23 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 23:55:37 +0000 Subject: [PATCH 34/52] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../bidsschematools/tests/test_validate_metaschema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py b/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py index dd8ee455c6..5e7e9437be 100644 --- a/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py +++ b/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py @@ -6,8 +6,8 @@ from bidsschematools.validate_schema import ( dereference_schema, load_schema, + schema_path, validate_schema, - schema_path ) From 4bb071d7eb7092400e7efcc9db0fb5a39755e8b2 Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Tue, 16 Apr 2024 18:56:38 -0500 Subject: [PATCH 35/52] flake8 --- .../bidsschematools/tests/test_validate_metaschema.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py b/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py index dd8ee455c6..058674ce2c 100644 --- a/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py +++ b/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py @@ -1,5 +1,3 @@ -from pathlib import Path - import pytest from jsonschema.exceptions import ValidationError From dc37e69fde2b72aa5226af5dc0a8e89926135563 Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Tue, 16 Apr 2024 19:16:19 -0500 Subject: [PATCH 36/52] try changing schema path --- tools/schemacode/bidsschematools/validate_schema.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/schemacode/bidsschematools/validate_schema.py b/tools/schemacode/bidsschematools/validate_schema.py index b213bdda7e..6da1d178c0 100644 --- a/tools/schemacode/bidsschematools/validate_schema.py +++ b/tools/schemacode/bidsschematools/validate_schema.py @@ -6,7 +6,8 @@ from jsonschema.exceptions import ValidationError from jsonschema.validators import validate -schema_path = Path(__file__).resolve().parents[3] / "src" / "schema" +#schema_path = Path(__file__).resolve().parents[3] / "src" / "schema" +schema_path = Path("src") / "schema" def load_schema(schema_path: Union[str, Path]) -> Union[Dict[str, Any], str]: From c5ab64c895fd982888a1f01f3eea7b8a2c553567 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 17 Apr 2024 00:17:36 +0000 Subject: [PATCH 37/52] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tools/schemacode/bidsschematools/validate_schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/schemacode/bidsschematools/validate_schema.py b/tools/schemacode/bidsschematools/validate_schema.py index 6da1d178c0..d18da950b5 100644 --- a/tools/schemacode/bidsschematools/validate_schema.py +++ b/tools/schemacode/bidsschematools/validate_schema.py @@ -6,7 +6,7 @@ from jsonschema.exceptions import ValidationError from jsonschema.validators import validate -#schema_path = Path(__file__).resolve().parents[3] / "src" / "schema" +# schema_path = Path(__file__).resolve().parents[3] / "src" / "schema" schema_path = Path("src") / "schema" From aae4253281b20d858f826324f516a6ae93d29f3d Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Tue, 16 Apr 2024 19:20:59 -0500 Subject: [PATCH 38/52] fix paths --- tools/schemacode/bidsschematools/validate_schema.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/schemacode/bidsschematools/validate_schema.py b/tools/schemacode/bidsschematools/validate_schema.py index 6da1d178c0..e9db52e560 100644 --- a/tools/schemacode/bidsschematools/validate_schema.py +++ b/tools/schemacode/bidsschematools/validate_schema.py @@ -6,8 +6,8 @@ from jsonschema.exceptions import ValidationError from jsonschema.validators import validate -#schema_path = Path(__file__).resolve().parents[3] / "src" / "schema" schema_path = Path("src") / "schema" +metaschema_path = Path("src") / "metaschema.json" def load_schema(schema_path: Union[str, Path]) -> Union[Dict[str, Any], str]: @@ -52,7 +52,7 @@ def _dereference_obj(obj): def validate_schema(schema: dict): schema = dereference_schema(schema) - with open("metaschema.json", "r") as f: + with open(metaschema_path, "r") as f: metaschema = json.load(f) # validate is put in this try/except clause because the error is sometimes too long to From a9911d56d9051536e033abadd496c1f8d208507b Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Tue, 16 Apr 2024 19:46:39 -0500 Subject: [PATCH 39/52] use existing functions --- .../tests/test_validate_metaschema.py | 37 ++------------- .../bidsschematools/validate_schema.py | 46 +------------------ 2 files changed, 7 insertions(+), 76 deletions(-) diff --git a/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py b/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py index 18acdf49f0..c577a0d071 100644 --- a/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py +++ b/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py @@ -1,46 +1,19 @@ import pytest from jsonschema.exceptions import ValidationError -from bidsschematools.validate_schema import ( - dereference_schema, - load_schema, - schema_path, - validate_schema, -) - - -def test_load_schema(): - """Test that a valid schema does not raise an error.""" - load_schema(schema_path) - - -def test_derefence_schema(): - """Test that a valid schema does not raise an error.""" - schema = load_schema(schema_path) - dereference_schema(schema) +from bidsschematools.schema import load_schema +from bidsschematools.validate_schema import validate_schema def test_valid_schema(): """Test that a valid schema does not raise an error.""" - schema = load_schema(schema_path) + schema = load_schema() validate_schema(schema) -def test_broken_reference(): - """Test that a broken reference raises an error.""" - schema = load_schema(schema_path) - schema["rules"]["files"]["deriv"]["preprocessed_data"]["anat_nonparametric_common"][ - "$ref" - ] = "broken.reference" - with pytest.raises(KeyError) as e: - dereference_schema(schema) - - assert "'broken'" in str(e.value) - - def test_add_legal_field(): """Test that adding a legal field does not raise an error.""" - schema = load_schema(schema_path) + schema = load_schema() schema["rules"]["files"]["deriv"]["preprocessed_data"]["anat_nonparametric_common"][ "entities" ]["density"] = "optional" @@ -49,7 +22,7 @@ def test_add_legal_field(): def test_invalid_value(): """Test that an invalid value raises an error.""" - schema = load_schema(schema_path) + schema = load_schema() schema["rules"]["files"]["deriv"]["preprocessed_data"]["anat_nonparametric_common"][ "entities" ]["density"] = "invalid" diff --git a/tools/schemacode/bidsschematools/validate_schema.py b/tools/schemacode/bidsschematools/validate_schema.py index e9db52e560..19ff83cb56 100644 --- a/tools/schemacode/bidsschematools/validate_schema.py +++ b/tools/schemacode/bidsschematools/validate_schema.py @@ -1,57 +1,15 @@ import json from pathlib import Path -from typing import Any, Dict, Union -import yaml from jsonschema.exceptions import ValidationError from jsonschema.validators import validate -schema_path = Path("src") / "schema" -metaschema_path = Path("src") / "metaschema.json" - - -def load_schema(schema_path: Union[str, Path]) -> Union[Dict[str, Any], str]: - """Load a schema from a file or directory""" - if Path(schema_path).is_dir(): - return {f.stem: load_schema(f) for f in Path(schema_path).iterdir()} - elif Path(schema_path).is_file() and ( - str(schema_path).endswith(".yaml") or str(schema_path).endswith(".yml") - ): - with open(schema_path, "r") as f: - return yaml.safe_load(f) - else: - with open(schema_path, "r") as f: - return f.read() - +from .schema import load_schema -def dereference_schema(schema: dict): - """Dereference a schema by replacing $ref with the actual schema it points to""" - - def _dereference_obj(obj): - if isinstance(obj, dict): - out = dict() - for k, v in obj.items(): - if k == "$ref": - address = v.split(".") - here = schema - for part in address: - here = here[part] - if isinstance(here, dict): - out.update(_dereference_obj(here)) - else: - return _dereference_obj(here) - else: - out[k] = _dereference_obj(v) - return out - if isinstance(obj, list): - return [_dereference_obj(x) for x in obj] - return obj - - return _dereference_obj(schema) +metaschema_path = Path("src") / "metaschema.json" def validate_schema(schema: dict): - schema = dereference_schema(schema) with open(metaschema_path, "r") as f: metaschema = json.load(f) From 45092863849f5ed7ce01c54f180d43d39f7a60f2 Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Tue, 16 Apr 2024 19:56:17 -0500 Subject: [PATCH 40/52] convert from namespace --- .../schemacode/bidsschematools/validate_schema.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/tools/schemacode/bidsschematools/validate_schema.py b/tools/schemacode/bidsschematools/validate_schema.py index 19ff83cb56..430b6cb4db 100644 --- a/tools/schemacode/bidsschematools/validate_schema.py +++ b/tools/schemacode/bidsschematools/validate_schema.py @@ -4,29 +4,19 @@ from jsonschema.exceptions import ValidationError from jsonschema.validators import validate -from .schema import load_schema metaschema_path = Path("src") / "metaschema.json" -def validate_schema(schema: dict): +def validate_schema(schema): with open(metaschema_path, "r") as f: metaschema = json.load(f) # validate is put in this try/except clause because the error is sometimes too long to # print in the terminal try: - validate(instance=schema, schema=metaschema) + validate(instance=schema.to_dict(), schema=metaschema) except ValidationError as e: with open("error_log.txt", "w") as file: file.write(str(e)) raise e - - -def main(): - schema = load_schema(schema_path) - validate_schema(schema) - - -if __name__ == "__main__": - main() From 5bd4ec36c070d02dce3d264f61705401ce92c7b1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 17 Apr 2024 00:56:39 +0000 Subject: [PATCH 41/52] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tools/schemacode/bidsschematools/validate_schema.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/schemacode/bidsschematools/validate_schema.py b/tools/schemacode/bidsschematools/validate_schema.py index 430b6cb4db..89718fe1a6 100644 --- a/tools/schemacode/bidsschematools/validate_schema.py +++ b/tools/schemacode/bidsschematools/validate_schema.py @@ -4,7 +4,6 @@ from jsonschema.exceptions import ValidationError from jsonschema.validators import validate - metaschema_path = Path("src") / "metaschema.json" From 737081d32e3556b72b47f115065a8ca37aba2e04 Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Tue, 16 Apr 2024 20:02:44 -0500 Subject: [PATCH 42/52] update fields for existing loader --- src/metaschema.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/metaschema.json b/src/metaschema.json index 8426daf2be..b21e712422 100644 --- a/src/metaschema.json +++ b/src/metaschema.json @@ -604,10 +604,10 @@ ], "additionalProperties": false }, - "BIDS_VERSION": { + "bids_version": { "type": "string" }, - "SCHEMA_VERSION": { + "schema_version": { "type": "string" }, "README": { From dc121e06f794bcc342f7adab504f73c88e772250 Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Tue, 16 Apr 2024 20:09:32 -0500 Subject: [PATCH 43/52] fix required props --- src/metaschema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/metaschema.json b/src/metaschema.json index b21e712422..e65ab084fe 100644 --- a/src/metaschema.json +++ b/src/metaschema.json @@ -614,7 +614,7 @@ "type": "string" } }, - "required": ["meta", "objects", "rules", "BIDS_VERSION", "SCHEMA_VERSION"], + "required": ["meta", "objects", "rules", "bids_version", "schema_version"], "additionalProperties": false, "definitions": { "entities": { From e74ad1d96e2f6da154191c5288a11387ce7c0a16 Mon Sep 17 00:00:00 2001 From: Chris Markiewicz Date: Thu, 18 Apr 2024 11:14:54 -0400 Subject: [PATCH 44/52] ENH: Link metaschema into package data --- tools/schemacode/bidsschematools/data/metaschema.json | 1 + tools/schemacode/setup.cfg | 1 + 2 files changed, 2 insertions(+) create mode 120000 tools/schemacode/bidsschematools/data/metaschema.json diff --git a/tools/schemacode/bidsschematools/data/metaschema.json b/tools/schemacode/bidsschematools/data/metaschema.json new file mode 120000 index 0000000000..ae3cfc38f9 --- /dev/null +++ b/tools/schemacode/bidsschematools/data/metaschema.json @@ -0,0 +1 @@ +../../../../src/metaschema.json \ No newline at end of file diff --git a/tools/schemacode/setup.cfg b/tools/schemacode/setup.cfg index 98e4e9f9c4..f75732b593 100644 --- a/tools/schemacode/setup.cfg +++ b/tools/schemacode/setup.cfg @@ -55,6 +55,7 @@ all = [options.package_data] bidsschematools = + data/metaschema.json data/schema/BIDS_VERSION data/schema/SCHEMA_VERSION data/schema/**/*.yaml From e3dd7199c10abfc476b95d8af4657ba5dc704714 Mon Sep 17 00:00:00 2001 From: Chris Markiewicz Date: Thu, 18 Apr 2024 11:26:15 -0400 Subject: [PATCH 45/52] RF: Use package data instead of relying on CWD --- tools/schemacode/bidsschematools/validate_schema.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tools/schemacode/bidsschematools/validate_schema.py b/tools/schemacode/bidsschematools/validate_schema.py index 89718fe1a6..521a8b72ab 100644 --- a/tools/schemacode/bidsschematools/validate_schema.py +++ b/tools/schemacode/bidsschematools/validate_schema.py @@ -1,15 +1,12 @@ import json -from pathlib import Path +from importlib.resources import files from jsonschema.exceptions import ValidationError from jsonschema.validators import validate -metaschema_path = Path("src") / "metaschema.json" - def validate_schema(schema): - with open(metaschema_path, "r") as f: - metaschema = json.load(f) + metaschema = json.loads(files("bidsschematools.data").joinpath("metaschema.json").read_text()) # validate is put in this try/except clause because the error is sometimes too long to # print in the terminal From 24deed4f0c3e0edc87f0582d41437e648823b4f3 Mon Sep 17 00:00:00 2001 From: Chris Markiewicz Date: Thu, 18 Apr 2024 11:28:00 -0400 Subject: [PATCH 46/52] STY: Import from top-level jsonschema --- tools/schemacode/bidsschematools/validate_schema.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/schemacode/bidsschematools/validate_schema.py b/tools/schemacode/bidsschematools/validate_schema.py index 521a8b72ab..b04c48f0d3 100644 --- a/tools/schemacode/bidsschematools/validate_schema.py +++ b/tools/schemacode/bidsschematools/validate_schema.py @@ -1,8 +1,7 @@ import json from importlib.resources import files -from jsonschema.exceptions import ValidationError -from jsonschema.validators import validate +from jsonschema import ValidationError, validate def validate_schema(schema): From ca266e5bae369151acfc62fb557023935840cb18 Mon Sep 17 00:00:00 2001 From: Chris Markiewicz Date: Thu, 18 Apr 2024 11:48:07 -0400 Subject: [PATCH 47/52] PY38: importlib_resources --- tools/schemacode/bidsschematools/validate_schema.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/schemacode/bidsschematools/validate_schema.py b/tools/schemacode/bidsschematools/validate_schema.py index b04c48f0d3..16abdf6f38 100644 --- a/tools/schemacode/bidsschematools/validate_schema.py +++ b/tools/schemacode/bidsschematools/validate_schema.py @@ -1,5 +1,10 @@ import json -from importlib.resources import files +import sys + +if sys.version_info < (3, 9): + from importlib_resources import files +else: + from importlib.resources import files from jsonschema import ValidationError, validate From fccbf3bf89e57a9e36deea5bc44fdf10169f8402 Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Thu, 18 Apr 2024 11:02:33 -0500 Subject: [PATCH 48/52] use "deprecated" instead of "DEPRECATED" --- src/metaschema.json | 8 ++++---- src/schema/objects/common_principles.yaml | 2 +- .../rules/sidecars/derivatives/common_derivatives.yaml | 4 ++-- src/schema/rules/sidecars/ieeg.yaml | 2 +- src/schema/rules/sidecars/mri.yaml | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/metaschema.json b/src/metaschema.json index e65ab084fe..129e3f1c5d 100644 --- a/src/metaschema.json +++ b/src/metaschema.json @@ -654,11 +654,11 @@ "patternProperties": { "^[a-zA-Z0-9_]+$": { "anyOf": [ - {"enum": ["recommended", "optional", "required", "DEPRECATED"]}, + {"enum": ["recommended", "optional", "required", "deprecated"]}, { "type": "object", "properties": { - "level": {"enum": ["recommended", "optional", "required", "DEPRECATED"]}, + "level": {"enum": ["recommended", "optional", "required", "deprecated"]}, "level_addendum": {"type": "string"} }, "required": ["level", "level_addendum"], @@ -692,11 +692,11 @@ "patternProperties": { "^[a-zA-Z0-9_]+$": { "anyOf": [ - {"enum": ["recommended", "optional", "required", "DEPRECATED"]}, + {"enum": ["recommended", "optional", "required", "deprecated"]}, { "type": "object", "properties": { - "level": {"enum": ["recommended", "optional", "required", "DEPRECATED"]}, + "level": {"enum": ["recommended", "optional", "required", "deprecated"]}, "level_addendum": {"type": "string"}, "description_addendum": {"type": "string"} }, diff --git a/src/schema/objects/common_principles.yaml b/src/schema/objects/common_principles.yaml index 7e669efb52..2a07784ad3 100644 --- a/src/schema/objects/common_principles.yaml +++ b/src/schema/objects/common_principles.yaml @@ -47,7 +47,7 @@ dataset: A set of neuroimaging and behavioral data acquired for a purpose of a particular study. A dataset consists of data acquired from one or more subjects, possibly from multiple sessions. deprecated: - display_name: DEPRECATED + display_name: deprecated description: | A "deprecated" entity or metadata field SHOULD NOT be used in the generation of new datasets. It remains in the standard in order to preserve the interpretability of existing datasets. diff --git a/src/schema/rules/sidecars/derivatives/common_derivatives.yaml b/src/schema/rules/sidecars/derivatives/common_derivatives.yaml index a550ca2b79..fbc05937bc 100644 --- a/src/schema/rules/sidecars/derivatives/common_derivatives.yaml +++ b/src/schema/rules/sidecars/derivatives/common_derivatives.yaml @@ -7,7 +7,7 @@ CommonDerivativeFields: level: recommended description_addendum: This describes the nature of the file. Sources: optional - RawSources: DEPRECATED + RawSources: deprecated SpatialReferenceEntity: selectors: @@ -36,7 +36,7 @@ MaskDerivatives: fields: Type: recommended Sources: recommended - RawSources: DEPRECATED + RawSources: deprecated SegmentationCommon: selectors: diff --git a/src/schema/rules/sidecars/ieeg.yaml b/src/schema/rules/sidecars/ieeg.yaml index 7d2655a1d7..066e17a8bf 100644 --- a/src/schema/rules/sidecars/ieeg.yaml +++ b/src/schema/rules/sidecars/ieeg.yaml @@ -66,7 +66,7 @@ iEEGRecommended: - datatype == "ieeg" - suffix == "ieeg" fields: - DCOffsetCorrection: DEPRECATED + DCOffsetCorrection: deprecated HardwareFilters: recommended ElectrodeManufacturer: recommended ElectrodeManufacturersModelName: recommended diff --git a/src/schema/rules/sidecars/mri.yaml b/src/schema/rules/sidecars/mri.yaml index b937898b3d..cc27d676be 100644 --- a/src/schema/rules/sidecars/mri.yaml +++ b/src/schema/rules/sidecars/mri.yaml @@ -25,7 +25,7 @@ MRIHardware: SoftwareVersions: level: recommended description_addendum: Corresponds to DICOM Tag 0018, 1020 `Software Versions`. - HardcopyDeviceSoftwareVersion: DEPRECATED + HardcopyDeviceSoftwareVersion: deprecated MagneticFieldStrength: level: recommended, but required for Arterial Spin Labeling ReceiveCoilName: recommended From 453e5ba93962bdad532247bd4d5f7a1c56ea990b Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Thu, 18 Apr 2024 11:17:34 -0500 Subject: [PATCH 49/52] move to schema.py and test_schema.py --- tools/schemacode/bidsschematools/schema.py | 29 +++++++++++++++++ .../bidsschematools/tests/test_schema.py | 29 ++++++++++++++++- .../tests/test_validate_metaschema.py | 32 ------------------- .../bidsschematools/validate_schema.py | 22 ------------- 4 files changed, 57 insertions(+), 55 deletions(-) delete mode 100644 tools/schemacode/bidsschematools/tests/test_validate_metaschema.py delete mode 100644 tools/schemacode/bidsschematools/validate_schema.py diff --git a/tools/schemacode/bidsschematools/schema.py b/tools/schemacode/bidsschematools/schema.py index cae15fece1..cc40a5e6f7 100644 --- a/tools/schemacode/bidsschematools/schema.py +++ b/tools/schemacode/bidsschematools/schema.py @@ -1,12 +1,22 @@ """Schema loading- and processing-related functions.""" +import json import logging import os import re +import sys +import tempfile from collections.abc import Iterable, Mapping from copy import deepcopy from functools import lru_cache +from jsonschema import ValidationError, validate + +if sys.version_info < (3, 9): + from importlib_resources import files +else: + from importlib.resources import files + from . import __bids_version__, __version__, utils from .types import Namespace @@ -274,3 +284,22 @@ def filter_schema(schema, **kwargs): if isinstance(item, dict): new_schema[i] = filter_schema(item, **kwargs) return new_schema + + +def validate_schema(schema: Namespace): + """Validate a schema against the BIDS metaschema.""" + metaschema = json.loads(files("bidsschematools.data").joinpath("metaschema.json").read_text()) + + # validate is put in this try/except clause because the error is sometimes too long to + # print in the terminal + try: + validate(instance=schema.to_dict(), schema=metaschema) + except ValidationError as e: + with tempfile.NamedTemporaryFile( + prefix="schema_error_", suffix=".txt", delete=False, mode="w+" + ) as file: + file.write(str(e)) + # ValidationError does not have an add_note method yet + # e.add_note(f"See {file.name} for full error log.") + raise e + diff --git a/tools/schemacode/bidsschematools/tests/test_schema.py b/tools/schemacode/bidsschematools/tests/test_schema.py index bd5fc28942..1fc94db568 100644 --- a/tools/schemacode/bidsschematools/tests/test_schema.py +++ b/tools/schemacode/bidsschematools/tests/test_schema.py @@ -4,8 +4,8 @@ from collections.abc import Mapping import pytest - from bidsschematools import __bids_version__, schema, types +from jsonschema.exceptions import ValidationError from ..data import load_resource @@ -356,3 +356,30 @@ def check_for_namespaces(obj): raise ValueError("Namespace object found in dict") check_for_namespaces(schema.load_schema().to_dict()) + + +def test_valid_schema(): + """Test that a valid schema does not raise an error.""" + namespace = schema.load_schema() + schema.validate_schema(namespace) + + +def test_add_legal_field(): + """Test that adding a legal field does not raise an error.""" + namespace = schema.load_schema() + namespace["rules"]["files"]["deriv"]["preprocessed_data"]["anat_nonparametric_common"][ + "entities" + ]["density"] = "optional" + schema.validate_schema(namespace) + + +def test_invalid_value(): + """Test that an invalid value raises an error.""" + namespace = schema.load_schema() + namespace["rules"]["files"]["deriv"]["preprocessed_data"]["anat_nonparametric_common"][ + "entities" + ]["density"] = "invalid" + with pytest.raises(ValidationError) as e: + schema.validate_schema(namespace) + print(e.value) + assert "invalid" in str(e.value) diff --git a/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py b/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py deleted file mode 100644 index c577a0d071..0000000000 --- a/tools/schemacode/bidsschematools/tests/test_validate_metaschema.py +++ /dev/null @@ -1,32 +0,0 @@ -import pytest -from jsonschema.exceptions import ValidationError - -from bidsschematools.schema import load_schema -from bidsschematools.validate_schema import validate_schema - - -def test_valid_schema(): - """Test that a valid schema does not raise an error.""" - schema = load_schema() - validate_schema(schema) - - -def test_add_legal_field(): - """Test that adding a legal field does not raise an error.""" - schema = load_schema() - schema["rules"]["files"]["deriv"]["preprocessed_data"]["anat_nonparametric_common"][ - "entities" - ]["density"] = "optional" - validate_schema(schema) - - -def test_invalid_value(): - """Test that an invalid value raises an error.""" - schema = load_schema() - schema["rules"]["files"]["deriv"]["preprocessed_data"]["anat_nonparametric_common"][ - "entities" - ]["density"] = "invalid" - with pytest.raises(ValidationError) as e: - validate_schema(schema) - - assert "invalid" in str(e.value) diff --git a/tools/schemacode/bidsschematools/validate_schema.py b/tools/schemacode/bidsschematools/validate_schema.py deleted file mode 100644 index 16abdf6f38..0000000000 --- a/tools/schemacode/bidsschematools/validate_schema.py +++ /dev/null @@ -1,22 +0,0 @@ -import json -import sys - -if sys.version_info < (3, 9): - from importlib_resources import files -else: - from importlib.resources import files - -from jsonschema import ValidationError, validate - - -def validate_schema(schema): - metaschema = json.loads(files("bidsschematools.data").joinpath("metaschema.json").read_text()) - - # validate is put in this try/except clause because the error is sometimes too long to - # print in the terminal - try: - validate(instance=schema.to_dict(), schema=metaschema) - except ValidationError as e: - with open("error_log.txt", "w") as file: - file.write(str(e)) - raise e From bdf6baf1ddf2ee39bb3b8a4fc2ced1e78603e463 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 18 Apr 2024 16:18:01 +0000 Subject: [PATCH 50/52] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tools/schemacode/bidsschematools/schema.py | 3 +-- tools/schemacode/bidsschematools/tests/test_schema.py | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/schemacode/bidsschematools/schema.py b/tools/schemacode/bidsschematools/schema.py index cc40a5e6f7..ed6b5da5f3 100644 --- a/tools/schemacode/bidsschematools/schema.py +++ b/tools/schemacode/bidsschematools/schema.py @@ -296,10 +296,9 @@ def validate_schema(schema: Namespace): validate(instance=schema.to_dict(), schema=metaschema) except ValidationError as e: with tempfile.NamedTemporaryFile( - prefix="schema_error_", suffix=".txt", delete=False, mode="w+" + prefix="schema_error_", suffix=".txt", delete=False, mode="w+" ) as file: file.write(str(e)) # ValidationError does not have an add_note method yet # e.add_note(f"See {file.name} for full error log.") raise e - diff --git a/tools/schemacode/bidsschematools/tests/test_schema.py b/tools/schemacode/bidsschematools/tests/test_schema.py index 1fc94db568..6c52f0c2c1 100644 --- a/tools/schemacode/bidsschematools/tests/test_schema.py +++ b/tools/schemacode/bidsschematools/tests/test_schema.py @@ -4,9 +4,10 @@ from collections.abc import Mapping import pytest -from bidsschematools import __bids_version__, schema, types from jsonschema.exceptions import ValidationError +from bidsschematools import __bids_version__, schema, types + from ..data import load_resource From 5465f429afa139258112d5a4fa179e378a61e82d Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Thu, 18 Apr 2024 12:18:30 -0400 Subject: [PATCH 51/52] Update src/schema/objects/common_principles.yaml --- src/schema/objects/common_principles.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/schema/objects/common_principles.yaml b/src/schema/objects/common_principles.yaml index 2a07784ad3..7e669efb52 100644 --- a/src/schema/objects/common_principles.yaml +++ b/src/schema/objects/common_principles.yaml @@ -47,7 +47,7 @@ dataset: A set of neuroimaging and behavioral data acquired for a purpose of a particular study. A dataset consists of data acquired from one or more subjects, possibly from multiple sessions. deprecated: - display_name: deprecated + display_name: DEPRECATED description: | A "deprecated" entity or metadata field SHOULD NOT be used in the generation of new datasets. It remains in the standard in order to preserve the interpretability of existing datasets. From 96e6bab8c1eb1c53095c5b65529127beb4a20ee4 Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Thu, 18 Apr 2024 12:42:15 -0400 Subject: [PATCH 52/52] Update README.md --- src/schema/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/schema/README.md b/src/schema/README.md index d801314595..e8cb197757 100644 --- a/src/schema/README.md +++ b/src/schema/README.md @@ -1023,3 +1023,11 @@ be found at . The JSON version of the schema contains `schema_version` and `bids_version` keys that identify the state of both the schema and the specification at the time it was compiled. + +## Metaschema + +The `metaschema.json` file is a meta-schema that uses the JSON Schema language to +formalize the allowable directories, files, fields and values of the BIDS schema, +ensuring consistency across the entire schema directory. Validation of the schema is +incorporated into the CI, so any changes that are inconsistent will be flagged before +inclusion.