diff --git a/src/schema/objects/formats.yaml b/src/schema/objects/formats.yaml index 7de4345b2d..e518936e2b 100644 --- a/src/schema/objects/formats.yaml +++ b/src/schema/objects/formats.yaml @@ -70,9 +70,18 @@ datetime: display_name: Datetime description: | A datetime in the form `"YYYY-MM-DDThh:mm:ss[.000000][Z]"`, - where [.000000] is an optional subsecond resolution between 1 and 6 decimal points, - and [Z] is an optional, valid timezone code. - pattern: '[0-9]{4}-[0-9]{2}-[0-9]{2}T(?:2[0-3]|[01][0-9]):[0-5][0-9]:[0-5][0-9](\.[0-9]{1,6})?([A-Z]{2,4})?' + where `[.000000]` is an optional subsecond resolution between 1 and 6 decimal points, + and `[Z]` is an optional literal character `Z` that indicates + Coordinated Universal Time (UTC). + pattern: "\ + [0-9]{4}\ + -(?:0[1-9]|1[0-2])\ + -(?:0[1-9]|[12][0-9]|3[01])\ + T(?:2[0-3]|[01][0-9])\ + :[0-5][0-9]\ + :(?:[0-5][0-9]|60)\ + (?:\\.[0-9]{1,6})?\ + Z?" file_relative: display_name: Path relative to the parent file description: | diff --git a/src/schema/rules/files/deriv/preprocessed_data.yaml b/src/schema/rules/files/deriv/preprocessed_data.yaml index fb9da23f39..8398536da5 100644 --- a/src/schema/rules/files/deriv/preprocessed_data.yaml +++ b/src/schema/rules/files/deriv/preprocessed_data.yaml @@ -69,6 +69,54 @@ beh_noncontinuous_common: space: optional description: optional +channels_channels_common: + $ref: rules.files.raw.channels.channels + entities: + $ref: rules.files.raw.channels.channels.entities + description: optional + +channels_channels__meg_common: + $ref: rules.files.raw.channels.channels__meg + entities: + $ref: rules.files.raw.channels.channels__meg.entities + description: optional + +channels_channels__motion_common: + $ref: rules.files.raw.channels.channels__motion + entities: + $ref: rules.files.raw.channels.channels__motion.entities + description: optional + +channels_coordsystem_common: + $ref: rules.files.raw.channels.coordsystem + entities: + $ref: rules.files.raw.channels.coordsystem.entities + description: optional + +channels_coordsystem__eeg_common: + $ref: rules.files.raw.channels.coordsystem__eeg + entities: + $ref: rules.files.raw.channels.coordsystem__eeg.entities + description: optional + +channels_electrodes_common: + $ref: rules.files.raw.channels.electrodes + entities: + $ref: rules.files.raw.channels.electrodes.entities + description: optional + +channels_electrodes__meg_common: + $ref: rules.files.raw.channels.electrodes__meg + entities: + $ref: rules.files.raw.channels.electrodes__meg.entities + description: optional + +channels_electrodes_optodes_common: + $ref: rules.files.raw.channels.optodes + entities: + $ref: rules.files.raw.channels.optodes.entities + description: optional + dwi_dwi_common: $ref: rules.files.raw.dwi.dwi entities: @@ -229,3 +277,9 @@ pet_blood_common: $ref: rules.files.raw.pet.blood.entities space: optional description: optional + +task_events_common: + $ref: rules.files.raw.task.events + entities: + $ref: rules.files.raw.task.events.entities + description: optional diff --git a/tools/schemacode/src/bidsschematools/schema.py b/tools/schemacode/src/bidsschematools/schema.py index ad6462f32c..a95b2696f4 100644 --- a/tools/schemacode/src/bidsschematools/schema.py +++ b/tools/schemacode/src/bidsschematools/schema.py @@ -90,6 +90,21 @@ def _find(obj, predicate): yield from _find(item, predicate) +def _dereference(namespace, base_schema): + # In-place, recursively dereference objects + # This allows a referenced object to itself contain a reference + # A dependency graph could be constructed, but would likely be slower + # to build than to duplicate a couple dereferences + for struct in _find(namespace, lambda obj: "$ref" in obj): + target = base_schema.get(struct["$ref"]) + if target is None: + raise ValueError(f"Reference {struct['$ref']} not found in schema.") + if isinstance(target, Mapping): + struct.pop("$ref") + _dereference(target, base_schema) + struct.update({**target, **struct}) + + def dereference(namespace, inplace=True): """Replace references in namespace with the contents of the referred object. @@ -109,11 +124,7 @@ def dereference(namespace, inplace=True): if not inplace: namespace = deepcopy(namespace) - for struct in _find(namespace, lambda obj: "$ref" in obj): - target = namespace.get(struct["$ref"]) - if isinstance(target, Mapping): - struct.pop("$ref") - struct.update({**target, **struct}) + _dereference(namespace, namespace) # At this point, any remaining refs are one-off objects in lists for struct in _find(namespace, lambda obj: any("$ref" in sub for sub in obj)): diff --git a/tools/schemacode/src/bidsschematools/tests/test_schema.py b/tools/schemacode/src/bidsschematools/tests/test_schema.py index 6c52f0c2c1..6b1449e13e 100644 --- a/tools/schemacode/src/bidsschematools/tests/test_schema.py +++ b/tools/schemacode/src/bidsschematools/tests/test_schema.py @@ -85,8 +85,8 @@ def test_formats(schema_obj): "2022-01-05T13:16:30", "2022-01-05T13:16:30.5", # subsecond resolution is allowed "2022-01-05T13:16:30.000005", # up to 6 decimal points - "2022-01-05T13:16:30UTC", # timezones are allowed - "2022-01-05T13:16:30.05UTC", + "2022-01-05T13:16:30Z", # UTC indicator is allowed + "2022-01-05T13:16:30.05Z", ], "time": [ "13:16:30", @@ -133,7 +133,7 @@ def test_formats(schema_obj): ], "datetime": [ "2022-01-05T13:16:30.1000005", # too many decimal points - "2022-01-05T13:16:30U", # time zone too short + "2022-01-05T13:16:30U", # Only Z is permitted "2022-01-05T13:16:30UTCUTC", # time zone too long "2022-01-05T34:10:10", # invalid time ],