From 9be1ada6ede3990ad91e0627f285814cd5416cbb Mon Sep 17 00:00:00 2001 From: Dan Brady Date: Mon, 1 Sep 2025 11:55:27 +0100 Subject: [PATCH 1/9] add tests for datatypes and modalities --- tests/test_context.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_context.py b/tests/test_context.py index c58c6f7..a88ff31 100644 --- a/tests/test_context.py +++ b/tests/test_context.py @@ -9,7 +9,8 @@ def test_load(examples): assert ds.dataset_description.Name.startswith('Synthetic dataset') assert ds.subjects.participant_id == [f'sub-{i:02d}' for i in range(1, 6)] assert sorted(ds.subjects.sub_dirs) == [f'sub-{i:02d}' for i in range(1, 6)] - + assert sorted(ds.datatypes) == ["anat", "beh", "func"] + assert sorted(ds.modalities) == ["beh", "mri"] def test_fileparts(examples, schema): tree = FileTree.read_from_filesystem(examples / 'synthetic') From ceccc3b55391aead3032b3ae87d23b90d32989c1 Mon Sep 17 00:00:00 2001 From: Dan Brady Date: Mon, 1 Sep 2025 11:59:34 +0100 Subject: [PATCH 2/9] update Dataset to take schema as parameter --- src/bids_validator/context.py | 1 + tests/test_context.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bids_validator/context.py b/src/bids_validator/context.py index 434018c..6a2284d 100644 --- a/src/bids_validator/context.py +++ b/src/bids_validator/context.py @@ -113,6 +113,7 @@ class Dataset: """A dataset object that loads properties on first access.""" tree: FileTree + schema: Namespace ignored: list[str] = attrs.field(factory=list) subjects: Subjects = attrs.field(init=False) diff --git a/tests/test_context.py b/tests/test_context.py index a88ff31..0584063 100644 --- a/tests/test_context.py +++ b/tests/test_context.py @@ -2,9 +2,9 @@ from bids_validator.types.files import FileTree -def test_load(examples): +def test_load(examples, schema): tree = FileTree.read_from_filesystem(examples / 'synthetic') - ds = context.Dataset(tree) + ds = context.Dataset(tree, schema) assert ds.dataset_description.Name.startswith('Synthetic dataset') assert ds.subjects.participant_id == [f'sub-{i:02d}' for i in range(1, 6)] From 0be8cb8f0e1c9ca1ba04361f7f43d8c5db746983 Mon Sep 17 00:00:00 2001 From: Dan Brady Date: Mon, 1 Sep 2025 12:00:33 +0100 Subject: [PATCH 3/9] add find_datatypes recursive method --- src/bids_validator/context.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/bids_validator/context.py b/src/bids_validator/context.py index 6a2284d..7b602d9 100644 --- a/src/bids_validator/context.py +++ b/src/bids_validator/context.py @@ -136,8 +136,27 @@ def modalities(self) -> list[str]: @cached_property def datatypes(self) -> list[str]: """List of datatypes found in the dataset.""" - ... - return [] + + def find_datatypes( + self, + tree: FileTree, + datatypes: Namespace, + result: set[str] | None = None + ) -> set[str|None]: + """ Recursively work through tree to find datatypes """ + if result is None: + result = set() + + for child_name, child_obj in tree.children.items(): + if not child_obj.is_dir: + continue + + if child_name in datatypes.keys(): + result.add(child_name) + else: + result = self.find_datatypes(child_obj, datatypes, result) + + return result @attrs.define From fde7300868bbda24e7877d2256e4b30622d857e1 Mon Sep 17 00:00:00 2001 From: Dan Brady Date: Mon, 1 Sep 2025 12:01:05 +0100 Subject: [PATCH 4/9] use find_dataypes in datatypes method --- src/bids_validator/context.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/bids_validator/context.py b/src/bids_validator/context.py index 7b602d9..0998440 100644 --- a/src/bids_validator/context.py +++ b/src/bids_validator/context.py @@ -136,6 +136,11 @@ def modalities(self) -> list[str]: @cached_property def datatypes(self) -> list[str]: """List of datatypes found in the dataset.""" + + datatypes = self.schema.objects.datatypes + result = self.find_datatypes(self.tree, datatypes) + + return list(result) def find_datatypes( self, From fd8d6f32695be1a6fe315fdb9b0c665ba7421b25 Mon Sep 17 00:00:00 2001 From: Dan Brady Date: Mon, 1 Sep 2025 12:01:24 +0100 Subject: [PATCH 5/9] add modalities method --- src/bids_validator/context.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/bids_validator/context.py b/src/bids_validator/context.py index 0998440..5b48ffd 100644 --- a/src/bids_validator/context.py +++ b/src/bids_validator/context.py @@ -130,8 +130,15 @@ def dataset_description(self) -> Namespace: @cached_property def modalities(self) -> list[str]: """List of modalities found in the dataset.""" - ... - return [] + result = set() + + modalities = self.schema.rules.modalities + for datatype in self.datatypes: + for mod_name, mod_dtypes in modalities.items(): + if datatype in mod_dtypes.datatypes: + result.add(mod_name) + + return list(result) @cached_property def datatypes(self) -> list[str]: From 8e37d7c7c706c0625ac74e0612be3009afd4b388 Mon Sep 17 00:00:00 2001 From: Dan Brady Date: Mon, 1 Sep 2025 12:10:48 +0100 Subject: [PATCH 6/9] make ruff happy --- src/bids_validator/context.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/bids_validator/context.py b/src/bids_validator/context.py index 5b48ffd..b74e821 100644 --- a/src/bids_validator/context.py +++ b/src/bids_validator/context.py @@ -137,25 +137,21 @@ def modalities(self) -> list[str]: for mod_name, mod_dtypes in modalities.items(): if datatype in mod_dtypes.datatypes: result.add(mod_name) - + return list(result) @cached_property def datatypes(self) -> list[str]: """List of datatypes found in the dataset.""" - datatypes = self.schema.objects.datatypes result = self.find_datatypes(self.tree, datatypes) return list(result) - + def find_datatypes( - self, - tree: FileTree, - datatypes: Namespace, - result: set[str] | None = None - ) -> set[str|None]: - """ Recursively work through tree to find datatypes """ + self, tree: FileTree, datatypes: Namespace, result: set[str] | None = None + ) -> set[str | None]: + """Recursively work through tree to find datatypes.""" if result is None: result = set() @@ -167,7 +163,7 @@ def find_datatypes( result.add(child_name) else: result = self.find_datatypes(child_obj, datatypes, result) - + return result From c917c6a970a8aaabafcddf23e82bd2931e6483da Mon Sep 17 00:00:00 2001 From: Dan Brady Date: Fri, 5 Sep 2025 15:23:24 +0100 Subject: [PATCH 7/9] PR feedback changes --- src/bids_validator/context.py | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/bids_validator/context.py b/src/bids_validator/context.py index b74e821..18711b4 100644 --- a/src/bids_validator/context.py +++ b/src/bids_validator/context.py @@ -144,27 +144,30 @@ def modalities(self) -> list[str]: def datatypes(self) -> list[str]: """List of datatypes found in the dataset.""" datatypes = self.schema.objects.datatypes - result = self.find_datatypes(self.tree, datatypes) + result = find_datatypes(self.tree, datatypes) return list(result) - def find_datatypes( - self, tree: FileTree, datatypes: Namespace, result: set[str] | None = None - ) -> set[str | None]: - """Recursively work through tree to find datatypes.""" - if result is None: - result = set() - for child_name, child_obj in tree.children.items(): - if not child_obj.is_dir: - continue +def find_datatypes( + tree: FileTree, datatypes: Namespace, result: set[str] | None = None, max_depth: int = 2 +) -> set[str]: + """Recursively work through tree to find datatypes.""" + if result is None: + result = set() - if child_name in datatypes.keys(): - result.add(child_name) - else: - result = self.find_datatypes(child_obj, datatypes, result) + for child_name, child_obj in tree.children.items(): + if not child_obj.is_dir: + continue + + if child_name in datatypes.keys(): + result.add(child_name) + elif max_depth == 0: + continue + else: + result = find_datatypes(child_obj, datatypes, result, max_depth=max_depth - 1) - return result + return result @attrs.define From 46cfcdcca19d9f21d91ba9cbedce98773101b637 Mon Sep 17 00:00:00 2001 From: Chris Markiewicz Date: Fri, 5 Sep 2025 10:27:49 -0400 Subject: [PATCH 8/9] Update src/bids_validator/context.py --- src/bids_validator/context.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/bids_validator/context.py b/src/bids_validator/context.py index 18711b4..7b940a1 100644 --- a/src/bids_validator/context.py +++ b/src/bids_validator/context.py @@ -143,10 +143,7 @@ def modalities(self) -> list[str]: @cached_property def datatypes(self) -> list[str]: """List of datatypes found in the dataset.""" - datatypes = self.schema.objects.datatypes - result = find_datatypes(self.tree, datatypes) - - return list(result) + return list(find_datatypes(self.tree, self.schema.objects.datatypes)) def find_datatypes( From 0a62540d15b2fbed1ad2d2ac3ddb8e9d08ff35d2 Mon Sep 17 00:00:00 2001 From: Dan Brady Date: Fri, 5 Sep 2025 15:34:49 +0100 Subject: [PATCH 9/9] add test for find_datatypes --- tests/test_context.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/test_context.py b/tests/test_context.py index 0584063..0cc6c45 100644 --- a/tests/test_context.py +++ b/tests/test_context.py @@ -1,6 +1,7 @@ from bids_validator import context from bids_validator.types.files import FileTree +import pytest def test_load(examples, schema): tree = FileTree.read_from_filesystem(examples / 'synthetic') @@ -12,6 +13,21 @@ def test_load(examples, schema): assert sorted(ds.datatypes) == ["anat", "beh", "func"] assert sorted(ds.modalities) == ["beh", "mri"] + +@pytest.mark.parametrize( + "depth, expected", + [ + (2, {"anat", "beh", "func"}), + (1, set()) + ]) +def test_find_datatypes(examples, schema, depth, expected): + tree = FileTree.read_from_filesystem(examples / 'synthetic') + datatypes = schema.objects.datatypes + + result = context.find_datatypes(tree, datatypes, max_depth=depth) + + assert result == expected + def test_fileparts(examples, schema): tree = FileTree.read_from_filesystem(examples / 'synthetic')