From bfa7a0f1b0f37617c3dcb37ae9054a2ca218f63e Mon Sep 17 00:00:00 2001 From: fatai Date: Tue, 2 Apr 2024 18:13:34 +0200 Subject: [PATCH 1/7] permierversion de bep3201 --- .idea/.gitignore | 8 + .idea/DigLabTools.iml | 17 + .../inspectionProfiles/profiles_settings.xml | 6 + .idea/misc.xml | 7 + .idea/modules.xml | 8 + .idea/vcs.xml | 6 + bep32v01/Common_structure.py | 23 + bep32v01/Createdirectory.py | 79 + bep32v01/Createfile.py | 75 + bep32v01/Datatype.py | 30 + bep32v01/DirectoryStructure.py | 91 + bep32v01/Entity.py | 31 + bep32v01/Essaie/CHANGES | 0 bep32v01/Essaie/CITATION.cff | 0 bep32v01/Essaie/LICENSE | 0 bep32v01/Essaie/README | 0 bep32v01/Essaie/README.md | 0 bep32v01/Essaie/README.md.rst | 0 bep32v01/Essaie/README.md.rst.txt | 0 bep32v01/Essaie/dataset_description.json | 0 bep32v01/Essaie/genetic_info.json | 0 bep32v01/Essaie/participants.tsv | 0 bep32v01/Essaie/participants.tsv.json | 0 bep32v01/Essaie/samples.tsv | 0 bep32v01/Essaie/samples.tsv.json | 0 bep32v01/Filestructure.py | 80 + bep32v01/Modality.py | 22 + bep32v01/__init__.py | 0 bep32v01/helper.py | 115 + .../inspectionProfiles/Project_Default.xml | 19 + .../inspectionProfiles/profiles_settings.xml | 6 + bep32v01/ressources/.idea/misc.xml | 4 + bep32v01/ressources/.idea/modules.xml | 8 + bep32v01/ressources/.idea/ressources.iml | 8 + bep32v01/ressources/.idea/vcs.xml | 6 + bep32v01/ressources/.idea/workspace.xml | 194 + bep32v01/ressources/schema/BIDS_VERSION | 1 + bep32v01/ressources/schema/README.md | 1025 +++++ bep32v01/ressources/schema/SCHEMA_VERSION | 1 + .../ressources/schema/meta/associations.yaml | 92 + bep32v01/ressources/schema/meta/context.yaml | 364 ++ .../schema/meta/expression_tests.yaml | 116 + bep32v01/ressources/schema/meta/versions.yaml | 19 + .../ressources/schema/objects/columns.yaml | 872 ++++ .../schema/objects/common_principles.yaml | 156 + .../ressources/schema/objects/datatypes.yaml | 63 + .../ressources/schema/objects/entities.yaml | 424 ++ bep32v01/ressources/schema/objects/enums.yaml | 1407 +++++++ .../ressources/schema/objects/extensions.yaml | 320 ++ bep32v01/ressources/schema/objects/files.yaml | 128 + .../ressources/schema/objects/formats.yaml | 130 + .../ressources/schema/objects/metadata.yaml | 3708 +++++++++++++++++ .../ressources/schema/objects/modalities.yaml | 37 + .../ressources/schema/objects/suffixes.yaml | 785 ++++ .../ressources/schema/rules/checks/anat.yaml | 42 + .../ressources/schema/rules/checks/asl.yaml | 318 ++ .../rules/checks/common_derivatives.yaml | 18 + .../schema/rules/checks/dataset.yaml | 92 + .../ressources/schema/rules/checks/dwi.yaml | 70 + .../schema/rules/checks/events.yaml | 29 + .../ressources/schema/rules/checks/fmap.yaml | 51 + .../ressources/schema/rules/checks/func.yaml | 129 + .../schema/rules/checks/general.yaml | 25 + .../ressources/schema/rules/checks/hints.yaml | 52 + .../ressources/schema/rules/checks/micr.yaml | 38 + .../ressources/schema/rules/checks/mri.yaml | 129 + .../ressources/schema/rules/checks/nifti.yaml | 51 + .../ressources/schema/rules/checks/nirs.yaml | 105 + .../schema/rules/checks/privacy.yaml | 29 + .../schema/rules/checks/references.yaml | 43 + .../schema/rules/common_principles.yaml | 18 + .../schema/rules/dataset_metadata.yaml | 50 + .../ressources/schema/rules/directories.yaml | 112 + .../ressources/schema/rules/entities.yaml | 32 + bep32v01/ressources/schema/rules/errors.yaml | 248 ++ .../schema/rules/files/common/core.yaml | 39 + .../schema/rules/files/common/tables.yaml | 32 + .../schema/rules/files/deriv/imaging.yaml | 220 + .../rules/files/deriv/preprocessed_data.yaml | 224 + .../schema/rules/files/raw/anat.yaml | 219 + .../schema/rules/files/raw/beh.yaml | 16 + .../schema/rules/files/raw/channels.yaml | 95 + .../schema/rules/files/raw/dwi.yaml | 40 + .../schema/rules/files/raw/eeg.yaml | 21 + .../schema/rules/files/raw/fmap.yaml | 151 + .../schema/rules/files/raw/func.yaml | 45 + .../schema/rules/files/raw/ieeg.yaml | 22 + .../schema/rules/files/raw/meg.yaml | 86 + .../schema/rules/files/raw/micr.yaml | 38 + .../schema/rules/files/raw/motion.yaml | 16 + .../schema/rules/files/raw/nirs.yaml | 15 + .../schema/rules/files/raw/perf.yaml | 49 + .../schema/rules/files/raw/pet.yaml | 34 + .../schema/rules/files/raw/photo.yaml | 30 + .../schema/rules/files/raw/task.yaml | 132 + .../ressources/schema/rules/modalities.yaml | 33 + .../schema/rules/sidecars/anat.yaml | 33 + .../ressources/schema/rules/sidecars/asl.yaml | 201 + .../ressources/schema/rules/sidecars/beh.yaml | 25 + .../schema/rules/sidecars/continuous.yaml | 25 + .../derivatives/common_derivatives.yaml | 76 + .../ressources/schema/rules/sidecars/dwi.yaml | 20 + .../ressources/schema/rules/sidecars/eeg.yaml | 176 + .../schema/rules/sidecars/entity_rules.yaml | 70 + .../schema/rules/sidecars/events.yaml | 14 + .../schema/rules/sidecars/fmap.yaml | 71 + .../schema/rules/sidecars/func.yaml | 88 + .../schema/rules/sidecars/ieeg.yaml | 140 + .../ressources/schema/rules/sidecars/meg.yaml | 266 ++ .../schema/rules/sidecars/micr.yaml | 100 + .../schema/rules/sidecars/motion.yaml | 76 + .../ressources/schema/rules/sidecars/mri.yaml | 315 ++ .../schema/rules/sidecars/nirs.yaml | 180 + .../ressources/schema/rules/sidecars/pet.yaml | 278 ++ .../derivatives/common_derivatives.yaml | 12 + .../schema/rules/tabular_data/eeg.yaml | 45 + .../schema/rules/tabular_data/ieeg.yaml | 68 + .../schema/rules/tabular_data/meg.yaml | 24 + .../rules/tabular_data/modality_agnostic.yaml | 62 + .../schema/rules/tabular_data/motion.yaml | 25 + .../schema/rules/tabular_data/nirs.yaml | 66 + .../schema/rules/tabular_data/perf.yaml | 8 + .../schema/rules/tabular_data/pet.yaml | 60 + .../schema/rules/tabular_data/physio.yaml | 9 + .../schema/rules/tabular_data/task.yaml | 21 + bep32v01/tests/__init__.py | 0 elab_bridge/Datastructures.py | 29 + elab_bridge/Test_export_bids.py | 24 + requirements.txt | 9 +- 129 files changed, 16342 insertions(+), 3 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/DigLabTools.iml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 bep32v01/Common_structure.py create mode 100644 bep32v01/Createdirectory.py create mode 100644 bep32v01/Createfile.py create mode 100644 bep32v01/Datatype.py create mode 100644 bep32v01/DirectoryStructure.py create mode 100644 bep32v01/Entity.py create mode 100644 bep32v01/Essaie/CHANGES create mode 100644 bep32v01/Essaie/CITATION.cff create mode 100644 bep32v01/Essaie/LICENSE create mode 100644 bep32v01/Essaie/README create mode 100644 bep32v01/Essaie/README.md create mode 100644 bep32v01/Essaie/README.md.rst create mode 100644 bep32v01/Essaie/README.md.rst.txt create mode 100644 bep32v01/Essaie/dataset_description.json create mode 100644 bep32v01/Essaie/genetic_info.json create mode 100644 bep32v01/Essaie/participants.tsv create mode 100644 bep32v01/Essaie/participants.tsv.json create mode 100644 bep32v01/Essaie/samples.tsv create mode 100644 bep32v01/Essaie/samples.tsv.json create mode 100644 bep32v01/Filestructure.py create mode 100644 bep32v01/Modality.py create mode 100644 bep32v01/__init__.py create mode 100644 bep32v01/helper.py create mode 100644 bep32v01/ressources/.idea/inspectionProfiles/Project_Default.xml create mode 100644 bep32v01/ressources/.idea/inspectionProfiles/profiles_settings.xml create mode 100644 bep32v01/ressources/.idea/misc.xml create mode 100644 bep32v01/ressources/.idea/modules.xml create mode 100644 bep32v01/ressources/.idea/ressources.iml create mode 100644 bep32v01/ressources/.idea/vcs.xml create mode 100644 bep32v01/ressources/.idea/workspace.xml create mode 100644 bep32v01/ressources/schema/BIDS_VERSION create mode 100644 bep32v01/ressources/schema/README.md create mode 100644 bep32v01/ressources/schema/SCHEMA_VERSION create mode 100644 bep32v01/ressources/schema/meta/associations.yaml create mode 100644 bep32v01/ressources/schema/meta/context.yaml create mode 100644 bep32v01/ressources/schema/meta/expression_tests.yaml create mode 100644 bep32v01/ressources/schema/meta/versions.yaml create mode 100644 bep32v01/ressources/schema/objects/columns.yaml create mode 100644 bep32v01/ressources/schema/objects/common_principles.yaml create mode 100644 bep32v01/ressources/schema/objects/datatypes.yaml create mode 100644 bep32v01/ressources/schema/objects/entities.yaml create mode 100644 bep32v01/ressources/schema/objects/enums.yaml create mode 100644 bep32v01/ressources/schema/objects/extensions.yaml create mode 100644 bep32v01/ressources/schema/objects/files.yaml create mode 100644 bep32v01/ressources/schema/objects/formats.yaml create mode 100644 bep32v01/ressources/schema/objects/metadata.yaml create mode 100644 bep32v01/ressources/schema/objects/modalities.yaml create mode 100644 bep32v01/ressources/schema/objects/suffixes.yaml create mode 100644 bep32v01/ressources/schema/rules/checks/anat.yaml create mode 100644 bep32v01/ressources/schema/rules/checks/asl.yaml create mode 100644 bep32v01/ressources/schema/rules/checks/common_derivatives.yaml create mode 100644 bep32v01/ressources/schema/rules/checks/dataset.yaml create mode 100644 bep32v01/ressources/schema/rules/checks/dwi.yaml create mode 100644 bep32v01/ressources/schema/rules/checks/events.yaml create mode 100644 bep32v01/ressources/schema/rules/checks/fmap.yaml create mode 100644 bep32v01/ressources/schema/rules/checks/func.yaml create mode 100644 bep32v01/ressources/schema/rules/checks/general.yaml create mode 100644 bep32v01/ressources/schema/rules/checks/hints.yaml create mode 100644 bep32v01/ressources/schema/rules/checks/micr.yaml create mode 100644 bep32v01/ressources/schema/rules/checks/mri.yaml create mode 100644 bep32v01/ressources/schema/rules/checks/nifti.yaml create mode 100644 bep32v01/ressources/schema/rules/checks/nirs.yaml create mode 100644 bep32v01/ressources/schema/rules/checks/privacy.yaml create mode 100644 bep32v01/ressources/schema/rules/checks/references.yaml create mode 100644 bep32v01/ressources/schema/rules/common_principles.yaml create mode 100644 bep32v01/ressources/schema/rules/dataset_metadata.yaml create mode 100644 bep32v01/ressources/schema/rules/directories.yaml create mode 100644 bep32v01/ressources/schema/rules/entities.yaml create mode 100644 bep32v01/ressources/schema/rules/errors.yaml create mode 100644 bep32v01/ressources/schema/rules/files/common/core.yaml create mode 100644 bep32v01/ressources/schema/rules/files/common/tables.yaml create mode 100644 bep32v01/ressources/schema/rules/files/deriv/imaging.yaml create mode 100644 bep32v01/ressources/schema/rules/files/deriv/preprocessed_data.yaml create mode 100644 bep32v01/ressources/schema/rules/files/raw/anat.yaml create mode 100644 bep32v01/ressources/schema/rules/files/raw/beh.yaml create mode 100644 bep32v01/ressources/schema/rules/files/raw/channels.yaml create mode 100644 bep32v01/ressources/schema/rules/files/raw/dwi.yaml create mode 100644 bep32v01/ressources/schema/rules/files/raw/eeg.yaml create mode 100644 bep32v01/ressources/schema/rules/files/raw/fmap.yaml create mode 100644 bep32v01/ressources/schema/rules/files/raw/func.yaml create mode 100644 bep32v01/ressources/schema/rules/files/raw/ieeg.yaml create mode 100644 bep32v01/ressources/schema/rules/files/raw/meg.yaml create mode 100644 bep32v01/ressources/schema/rules/files/raw/micr.yaml create mode 100644 bep32v01/ressources/schema/rules/files/raw/motion.yaml create mode 100644 bep32v01/ressources/schema/rules/files/raw/nirs.yaml create mode 100644 bep32v01/ressources/schema/rules/files/raw/perf.yaml create mode 100644 bep32v01/ressources/schema/rules/files/raw/pet.yaml create mode 100644 bep32v01/ressources/schema/rules/files/raw/photo.yaml create mode 100644 bep32v01/ressources/schema/rules/files/raw/task.yaml create mode 100644 bep32v01/ressources/schema/rules/modalities.yaml create mode 100644 bep32v01/ressources/schema/rules/sidecars/anat.yaml create mode 100644 bep32v01/ressources/schema/rules/sidecars/asl.yaml create mode 100644 bep32v01/ressources/schema/rules/sidecars/beh.yaml create mode 100644 bep32v01/ressources/schema/rules/sidecars/continuous.yaml create mode 100644 bep32v01/ressources/schema/rules/sidecars/derivatives/common_derivatives.yaml create mode 100644 bep32v01/ressources/schema/rules/sidecars/dwi.yaml create mode 100644 bep32v01/ressources/schema/rules/sidecars/eeg.yaml create mode 100644 bep32v01/ressources/schema/rules/sidecars/entity_rules.yaml create mode 100644 bep32v01/ressources/schema/rules/sidecars/events.yaml create mode 100644 bep32v01/ressources/schema/rules/sidecars/fmap.yaml create mode 100644 bep32v01/ressources/schema/rules/sidecars/func.yaml create mode 100644 bep32v01/ressources/schema/rules/sidecars/ieeg.yaml create mode 100644 bep32v01/ressources/schema/rules/sidecars/meg.yaml create mode 100644 bep32v01/ressources/schema/rules/sidecars/micr.yaml create mode 100644 bep32v01/ressources/schema/rules/sidecars/motion.yaml create mode 100644 bep32v01/ressources/schema/rules/sidecars/mri.yaml create mode 100644 bep32v01/ressources/schema/rules/sidecars/nirs.yaml create mode 100644 bep32v01/ressources/schema/rules/sidecars/pet.yaml create mode 100644 bep32v01/ressources/schema/rules/tabular_data/derivatives/common_derivatives.yaml create mode 100644 bep32v01/ressources/schema/rules/tabular_data/eeg.yaml create mode 100644 bep32v01/ressources/schema/rules/tabular_data/ieeg.yaml create mode 100644 bep32v01/ressources/schema/rules/tabular_data/meg.yaml create mode 100644 bep32v01/ressources/schema/rules/tabular_data/modality_agnostic.yaml create mode 100644 bep32v01/ressources/schema/rules/tabular_data/motion.yaml create mode 100644 bep32v01/ressources/schema/rules/tabular_data/nirs.yaml create mode 100644 bep32v01/ressources/schema/rules/tabular_data/perf.yaml create mode 100644 bep32v01/ressources/schema/rules/tabular_data/pet.yaml create mode 100644 bep32v01/ressources/schema/rules/tabular_data/physio.yaml create mode 100644 bep32v01/ressources/schema/rules/tabular_data/task.yaml create mode 100644 bep32v01/tests/__init__.py create mode 100644 elab_bridge/Datastructures.py create mode 100644 elab_bridge/Test_export_bids.py diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..13566b81 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/DigLabTools.iml b/.idea/DigLabTools.iml new file mode 100644 index 00000000..57c2c965 --- /dev/null +++ b/.idea/DigLabTools.iml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 00000000..105ce2da --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..0ab07188 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..770a7849 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..35eb1ddf --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/bep32v01/Common_structure.py b/bep32v01/Common_structure.py new file mode 100644 index 00000000..05974443 --- /dev/null +++ b/bep32v01/Common_structure.py @@ -0,0 +1,23 @@ +import bep32v01 +from bep32v01 import * + +from DirectoryStructure import DirectoryStructure +from Datatype import DataTypes +from Entity import Entity +from Filestructure import FileStructure +from Modality import Modality + +class Common_structure: + def __init__(self, output='.'): + self.directory = DirectoryStructure() # Création d'une instance de la classe DirectoryStructure + self.entity = Entity() + self.filestructure = FileStructure() + self.modality = Modality() + self.datatype = DataTypes() + self .output = output + + def initialize(self): + self.directory.get_details() + self.filestructure.get_detail() + def create_entity(self): + self \ No newline at end of file diff --git a/bep32v01/Createdirectory.py b/bep32v01/Createdirectory.py new file mode 100644 index 00000000..aa8c5aa8 --- /dev/null +++ b/bep32v01/Createdirectory.py @@ -0,0 +1,79 @@ +import json +import os +from Filestructure import FileStructure +from DirectoryStructure import DirectoryStructure + +from Entity import Entity +from Datatype import DataTypes + +from pathlib import Path + + +class Createdirectory: + def __init__(self, output_path, sub_id=1, session_id=1, modality="micrs"): + self.session_path = None + self.output_path = output_path + self.dir_name = [] + self.filestructure = FileStructure() + self.filestructure.get_detail() + self.directorystructure = DirectoryStructure() + self.entity = Entity() + self.dataType = DataTypes() + self.sub_id = sub_id + self.session_id = session_id + sub_directory = [] + self.modality = modality + + def layout_folder(self): + top_level_dir = self.directorystructure.get_top_level_directory() + entity_dir = self.directorystructure.get_entity_directory() + value_dir = self.directorystructure.get_value_directory() + all_dir = self.directorystructure.get_all_directory() + for dir in all_dir: + + path = "" + if dir in top_level_dir: + # print(dir) + if dir in entity_dir: + path = self.entity.get_entity_name(dir) + f'-{str(self.sub_id)}' + + + + elif dir in value_dir: + path = self.dataType.get_data_type_value(dir) + + + else: + path = dir + print(f"{dir} ==========> {path}") + self.dir_name.append(path) + + def build_top_folder(self): + for dir in self.dir_name: + first_level_dir = os.path.join(self.output_path, dir) + print("path: ", first_level_dir) + if not os.path.exists(first_level_dir): + os.makedirs(first_level_dir) + + # subdirectory session + subject_dir = os.path.join(self.output_path, f'sub-{self.sub_id}') + session_dir = os.path.join(subject_dir, f'ses-{self.session_id}') + print("subject_dir: ", session_dir) + if not os.path.exists(session_dir): + os.makedirs(session_dir) + # subdirectory_modality + modality_dir = os.path.join(session_dir, self.modality) + if not os.path.exists(modality_dir): + os.makedirs(modality_dir) + + +def main(): + output_path = "Essaie" # Change this to your desired output path + creator = Createdirectory(output_path) + creator.layout_folder() + creator.build_top_folder() + print("Directory layout created successfully.") + + +if __name__ == "__main__": + main() diff --git a/bep32v01/Createfile.py b/bep32v01/Createfile.py new file mode 100644 index 00000000..b0f3cc20 --- /dev/null +++ b/bep32v01/Createfile.py @@ -0,0 +1,75 @@ +import json +import os +from Filestructure import FileStructure + + +class CreatFile: + def __init__(self, output_path): + self.output_path = output_path + self.file_name = [] + self.filestructure = FileStructure() + + def create_empty_file(self, filename): + file_path = os.path.join(self.output_path, filename) + with open(file_path, 'w'): + pass + + def write_json_to_file(self, filename, data): + file_path = os.path.join(self.output_path, filename) + with open(file_path, 'w') as file: + json.dump(data, file) + + def dataset_structure(self, input_data): + self.write_json_to_file('dataset_description.json', input_data) + + def readme_change_licence(self): + for filename in ['README', 'CHANGES', 'LICENSES']: + self.create_empty_file(filename) + + def create_file(self, filename): + self.create_empty_file(filename) + + def citation_file(self): + self.create_file('CITATION.cff') + + def participant_file(self): + self.create_file('participants.tsv') + self.create_file('participants.json') + + def sample_file(self): + self.create_file('sample.tsv') + self.create_file('sample.json') + + def dataset_description(self): + self.create_file('dataset_description.json') + + def creat_all_files(self): + self.layout_file() + for filename in self.file_name: + self.create_empty_file(filename) + + def get_file_structure(self): + return self.filestructure + + def layout_file(self): + all_file = self.filestructure.get_top_level_files_list() + + for filename in all_file: + info = self.filestructure.get_detail_for_file(filename) + if 'path' in info: + self.file_name.append(info['path']) + elif 'stem' in info: + path = info['stem'] + for extension in info['extensions']: + path = path + extension + print(path) + self.file_name.append(path) + return self.file_name + + +if __name__ == "__main__": + creatfile = CreatFile('Essaie') + # d = creatfile.get_file_structure() + # creatfile.layout_file() + creatfile.creat_all_files() + # print(d.get_top_level_files_list()) diff --git a/bep32v01/Datatype.py b/bep32v01/Datatype.py new file mode 100644 index 00000000..c2246625 --- /dev/null +++ b/bep32v01/Datatype.py @@ -0,0 +1,30 @@ +import yaml + + +def _load_data_types(yaml_path="ressources/schema/objects/datatypes.yaml"): + with open(yaml_path, 'r') as file: + data_types_data = yaml.safe_load(file) + return data_types_data + + +class DataTypes: + def __init__(self): + self.data_types = _load_data_types() + + def get_data_type_value(self, data_type_name): + return self.data_types.get(data_type_name).get("value") + + +def main(): + data_types = DataTypes() + data_type_name = "anat" # Exemple de nom de type de données + data_type = data_types.get_data_type_value(data_type_name) + if data_type: + print(f"Données de type '{data_type_name}':") + print(data_type) + else: + print(f"Le type de données '{data_type_name}' n'existe pas.") + + +if __name__ == "__main__": + main() diff --git a/bep32v01/DirectoryStructure.py b/bep32v01/DirectoryStructure.py new file mode 100644 index 00000000..a70fc130 --- /dev/null +++ b/bep32v01/DirectoryStructure.py @@ -0,0 +1,91 @@ +from pathlib import Path +import yaml +import helper + + +class DirectoryStructure: + def __init__(self): + self.relative_path = "ressources/schema/rules/directories.yaml" + self.entity_directory = [] + self.all_directory = None + self.value_directory = None + self.required_directory = None + self.optional_directory = None + self.recommended_directory = None + self.top_level_directory = None + self.sub_directory = None + self.get_detail() + + + def load_all_directories(self, relative_path): + # Obtenir le chemin absolu du fichier YAML en utilisant le chemin relatif fourni + absolute_path = Path(relative_path).resolve() + + # Vérifier si le fichier existe + if absolute_path.exists(): + with open(absolute_path, 'r') as file: + directory_rules = yaml.safe_load(file) + + if directory_rules: + self.all_directory = list(set(helper.find_keys_in_dict(directory_rules, 'level'))) + else: + print("Le fichier de règles des répertoires est vide.") + else: + print("Le fichier YAML spécifié n'existe pas :", absolute_path) + return self.all_directory + + def load_all_directoires_all_details(self, relative_path): + self.entity_directory, self.value_directory, self.required_directory, self.optional_directory, self.recommended_directory, self.top_level_directory = helper.get_directories_with_details( + relative_path) + + def get_detail(self): + self.load_all_directories(self.relative_path) + self.load_all_directoires_all_details(self.relative_path) + return self + + # Getter pour all_directory + def get_all_directory(self): + return self.all_directory + + # Getter pour entity_directory + def get_entity_directory(self): + return self.entity_directory + + # Getter pour value_directory + def get_value_directory(self): + return self.value_directory + + # Getter pour required_directory + def get_required_directory(self): + return self.required_directory + + # Getter pour optional_directory + def get_optional_directory(self): + return self.optional_directory + + # Getter pour recommended_directory + def get_recommended_directory(self): + return self.recommended_directory + + # Getter pour top_level_directory + def get_top_level_directory(self): + return self.top_level_directory + + +# Exemple d'utilisation +if __name__ == "__main__": + # Chemin relatif vers le fichier YAML des règles de répertoires + relative_path = "ressources/schema/rules/directories.yaml" + + # Création d'une instance de la classe DirectoryStructure + common_structure = DirectoryStructure() + common_structure.get_detail() + + # Affichage des répertoires chargés en utilisant les getters + print("All:", common_structure.get_all_directory()) + print("Entity:", common_structure.get_entity_directory()) + print("par Valeur :", common_structure.get_value_directory()) + print("REQUIRED :", common_structure.get_required_directory()) + print("optional :", common_structure.get_optional_directory()) + print("top level:", common_structure.get_top_level_directory()) + print("recomende:", common_structure.get_recommended_directory()) diff --git a/bep32v01/Entity.py b/bep32v01/Entity.py new file mode 100644 index 00000000..52c3ec6e --- /dev/null +++ b/bep32v01/Entity.py @@ -0,0 +1,31 @@ +import yaml + + +class Entity: + def __init__(self): + self.entities = self._load_entities() + + def _load_entities(self, yaml_path="ressources/schema/objects/entities.yaml"): + with open(yaml_path, 'r') as file: + entities_data = yaml.safe_load(file) + return entities_data + + def get_entity_name(self, entity_name): + if entity_name in self.entities: + return self.entities[entity_name].get("name") + else: + return None + + +def main(): + entities = Entity() + entity_name = "acquisition" # Exemple de nom d'entité + entity_name_output = entities.get_entity_name(entity_name) + if entity_name_output: + print(f"Nom de l'entité '{entity_name}': {entity_name_output}") + else: + print(f"L'entité '{entity_name}' n'existe pas.") + + +if __name__ == "__main__": + main() diff --git a/bep32v01/Essaie/CHANGES b/bep32v01/Essaie/CHANGES new file mode 100644 index 00000000..e69de29b diff --git a/bep32v01/Essaie/CITATION.cff b/bep32v01/Essaie/CITATION.cff new file mode 100644 index 00000000..e69de29b diff --git a/bep32v01/Essaie/LICENSE b/bep32v01/Essaie/LICENSE new file mode 100644 index 00000000..e69de29b diff --git a/bep32v01/Essaie/README b/bep32v01/Essaie/README new file mode 100644 index 00000000..e69de29b diff --git a/bep32v01/Essaie/README.md b/bep32v01/Essaie/README.md new file mode 100644 index 00000000..e69de29b diff --git a/bep32v01/Essaie/README.md.rst b/bep32v01/Essaie/README.md.rst new file mode 100644 index 00000000..e69de29b diff --git a/bep32v01/Essaie/README.md.rst.txt b/bep32v01/Essaie/README.md.rst.txt new file mode 100644 index 00000000..e69de29b diff --git a/bep32v01/Essaie/dataset_description.json b/bep32v01/Essaie/dataset_description.json new file mode 100644 index 00000000..e69de29b diff --git a/bep32v01/Essaie/genetic_info.json b/bep32v01/Essaie/genetic_info.json new file mode 100644 index 00000000..e69de29b diff --git a/bep32v01/Essaie/participants.tsv b/bep32v01/Essaie/participants.tsv new file mode 100644 index 00000000..e69de29b diff --git a/bep32v01/Essaie/participants.tsv.json b/bep32v01/Essaie/participants.tsv.json new file mode 100644 index 00000000..e69de29b diff --git a/bep32v01/Essaie/samples.tsv b/bep32v01/Essaie/samples.tsv new file mode 100644 index 00000000..e69de29b diff --git a/bep32v01/Essaie/samples.tsv.json b/bep32v01/Essaie/samples.tsv.json new file mode 100644 index 00000000..e69de29b diff --git a/bep32v01/Filestructure.py b/bep32v01/Filestructure.py new file mode 100644 index 00000000..f2270d85 --- /dev/null +++ b/bep32v01/Filestructure.py @@ -0,0 +1,80 @@ +import yaml + + +class FileStructure: + def __init__(self, relative_path="ressources/schema/rules/files/common/core.yaml"): + self.relative_path = relative_path + self.all_files = [] + self.top_level_files = [] + self.top_level_directory = [] + self.top_level_file_details = {} + self.top_level_directory_detail = {} + self.get_detail() + + + def get_all_files(self): + with open("ressources/schema/objects/files.yaml", 'r') as file: + file_rules = yaml.safe_load(file) + if file_rules: + for key in file_rules: + self.all_files.append(key) + if file_rules.get(key).get("file_type") == "regular": + self.top_level_files.append(key) + else: + self.top_level_directory.append(key) + + def get_all_files_detail(self, relative_path): + with open(relative_path, 'r') as file: + file_rules = yaml.safe_load(file) + if file_rules: + for key, value in file_rules.items(): + if key in self.top_level_files: + self.top_level_file_details[key] = value + else: + self.top_level_directory_detail[key] = value + + def get_detail(self): + self.get_all_files() + self.get_all_files_detail(self.relative_path) + self.get_all_files_detail("ressources/schema/rules/files/common/tables.yaml") + return self + + def get_detail_for_file(self, file_name): + return self.top_level_file_details.get(file_name) + + def get_detail_for_directory(self, directory_name): + return self.top_level_directory_detail.get(directory_name) + + # Getters pour tous les attributs + def get_relative_path(self): + return self.relative_path + + def get_all_files_list(self): + return self.all_files + + def get_top_level_files_list(self): + return self.top_level_files + + def get_top_level_directory_list(self): + return self.top_level_directory + + def get_top_level_file_details(self): + return self.top_level_file_details + + def get_top_level_directory_details(self): + return self.top_level_directory_detail + + +def main(): + file_structure = FileStructure() + file_structure.get_detail() + print(file_structure.get_all_files_list()) + # print("Détail pour le fichier README:") + #print(file_structure.get_detail_for_file("README")) + + print("Détail pour le répertoire code:") + #print(file_structure.get_detail_for_directory("code")) + + +if __name__ == "__main__": + main() diff --git a/bep32v01/Modality.py b/bep32v01/Modality.py new file mode 100644 index 00000000..01659252 --- /dev/null +++ b/bep32v01/Modality.py @@ -0,0 +1,22 @@ +import yaml + + +class Modality: + def __init__(self, relative_path="ressources/schema/objects/modalities.yaml"): + self.relative_path = relative_path + self.modalities = [] + + with open(relative_path, "r") as file: + modalities_yaml = yaml.safe_load(file) + if modalities_yaml: + self.modalities = list(modalities_yaml.keys()) + + +def main(): + modality = Modality() + print("Modalités :") + print(modality.modalities) + + +if __name__ == "__main__": + main() diff --git a/bep32v01/__init__.py b/bep32v01/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/bep32v01/helper.py b/bep32v01/helper.py new file mode 100644 index 00000000..966a444b --- /dev/null +++ b/bep32v01/helper.py @@ -0,0 +1,115 @@ +import os + +import yaml + + +def find_keys_in_dict(dictionary, target_value): + """ + Search for keys corresponding to the given value in a dictionary. + :param dictionary: The dictionary to search in. + :param target_value: The value to search for. + :return: A list of keys corresponding to the value, or an empty list if the value is not found. + """ + keys = [] + + # Iterate through all key-value pairs in the dictionary + for key, value in dictionary.items(): + # Check if the value matches the target value + + if target_value in value: + + keys.append(key) + # If the value is another dictionary, recursively call the function to search within that dictionary + elif isinstance(value, dict): + nested_keys = find_keys_in_dict(value, target_value) + # Extend the keys list with the keys found in the nested dictionary + keys.extend(nested_keys) + + return keys + + +def find_value_in_dict(dictionary, target_key): + """ + Search for a value corresponding to the given key in a dictionary. + :param dictionary: The dictionary to search in. + :param target_key: The key to search for. + :return: The value corresponding to the key, or None if the key is not found. + """ + # Iterate through all keys and values in the dictionary + for key, value in dictionary.items(): + # Check if the key matches the target key + if key == target_key: + return value + # If the value is another dictionary, recursively call the function to search within that dictionary + elif isinstance(value, dict): + result = find_value_in_dict(value, target_key) + if result is not None: + return result + # If the key is not found in this dictionary or any of its sub-dictionaries, return None + return None + + +def find_keys_with_value(dictionary, target_value): + """ + Find keys containing the given value in a dictionary. + :param dictionary: The dictionary to search in. + :param target_value: The value to search for. + :return: A list of keys containing the value, or an empty list if the value is not found. + """ + keys = [] + + # Iterate through all key-value pairs in the dictionary + for key, value in dictionary.items(): + # Check if the value matches the target value + if isinstance(value, list): + if target_value in value: + keys.append(key) + elif value == target_value: + keys.append(key) + # If the value is another dictionary, recursively call the function to search within that dictionary + elif isinstance(value, dict): + nested_keys = find_keys_with_value(value, target_value) + # Extend the keys list with the keys found in the nested dictionary + keys.extend(nested_keys) + + return keys + + +def get_directories_with_details(yaml_file): + """ + Get directories with the 'entity' attribute from a YAML file. + :param yaml_file: Path to the YAML file containing directory information. + :return: A list of directory names having the 'entity' attribute. + """ + directories_entities = [] + directories_values = [] + directory_required = [] + directory_optional = [] + direrectory_recomended = [] + top_level_directory = [] + sub_directory =[] + + # Load YAML file + with open(yaml_file, 'r') as file: + data = yaml.safe_load(file) + + # Iterate through each directory definition + for directory, info in data.get('raw', {}).items(): + + # Check if the directory definition contains the 'entity' attribute + if 'entity' in info: + directories_entities.append(directory) + if 'value' in info: + directories_values.append(directory) + if 'level' in info and info.get('level') == 'required': + print(info) + directory_required.append(directory) + if 'level' in info and info.get('level') == 'optional': + directory_optional.append(directory) + if 'recommended' in info: + direrectory_recomended.append(directory) + for directory in data.get('raw', {}).get('root', {}).get('subdirs', {}): + + top_level_directory.append(directory) + + return directories_entities, directories_values, directory_required, directory_optional, direrectory_recomended, top_level_directory diff --git a/bep32v01/ressources/.idea/inspectionProfiles/Project_Default.xml b/bep32v01/ressources/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 00000000..e94858f7 --- /dev/null +++ b/bep32v01/ressources/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,19 @@ + + + + \ No newline at end of file diff --git a/bep32v01/ressources/.idea/inspectionProfiles/profiles_settings.xml b/bep32v01/ressources/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 00000000..105ce2da --- /dev/null +++ b/bep32v01/ressources/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/bep32v01/ressources/.idea/misc.xml b/bep32v01/ressources/.idea/misc.xml new file mode 100644 index 00000000..6eeaaf25 --- /dev/null +++ b/bep32v01/ressources/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/bep32v01/ressources/.idea/modules.xml b/bep32v01/ressources/.idea/modules.xml new file mode 100644 index 00000000..8efbbeb2 --- /dev/null +++ b/bep32v01/ressources/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/bep32v01/ressources/.idea/ressources.iml b/bep32v01/ressources/.idea/ressources.iml new file mode 100644 index 00000000..d0876a78 --- /dev/null +++ b/bep32v01/ressources/.idea/ressources.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/bep32v01/ressources/.idea/vcs.xml b/bep32v01/ressources/.idea/vcs.xml new file mode 100644 index 00000000..b2bdec2d --- /dev/null +++ b/bep32v01/ressources/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/bep32v01/ressources/.idea/workspace.xml b/bep32v01/ressources/.idea/workspace.xml new file mode 100644 index 00000000..258be40d --- /dev/null +++ b/bep32v01/ressources/.idea/workspace.xml @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { + "associatedIndex": 8 +} + + + + { + "keyToString": { + "RunOnceActivity.OpenProjectViewOnStart": "true", + "RunOnceActivity.ShowReadmeOnStart": "true", + "SHARE_PROJECT_CONFIGURATION_FILES": "true", + "git-widget-placeholder": "elab/testexportbids", + "last_opened_file_path": "/home/pourtoi/PycharmProjects/DigLabTools/bep32v01/ressources", + "node.js.detected.package.eslint": "true", + "node.js.detected.package.tslint": "true", + "node.js.selected.package.eslint": "(autodetect)", + "node.js.selected.package.tslint": "(autodetect)", + "nodejs_package_manager_path": "npm", + "vue.rearranger.settings.migration": "true" + } +} + + + + + + + + + + + 1712044823700 + + + + + + \ No newline at end of file diff --git a/bep32v01/ressources/schema/BIDS_VERSION b/bep32v01/ressources/schema/BIDS_VERSION new file mode 100644 index 00000000..a01185b4 --- /dev/null +++ b/bep32v01/ressources/schema/BIDS_VERSION @@ -0,0 +1 @@ +1.10.0-dev diff --git a/bep32v01/ressources/schema/README.md b/bep32v01/ressources/schema/README.md new file mode 100644 index 00000000..d8013145 --- /dev/null +++ b/bep32v01/ressources/schema/README.md @@ -0,0 +1,1025 @@ +# BIDS schema description + +Portions of the BIDS specification are defined using YAML files in order to +make the specification machine-readable. + +Currently the portions of the specification that rely on this schema are: +- the entity tables, +- entity definitions, +- filename templates, +- metadata tables. + +Any changes to the specification should be mirrored in the schema. + +## Organization and syntax + +At the time of this writing, the schema has the following file layout: + +```plain +├── meta +│   ├── ... +│   └── versions.yaml +├── objects +│   ├── ... +│   └── suffixes.yaml +├── rules +│   ├── checks +│   │   ├── ... +│   │   └── references.yaml +│   ├── files +│   │   ├── common +│   │   │   ├── core.yaml +│   │   │   └── tables.yaml +│   │   ├── deriv +│   │   │   ├── imaging.yaml +│   │   │   └── preprocessed_data.yaml +│   │   └── raw +│   │   ├── ... +│   │   └── task.yaml +│   ├── sidecars +│   │   ├── derivatives +│   │   │   └── common_derivatives.yaml +│   │   ├── ... +│   │   └── pet.yaml +│   ├── tabular_data +│   │   ├── derivatives +│   │   │   └── common_derivatives.yaml +│   │   ├── ... +│   │   └── task.yaml +│   ├── ... +│   └── modalities.yaml +├── BIDS_VERSION +└── SCHEMA_VERSION +``` + +The top-level organization includes `objects`, where terms are defined; +`rules`, where constraints (such as valid filenames or required metadata fields) +are defined; +and `meta`, where definitions useful for interpreting the schema are defined. + +Each file is made up of YAML data, most often an *object*. +For example, the file `rules/checks/mri.yaml` contains the contents: + +```YAML +PhasePartUnits: + issue: + code: PHASE_UNITS + message: | + Phase images (with the `part-phase` entity) must have units + "rad" or "arbitrary". + level: error + selectors: + - modality == "mri" + - entities.part == "phase" + - '"Units" in sidecar' + checks: + - intersects([sidecar.Units], ["rad", "arbitrary"]) +``` + +When we wish to refer to a file we might write `rules/checks/mri.yaml`. +Alternately, we can use `rules.checks.mri` to refer to the object contained by the +file. +Using this notation, the *qualified name*, the contents of an entire directory or a +portion of a file can be referred to unambiguously. +For example, the entire `rules/checks/` directory is referred to as `rules.checks`, +and `rules.checks.mri.PhasePartUnits.issue` refers to the object: + +```JSON +{ + "code": "PHASE_UNITS", + "message": "Phase images (with the `part-phase` [...]\n\"rad\" or \"arbitrary\".\n", + "level": "error" +} +``` + +These qualified names may be used in this README, as well as in *references* and +*expressions*. + +### Description formatting + +Many objects throughout the schema have a `description` field, +which will typically be rendered somewhere in the specification. +Because the specification is written in [Markdown](https://en.wikipedia.org/wiki/Markdown), +these `description` fields may also contain Markdown, +including links to other locations in the specification. + +Because the same description may be used in multiple locations, +a mechanism is needed to ensure that the correct path is discovered +to render the description in each location. +To do this, the path should follow the form `SPEC_ROOT/path/within/source.md#anchor`. +For example, to link to the +[Definitions](https://bids-specification.readthedocs.io/en/stable/common-principles.html#definitions) +section of +[Common principles](https://bids-specification.readthedocs.io/en/stable/common-principles.html), +use the path `SPEC_ROOT/common-principles.md#definitions`: + +```Markdown +[Common principles - Definitions](SPEC_ROOT/common-principles.md#definitions) +``` + +Note that the Markdown extension `.md` MUST be used for this to render correctly. + +For more information please see the following pull request and linked discussions: +[#1096](https://github.com/bids-standard/bids-specification/pull/1096) + +### References + +Some schema entries take the form: + +```YAML +ObjectName: + $ref: objects.metadata.OtherObjectName +``` + +This object may be *dereferenced* by replacing the `$ref` entry +with the object being referenced. +The following two prototypical examples are presented to clarify the semantics of +references (the cases in which they are used will be presented later): + +1. In `objects.enums`: + ```YAML + _GeneticLevelEnum: + type: string + enum: + - $ref: objects.enums.Genetic.value + - $ref: objects.enums.Genomic.value + - $ref: objects.enums.Epigenomic.value + - $ref: objects.enums.Transcriptomic.value + - $ref: objects.enums.Metabolomic.value + - $ref: objects.enums.Proteomic.value + ``` + and in `objects.metadata`: + ```YAML + GeneticLevel: + name: GeneticLevel + display_name: Genetic Level + description: | + Describes the level of analysis. + Values MUST be one of `"Genetic"`, `"Genomic"`, `"Epigenomic"`, + `"Transcriptomic"`, `"Metabolomic"`, or `"Proteomic"`. + anyOf: + - $ref: objects.enums._GeneticLevelEnum + - type: array + items: + $ref: objects.enums._GeneticLevelEnum + ``` + Here `_GeneticLevelEnum` is used to describe the valid values of `GeneticLevel`, + (which are in turn references to individual values), and the references inside `GeneticLevel.anyOf` indicate that there may be a single + such value or a list of values. + +1. In [`rules.files.deriv.preprocessed_data`](./rules/files/deriv/preprocessed_data.yaml): + ```YAML + anat_nonparametric_common: + $ref: rules.files.raw.anat.nonparametric + entities: + $ref: rules.files.raw.anat.nonparametric.entities + space: optional + description: optional + ``` + Here, the derivative datatype rule starts by copying the raw datatype rule + `rules.files.raw.anat.nonparametric`. + It then *overrides* the `entities` portion of that rule with a new object. + To *extend* the original `entities`, it again begins + by referencing `rules.files.raw.anat.nonparametric.entities`, + and adding the new entities `space` and `description`. + +### Expressions + +Rules definitions make use of a limited language of expressions that always evaluate to `true` or `false`. +These expressions may be used as `selectors`, determining whether a rule applies, +or `checks`, determining whether a rule is satisfied. + +Re-examining `rules.checks.mri.PhasePartUnits` from above: + +```YAML +PhasePartUnits: + issue: + code: PHASE_UNITS + message: | + Phase images (with the `part-phase` entity) must have units + "rad" or "arbitrary". + level: error + selectors: + - modality == "mri" + - entities.part == "phase" + - '"Units" in sidecar' + checks: + - intersects([sidecar.Units], ["rad", "arbitrary"]) +``` + +We see expressions may contain: + +- fields such as `modality`, `entities` (which has a `.part` subfield), `sidecar` +- String literals such as `"mri"`, `"Units"` or `"rad"` +- Lists containing fields or strings +- Comparison operators such as `==` (equality) or `in` (subfield exists in field) +- Functions such as `intersects()` + +In fact, the full list of fields is defined in the `meta.context.context` object, +which (currently) contains at the top level: + +- `schema`: access to the schema itself +- `dataset`: attributes of the whole dataset +- `subject`: attributes of the current subject +- `path`: the full path of the current file (relative to dataset root) +- `entities`: an object of entities parsed from the path +- `datatype`: the datatype, parsed from the path +- `suffix`: the suffix, parsed from the path +- `extension`: the file extension +- `modality`: the file modality, determined by datatype +- `sidecar`: the metadata values, accumulated by the inheritance principle +- `associations`: associated files, discovered by the inheritance principle +- `columns`: the columns in the current TSV file +- `json`: the contents of the current JSON file +- `gzip`: the contents of the current file GZIP header +- `nifti_header`: selected contents of the current NIfTI file's header +- `ome`: the contents of the current OME-XML metadata +- `tiff`: the contents of the current TIFF file's header + +Some of these are strings, while others are nested objects. +These are to be populated by an *interpreter* of the schema, +and provide the *namespace* in which expressions are evaluated. + +The following operators should be defined by an interpreter: + +| Operator | Definition | Example | +| ----------- | ------------------------------------------------------------- | --------------------------------------------- | +| `==` | equality | `suffix == "T1w"` | +| `!=` | inequality | `entities.task != "rest"` | +| `<`/`>` | less-than / greater-than | `sidecar.EchoTime < 0.5` | +| `<=`/`>=` | less-than-or-equal / greater-than-or-equal | `0 <= 4` | +| `in` | object lookup, true if RHS is a subfield of LHS | `"Units" in sidecar` | +| `!` | negation, true if the following value is false, or vice versa | `!true == false` | +| `&&` | conjunction, true if both RHS and LHS are true | `"Units" in sidecar && sidecar.Units == "mm"` | +| `\|\|` | disjunction, true if either RHS or LHS is true | `a < mn \|\| a > mx` | +| `.` | object query, returns value of subfield | `sidecar.Units` | +| `[]` | array/string index, returns value of Nth element (0-indexed) | `columns.participant_label[0]` | +| `+` | numeric addition / string concatenation | `x + 1`, `stem + "suffix"` | +| `-`/`*`/`/` | numeric operators (division coerces to float) | `length(array) - 2`, `x * 2`, `1 / 2 == 0.5` | + +The following functions should be defined by an interpreter: + +| Function | Definition | Example | Note | +| ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------ | +| `count(arg: array, val: any)` | Number of elements in an array equal to `val` | `count(columns.type, "EEG")` | The number of times "EEG" appears in the column "type" of the current TSV file | +| `exists(arg: str \| array, rule: str) -> int` | Count of files in an array that exist in the dataset. String is array with length 1. Rules include `"bids-uri"`, `"dataset"`, `"subject"` and `"stimuli"`. | `exists(sidecar.IntendedFor, "subject")` | True if all files in `IntendedFor` exist, relative to the subject directory. | +| `index(arg: array, val: any)` | Index of first element in an array equal to `val`, `null` if not found | `index(["i", "j", "k"], axis)` | The number, from 0-2 corresponding to the string `axis` | +| `intersects(a: array, b: array) -> bool` | `true` if arguments contain any shared elements | `intersects(dataset.modalities, ["pet", "mri"])` | True if either PET or MRI data is found in dataset | +| `length(arg: array) -> int` | Number of elements in an array | `length(columns.onset) > 0` | True if there is at least one value in the onset column | +| `match(arg: str, pattern: str) -> bool` | `true` if `arg` matches the regular expression `pattern` (anywhere in string) | `match(extension, ".gz$")` | True if the file extension ends with `.gz` | +| `max(arg: array) -> number` | The largest non-`n/a` value in an array | `max(columns.onset)` | The time of the last onset in an events.tsv file | +| `min(arg: array) -> number` | The smallest non-`n/a` value in an array | `min(sidecar.SliceTiming) == 0` | A check that the onset of the first slice is 0s | +| `sorted(arg: array) -> array` | The sorted values of the input array | `sorted(sidecar.VolumeTiming) == sidecar.VolumeTiming` | True if `sidecar.VolumeTiming` is sorted | +| `substr(arg: str, start: int, end: int) -> str` | The portion of the input string spanning from start position to end position | `substr(path, 0, length(path) - 3)` | `path` with the last three characters dropped | +| `type(arg: Any) -> str` | The name of the type, including `"array"`, `"object"`, `"null"` | `type(datatypes)` | Returns `"array"` | + +#### The special value `null` + +Missing values in the context object have the special value `null`. +This value propagates through all of the above operations in a fully-defined, +hopefully intuitive way. +Most operations involving `null` simply resolve to `null`: + +| Operation | Result | +| -------------------------- | ------ | +| `sidecar.MissingValue` | `null` | +| `null.anything` | `null` | +| `null[0]` | `null` | +| `null && true` | `null` | +| `null \|\| true` | `null` | +| `!null` | `null` | +| `null + 1` | `null` | +| `null - 1` | `null` | +| `null * 1` | `null` | +| `null / 1` | `null` | +| `match(null, pattern)` | `null` | +| `intersects(list, null)` | `null` | +| `substr(null, 0, 1)` | `null` | +| `substr(str, null, 1)` | `null` | +| `substr(str, 0, null)` | `null` | +| `length(null)` | `null` | +| `count(null, val)` | `null` | +| `count(list, null)` | `null` | +| `index(null, val)` | `null` | +| `index([0], null)` | `null` | +| `index([], val)` | `null` | +| `min(null)` | `null` | +| `max(null)` | `null` | +| `exists(null, "bids-uri")` | `null` | +| `exists("/path", null)` | `null` | + +The following operators have boolean results: + +| Operation | Result | Comment | +| ------------------------ | ------- | ---------------------------- | +| `null == false` | `false` | | +| `null == true` | `false` | | +| `null != false` | `true` | | +| `null != true` | `true` | | +| `null == null` | `true` | | +| `null == 1` | `false` | Also `<`, `>`, `<=` and `>=` | +| `"VolumeTiming" in null` | `false` | | + +The `type()` function returns a string: + +| Operation | Result | +| ------------ | -------- | +| `type(null)` | `"null"` | + +Finally, if an expression (selector or check) evaluates to `null`, +the `null` will be interpreted equivalent to `false`. +That is, a `null` selector will not apply the current rule, and a `null` +check will fail. + +## Object files + +Object files define "objects" or "terms", which are semantic descriptions of +concepts used in BIDS. These reside under the `object.*` namespace in the schema. +These files **do not** describe how objects of different types +(for example file suffixes and file entities) interact with one another, +or whether objects are required in a given dataset or file. + +### Overview + +There are currently 12 sub-namespaces, which fall into five rough categories. + +The namespaces are: + +| Namespace | Description | Group | +| --------------------------- | ----------------------------------------------------------------------------------- | ---------------- | +| `objects.common_principles` | Terms that are used throughout BIDS | General terms | +| `objects.modalities` | Broad categories of data represented in BIDS, roughly matching recording instrument | General terms | +| `objects.entities` | Name-value pairs appearing in filenames | Name/value terms | +| `objects.metadata` | Name-value pairs appearing in JSON files | Name/value terms | +| `objects.columns` | Column headings and values appearing in TSV files | Name/value terms | +| `objects.datatypes` | Subdirectories that organize files by type (such as `anat`, `eeg`) | Value terms | +| `objects.suffixes` | Filename suffixes that describe the contents of the file | Value terms | +| `objects.extensions` | Filename component that describe the format of the file | Value terms | +| `objects.formats` | Terms that define the forms values (for example, in metadata) might take | Formats | +| `objects.files` | Files and directories that may appear at the root of a dataset | Files | +| `objects.enums` | Full descriptions of enumerated values used in other sub-namespaces | Value terms | + +Because these objects vary, the contents of each namespace can vary. + +Common fields to all objects: + +| Field | Description | +| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | +| `description` | A description of the term that can be understood that should not depend on particular surrounding text; may contain markdown for rendering | +| `display_name` | A human-friendly name, for tools to display; may include spaces | + +The name/value terms groups (`entities`, `metadata` and `columns`) define terms where +a name, when present, has a given meaning, and its value may be restricted. + +These objects additionally have the field: + +| Field | Description | +| -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `name` | For terms that can take on multiple values (such as entities, metadata fields), the name of the term as it appears in the specification and in a dataset; must be alphanumeric; mutually exclusive with `value` | +| `type` | The type (such as `string`, `integer`, `object`) of values the term describes | +| `format` | The format of the term (defined in `objects.formats`) | + +Value terms groups (`datatypes`, `suffixes`, `extensions`) define terms where a field +can take on multiple values. +For example, a file has one datatype, as compared to a collection of entities. + +These objects may have the fields: + +| Field | Description | +| ------- | ---------------------------------------------------------------------------------------------------------------- | +| `value` | For terms that cannot take on multiple values (for example suffixes or extensions), the string value of the term | + +The `formats` terms provide one additional field: + +| Field | Description | +| --------- | ----------------------------------------------------------- | +| `pattern` | Regular expression validating a string rendering of a value | + +#### Value constraints + +For name/value terms, the `type` and `format` fields allow constraints to be placed on +the values described by the names. + +Additional fields may apply to further constrain the type: + +| Field | Description | +| -------------------------------------- | --------------------------------------------- | +| `maximum`/`minimum`/`exclusiveMinimum` | Value ranges for `integer` and `number` types | +| `maxValue`/`minValue` | Value ranges for `integer` and `number` types | +| `maxItems`/`minItems` | Size ranges for `array` types | +| `enum` | List of accepted values for `string` types | + +Some values may be more flexible, allowing multiple possible values, or may be +arrays or objects: + +| Field | Description | +| ---------------------- | --------------------------------------------------------------------------------------------- | +| `anyOf` | A list of constraints, any of which could apply | +| `items` | The array described contains values whose types are constrained | +| `properties` | The object described has a given set of fields; the values of these fields may be constrained | +| `additionalProperties` | The object described has constraints on its values, but not the names | + +### On re-used objects with different definitions + +In a few cases, two objects with the same name appear multiple times in the specification. +When this happens, it is preferred to find a common definition, and clarify it in the rules (see below). +However, in some cases, the object description and permissible values differ, and it needs to be defined +as two separate objects. + +Consider the following examples: + +```yaml +# reference column for channels.tsv files for EEG data +reference__eeg: + name: reference + display_name: Electrode reference + description: | + Name of the reference electrode(s). + This column is not needed when it is common to all channels. + In that case the reference electrode(s) can be specified in `*_eeg.json` as `EEGReference`). + type: string +# reference column for channels.tsv files for iEEG data +reference__ieeg: + name: reference + display_name: Electrode reference + description: | + Specification of the reference (for example, `mastoid`, `ElectrodeName01`, `intracranial`, `CAR`, `other`, `n/a`). + If the channel is not an electrode channel (for example, a microphone channel) use `n/a`. + anyOf: + - type: string + - type: string + enum: + - n/a +``` + +Here, the TSV column `"reference"` means different things when used for EEG data, +as compared to iEEG data, so two definitions are needed. +Because columns use `snake_case` (meaning they can be expected to contain underscores), +two underscores are needed to separate the column name from the string that indicates the use of the term. + +The convention can be summed up in the following rules: + +1. Each specific term takes on the form `_`, where `` is the common name that + the two (or more) terms share, `` indicates when the specific term applies. + +1. If the `` appears in `snake_case` then `` begins with an extra `_`. + +#### Valid fields for definitions by sub-namespace + +- `objects.common_principles` + | Field | Description | + | -------------- | ------------------- | + | `display_name` | Human-friendly name | + | `description` | Term definition | + +- `objects.modalities` + | Field | Description | + | -------------- | ------------------- | + | `display_name` | Human-friendly name | + | `description` | Term definition | + +- `objects.entities` + + | Field | Description | + | -------------- | ------------------------------------------------------- | + | `display_name` | Human-friendly name | + | `description` | Term definition | + | `name` | Key of entity, such as `sub` or `ses` | + | `type` | Type of value (always `string`) | + | `format` | Permissible format of values, either `label` or `index` | + | `enum` | Exclusive list of valid values, if present | + + Note that descriptions should apply to *all* uses of the entity; if additional information + applies in certain contexts, that should be written in the specification, and not the schema. + +- `objects.metadata` + | Field | Description | + | -------------- | ------------------------------------------------------------------------------------ | + | `display_name` | Human-friendly name | + | `description` | Term definition | + | `name` | Name of field in JSON object (in `CamelCase`) | + | `unit` | Interpretation of numeric values | + | `type` | Type of value (one of `array`, `string`, `integer`, `number`, `object` or `boolean`) | + | `format` | Permissible format of values, from definitions in `objects.formats` | + | `enum` | Exclusive list of valid values, if present | + | `maximum` | Maximum for numeric values | + | `minimum` | Minimum for numeric values | + | `*` | JSON-schema fields to further constrain values | + +- `objects.columns` + | Field | Description | + | -------------- | ------------------------------------------------------------------- | + | `display_name` | Human-friendly name | + | `description` | Term definition | + | `name` | Name of column in TSV file (in `snake_case`) | + | `unit` | Interpretation of numeric values | + | `type` | Type of value | + | `format` | Permissible format of values, from definitions in `objects.formats` | + | `pattern` | Regular expression constraining string values | + | `enum` | Exclusive list of valid values, if present | + | `maximum` | Maximum for numeric values | + | `minimum` | Minimum for numeric values | + | `*` | JSON-schema fields to further constrain values | + +- `objects.datatypes` + | Field | Description | + | -------------- | -------------------------- | + | `display_name` | Human-friendly name | + | `description` | Term definition | + | `value` | String value of `datatype` | + +- `objects.suffixes` + | Field | Description | + | -------------- | -------------------------------------------------------------- | + | `display_name` | Human-friendly name | + | `description` | Term definition | + | `value` | String value of `suffix` | + | `unit` | Interpretation of values in a data file with the given suffix | + | `maxValue` | Maximum permissible value in a data file with the given suffix | + | `minValue` | Minimum permissible value in a data file with the given suffix | + | `anyOf` | Used to describe multiple permissible units | + +- `objects.extensions` + | Field | Description | + | -------------- | --------------------------- | + | `display_name` | Human-friendly name | + | `description` | Term definition | + | `value` | String value of `extension` | + +- `objects.formats` + | Field | Description | + | -------------- | ---------------------------------- | + | `display_name` | Human-friendly name | + | `description` | Term definition | + | `pattern` | Regular expression defining format | + +- `objects.files` + | Field | Description | + | -------------- | ------------------------------------------------------------------------------------ | + | `display_name` | Human-friendly name | + | `description` | Term definition | + | `file_type` | Indicator that the file is a regular file (`"regular"`) or directory (`"directory"`) | + +- `objects.enums` + | Field | Description | + | -------------- | ---------------------- | + | `display_name` | Human-friendly name | + | `description` | Term definition | + | `value` | String value of `enum` | + +## Rule files + +The `rules.*` namespace contains most of the validatable content of the schema, +apart from value constraints that can be encoded in `objects`. + +There are several types of rule, and this section is subject to reconsolidation as +patterns are found. + +### Core concepts + +Core concepts are [expressions](#expressions) (defined above), requirement levels and issues. + +#### Requirement levels and severity + +BIDS follows RFC 2119 and has three requirement levels: OPTIONAL, RECOMMENDED and REQUIRED. +In the schema, we use `optional`, `recommended` and `required`. + +A rule interpreter (validator) is expected to treat: +- missing REQUIRED data/metadata as an error, +- missing RECOMMENDED data/metadata as a warning, +- and silently pass over missing OPTIONAL data. + +BIDS also defines a level `DEPRECATED`, rendered in the schema as `deprecated`, +and corresponding to a warning if the data/metadata is present. + +#### Issues + +Issues are messages intended to be communicated to a dataset curator to indicate an issue +with their dataset. + +They have a code and severity as well: + +| Field | Description | +| --------- | ---------------------------------------------- | +| `code` | Issue identifier, such as `EVENTS_TSV_MISSING` | +| `level` | Issue severity (`warning` or `error`) | +| `message` | Message for display to a user | + +A level of `warning` corresponds to a rule in the specification that is RECOMMENDED, +while a level of `error` corresponds to a rule that is REQUIRED. + +In some cases, an issue is contained next to a `level: required` or `level: recommended` +as part of a larger rule. +In these cases, the `level` field should be omitted from the issue +to avoid duplication or conflict. + +### Filename construction rules + +A significant portion of BIDS is devoted to the naming of files, +and almost all filenames consist of entities, a suffix, an extension, and a data type. +Exceptions will be noted below. + +`rules.files` contains the following subdivisions. + +| Namespace | Description | +| --------------------------- | ----------------------------------------------------------------------------------------- | +| `rules.files.common.core` | Files and directories that reside at the top level of datasets | +| `rules.files.common.tables` | Tabular metadata files that associate metadata with entities | +| `rules.files.raw.*` | Raw data and metadata files that have entities, suffixes, datatypes and extensions | +| `rules.files.deriv.*` | Derivative data and metadata files that have entities, suffixes, datatypes and extensions | + +#### Core files and directories + +`rules.files.common.core` describes files that have little-to-no variability in their form. +These either have a single `path` field, or a `stem` field and a list of `extensions`: + +| Field | Description | +| ------------ | ------------------------------------------------------------------------------------------------------------- | +| `level` | Requirement level of file, one of (`optional`, `recommended`, `required`, `deprecated`) | +| `path` | Location of file, relative to dataset root; mutually exclusive with `stem` and `extensions` | +| `stem` | Name of file, relative to dataset root, up to but not including the extension; mutually exclusive with `path` | +| `extensions` | List of valid extension strings, including the initial dot (`.`); mutually exclusive with `path` | + +These are the entries for `dataset_description.json` and `README`: + +```YAML +dataset_description: + level: required + path: dataset_description.json +README: + level: required + stem: README + extensions: + - '' + - .md + - .rst + - .txt +``` + +Here, `README` and `README.md` are both valid, while only `dataset_description.json` is permitted. + +#### Tabular metadata files + +`rules.files.common.tables` describes TSV files and their associated metadata, +including `participants.tsv`, `samples.tsv`, `*_sessions.tsv` and `*_scans.tsv`. +The first two use the `stem` field, while the latter two specify the entities used +to construct the filename. + +The valid fields are: + +| Field | Description | +| ------------ | ----------------------------------------------------------------------------------------------------------------- | +| `level` | Requirement level of file, one of (`optional`, `recommended`, `required`, `deprecated`) | +| `stem` | Name of file, relative to dataset root, up to but not including the extension; mutually exclusive with `entities` | +| `entities` | Object where the keys are entries in `objects.entities`. The value is a requirement level. | +| `extensions` | List of valid extension strings, including the initial dot (`.`) | + +For example: + +```YAML +participants: + level: optional + stem: participants + extensions: + - .tsv + - .json +sessions: + suffixes: + - sessions + extensions: + - .tsv + - .json + entities: + subject: required +``` + +Note that these files do not have a `datatype`, but otherwise follow the same rules as above. + +#### BIDS filenames + +`rules.files.raw` and `rules.files.deriv` contain series of related rules. +These are largely grouped by datatype, but file types that appear in multiple locations may be grouped together. + +The files described take the form: + +```plain +[sub-