Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

compatibility fixes for input dataset in BIDSVersion > 1.3 #76

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 24 additions & 9 deletions fmridenoise/interfaces/bids.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# Interface for loading preprocessed fMRI data and confounds table
from os.path import exists

from bids import BIDSLayout
from nipype.interfaces.io import IOBase
from nipype.utils.filemanip import copyfile
Expand Down Expand Up @@ -228,16 +230,29 @@ def validate_derivatives(bids_dir: str,
for derivative_path in derivatives_valid:
dataset_desc_path = os.path.join(derivative_path,
'dataset_description.json')
try:
if exists(dataset_desc_path):
with open(dataset_desc_path, 'r') as f:
dataset_desc = json.load(f)
scope.append(dataset_desc['PipelineDescription']['Name'])
except FileNotFoundError as e:
else:
raise MissingFile(f"{derivative_path} should contain" +
" dataset_description.json file") from e
except KeyError as e:
raise MissingFile(f"Key 'PipelineDescription.Name' is " +
"required in {dataset_desc_path} file") from e
" dataset_description.json file")
try:
major, minor, patch = (int(element) for element in str(dataset_desc['BIDSVersion']).split('.'))
except Exception:
raise Exception(f"Unable to parse bids version ({dataset_desc['BIDSVersion']}) into 3 parts")
if major == 1 and minor <= 3:
try:
scope.append(dataset_desc['PipelineDescription']['Name'])
except KeyError as e:
raise KeyError("Key 'PipelineDescription.Name' is "
f"required in {dataset_desc_path} file") from e
else:
pipeline = None
try:
for pipeline in dataset_desc['GeneratedBy']:
scope.append(pipeline['Name'])
except KeyError as e:
raise KeyError(f"Unable to extract Name from GeneratedBy: {pipeline} in file {dataset_desc_path}")

return derivatives_valid, scope

Expand Down Expand Up @@ -321,10 +336,10 @@ def get_entity_files(include_no_aroma: bool, include_aroma: bool, entity: dict)

return False, entity_files

subjects_to_exclude = []
# Select interface behavior depending on user behavior
if not tasks and not sessions and not subjects:
raise_missing = False
subjects_to_exclude = []
else:
raise_missing = True

Expand Down Expand Up @@ -362,7 +377,7 @@ def get_entity_files(include_no_aroma: bool, include_aroma: bool, entity: dict)
def _run_interface(self, runtime):

# Validate derivatives argument
derivatives, scope = BIDSValidate.validate_derivatives(
derivatives, _ = BIDSValidate.validate_derivatives(
bids_dir=self.inputs.bids_dir,
derivatives=self.inputs.derivatives
)
Expand Down
4 changes: 3 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
nibabel>=2.0
matplotlib<=3.2.1
seaborn>=0.9.0
numpy>=1.11
numpy>=1.11,<1.16.5
scipy>=1.4,<=1.5
nilearn>=0.4.0
pandas>=0.19,<1.2
jsonschema>=3.0.1
Expand Down
73 changes: 68 additions & 5 deletions tests/interfaces/bids/test_bids_validate.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import json
import os
import shutil
import tempfile
from bids import BIDSLayout
from fmridenoise.interfaces.bids import BIDSValidate, MissingFile, _lists_to_entities
import fmridenoise.pipelines as pipe
import unittest as ut
from os.path import join, dirname
from typing import List


# Data sets
testDir = dirname(__file__)
dummyDataPath = join(testDir, "dummy_complete")
Expand Down Expand Up @@ -54,7 +57,6 @@ def test_no_sessions_content(self):


class BidsValidateBasicPropertiesOnCompleteDataTestCase(ut.TestCase):

derivatives = ["fmriprep"]
tasks = ["audionback", "dualnback", "rest", "spatialnback"]
sessions = ["1", "2", "3", "4"]
Expand Down Expand Up @@ -125,9 +127,9 @@ def test_noAromaFiles(self):

def test_conf_raw_files(self):
filesCount = len(self.subjects) \
* (len(self.sessions) if self.sessions else 1) \
* len(self.tasks) \
* (len(self.runs) if self.runs else 1)
* (len(self.sessions) if self.sessions else 1) \
* len(self.tasks) \
* (len(self.runs) if self.runs else 1)
self.assertEqual(filesCount, len(self.bidsValidate._results['conf_raw']))

def test_conf_raw_json(self):
Expand Down Expand Up @@ -303,3 +305,64 @@ class BidsValidateOnRunsTestCase(BidsValidateBasicPropertiesOnCompleteDataTestCa
pipelinesDicts = list(map(pipe.load_pipeline_from_json, pipelines))
bids_dir = dummyRuns
maxDiff = None


class BidsValidate_validate_derivatives_TestCase(ut.TestCase):
bids_validate_1_3_content = {
"Name": "definitely not a fmriprep",
"BIDSVersion": "1.1.1",
"PipelineDescription": {
"Name": "NotAFmriprep",
"Version": "666",
"CodeURL": "https://no_url_there.com"
},
"CodeURL": "https://no_url_there.com",
"HowToAcknowledge": "Do not cite this",
"SourceDatasetsURLs": [
"https://no_url_there.com"
],
"License": "CC0"
}

bids_validate_1_4_content = {
"Name": "fMRIDenoise",
"BIDSVersion": "1.4.1",
"GeneratedBy": [
{
"Name": "fMRIDenoise",
"Version": "some version",
"CodeURL": "NO URL, DEVELOPMENT BUILD"
},
{
"Name": "Test",
"Version": "test_version",
"CodeURL": "NO URL"
}
]
}

def setUp(self) -> None:
self.bids_dir = tempfile.TemporaryDirectory()
self.derivatives_dir = join(self.bids_dir.name, 'derivatives')
self.derivative_name = 'test'
self.derivative_dir = join(self.derivatives_dir, self.derivative_name)
os.makedirs(self.derivative_dir)
self.derivatives_json_path = join(self.derivative_dir, 'dataset_description.json')

def tearDown(self) -> None:
self.bids_dir.cleanup()

def test_valid_old(self) -> None:
with open(self.derivatives_json_path, 'w') as f:
json.dump(self.bids_validate_1_3_content, f)
derivatives_valid, scope = BIDSValidate.validate_derivatives(self.bids_dir.name, self.derivative_name)
self.assertEqual([self.derivative_dir], derivatives_valid)
self.assertEqual(['NotAFmriprep'], scope)

def test_valid_new(self) -> None:
with open(self.derivatives_json_path, 'w') as f:
json.dump(self.bids_validate_1_4_content, f)
derivatives_valid, scope = BIDSValidate.validate_derivatives(self.bids_dir.name, self.derivative_name)
self.assertEqual([self.derivative_dir], derivatives_valid)
self.assertEqual(['fMRIDenoise', "Test"], scope)