Skip to content

Commit 2649d78

Browse files
authored
Merge pull request #2336 from mvdoc/bidsdatagrabber
FIX,REF: BIDSDataGrabber
2 parents ce6f37c + 889115d commit 2649d78

File tree

7 files changed

+201
-205
lines changed

7 files changed

+201
-205
lines changed

CHANGES

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
Upcoming release (0.14.1)
22
=========================
33

4+
* REF+FIX: Move BIDSDataGrabber to `interfaces.io` + fix correct default behavior (https://github.com/nipy/nipype/pull/2336)
45
* ENH: Add AFNI interface for 3dConvertDset (https://github.com/nipy/nipype/pull/2337)
56
* MAINT: Cleanup EngineBase (https://github.com/nipy/nipype/pull/2376)
67
* FIX: Robustly handled outputs of 3dFWHMx across different versions of AFNI (https://github.com/nipy/nipype/pull/2373)

nipype/interfaces/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@
1010
absolute_import)
1111
__docformat__ = 'restructuredtext'
1212

13-
from .io import DataGrabber, DataSink, SelectFiles
13+
from .io import DataGrabber, DataSink, SelectFiles, BIDSDataGrabber
1414
from .utility import IdentityInterface, Rename, Function, Select, Merge

nipype/interfaces/bids_utils.py

Lines changed: 0 additions & 149 deletions
This file was deleted.

nipype/interfaces/io.py

Lines changed: 124 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,23 +25,30 @@
2525
import glob
2626
import fnmatch
2727
import string
28+
import json
2829
import os
2930
import os.path as op
3031
import shutil
3132
import subprocess
3233
import re
3334
import tempfile
35+
from os.path import join, dirname
3436
from warnings import warn
3537

3638
import sqlite3
3739

3840
from .. import config, logging
3941
from ..utils.filemanip import copyfile, list_to_filename, filename_to_list
4042
from ..utils.misc import human_order_sorted, str2bool
41-
from .base import (TraitedSpec, traits, Str, File, Directory, BaseInterface,
42-
InputMultiPath, isdefined, OutputMultiPath,
43-
DynamicTraitedSpec, Undefined, BaseInterfaceInputSpec)
44-
from .bids_utils import BIDSDataGrabber
43+
from .base import (
44+
TraitedSpec, traits, Str, File, Directory, BaseInterface, InputMultiPath,
45+
isdefined, OutputMultiPath, DynamicTraitedSpec, Undefined, BaseInterfaceInputSpec)
46+
47+
have_pybids = True
48+
try:
49+
from bids import grabbids as gb
50+
except ImportError:
51+
have_pybids = False
4552

4653
try:
4754
import pyxnat
@@ -2717,3 +2724,116 @@ def _list_outputs(self):
27172724
outputs = self.output_spec().get()
27182725
outputs['out_file'] = out_file
27192726
return outputs
2727+
2728+
2729+
class BIDSDataGrabberInputSpec(DynamicTraitedSpec):
2730+
base_dir = Directory(exists=True,
2731+
desc='Path to BIDS Directory.',
2732+
mandatory=True)
2733+
output_query = traits.Dict(key_trait=Str,
2734+
value_trait=traits.Dict,
2735+
desc='Queries for outfield outputs')
2736+
raise_on_empty = traits.Bool(True, usedefault=True,
2737+
desc='Generate exception if list is empty '
2738+
'for a given field')
2739+
return_type = traits.Enum('file', 'namedtuple', usedefault=True)
2740+
2741+
2742+
class BIDSDataGrabber(IOBase):
2743+
2744+
""" BIDS datagrabber module that wraps around pybids to allow arbitrary
2745+
querying of BIDS datasets.
2746+
2747+
Examples
2748+
--------
2749+
2750+
By default, the BIDSDataGrabber fetches anatomical and functional images
2751+
from a project, and makes BIDS entities (e.g. subject) available for
2752+
filtering outputs.
2753+
2754+
>>> bg = BIDSDataGrabber()
2755+
>>> bg.inputs.base_dir = 'ds005/'
2756+
>>> bg.inputs.subject = '01'
2757+
>>> results = bg.run() # doctest: +SKIP
2758+
2759+
2760+
Dynamically created, user-defined output fields can also be defined to
2761+
return different types of outputs from the same project. All outputs
2762+
are filtered on common entities, which can be explicitly defined as
2763+
infields.
2764+
2765+
>>> bg = BIDSDataGrabber(infields = ['subject'], outfields = ['dwi'])
2766+
>>> bg.inputs.base_dir = 'ds005/'
2767+
>>> bg.inputs.subject = '01'
2768+
>>> bg.inputs.output_query['dwi'] = dict(modality='dwi')
2769+
>>> results = bg.run() # doctest: +SKIP
2770+
2771+
"""
2772+
input_spec = BIDSDataGrabberInputSpec
2773+
output_spec = DynamicTraitedSpec
2774+
_always_run = True
2775+
2776+
def __init__(self, infields=None, **kwargs):
2777+
"""
2778+
Parameters
2779+
----------
2780+
infields : list of str
2781+
Indicates the input fields to be dynamically created
2782+
"""
2783+
super(BIDSDataGrabber, self).__init__(**kwargs)
2784+
2785+
if not isdefined(self.inputs.output_query):
2786+
self.inputs.output_query = {"func": {"modality": "func"},
2787+
"anat": {"modality": "anat"}}
2788+
2789+
# If infields is empty, use all BIDS entities
2790+
if infields is None and have_pybids:
2791+
bids_config = join(dirname(gb.__file__), 'config', 'bids.json')
2792+
bids_config = json.load(open(bids_config, 'r'))
2793+
infields = [i['name'] for i in bids_config['entities']]
2794+
2795+
self._infields = infields or []
2796+
2797+
# used for mandatory inputs check
2798+
undefined_traits = {}
2799+
for key in self._infields:
2800+
self.inputs.add_trait(key, traits.Any)
2801+
undefined_traits[key] = kwargs[key] if key in kwargs else Undefined
2802+
2803+
self.inputs.trait_set(trait_change_notify=False, **undefined_traits)
2804+
2805+
def _run_interface(self, runtime):
2806+
if not have_pybids:
2807+
raise ImportError(
2808+
"The BIDSEventsGrabber interface requires pybids."
2809+
" Please make sure it is installed.")
2810+
return runtime
2811+
2812+
def _list_outputs(self):
2813+
layout = gb.BIDSLayout(self.inputs.base_dir)
2814+
2815+
# If infield is not given nm input value, silently ignore
2816+
filters = {}
2817+
for key in self._infields:
2818+
value = getattr(self.inputs, key)
2819+
if isdefined(value):
2820+
filters[key] = value
2821+
2822+
outputs = {}
2823+
for key, query in self.inputs.output_query.items():
2824+
args = query.copy()
2825+
args.update(filters)
2826+
filelist = layout.get(return_type=self.inputs.return_type, **args)
2827+
if len(filelist) == 0:
2828+
msg = 'Output key: %s returned no files' % key
2829+
if self.inputs.raise_on_empty:
2830+
raise IOError(msg)
2831+
else:
2832+
iflogger.warning(msg)
2833+
filelist = Undefined
2834+
2835+
outputs[key] = filelist
2836+
return outputs
2837+
2838+
def _add_output_traits(self, base):
2839+
return add_traits(base, list(self.inputs.output_query.keys()))

nipype/interfaces/tests/test_auto_BIDSDataGrabber.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
22
from __future__ import unicode_literals
3-
from ..bids_utils import BIDSDataGrabber
3+
from ..io import BIDSDataGrabber
44

55

66
def test_BIDSDataGrabber_inputs():

nipype/interfaces/tests/test_bids.py

Lines changed: 0 additions & 50 deletions
This file was deleted.

0 commit comments

Comments
 (0)