Skip to content

Commit 8f5f380

Browse files
committed
Move BIDSGrabber to interfaces.io
1 parent 373bddd commit 8f5f380

File tree

2 files changed

+125
-148
lines changed

2 files changed

+125
-148
lines changed

nipype/interfaces/bids_utils.py

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

nipype/interfaces/io.py

Lines changed: 125 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import subprocess
3131
import re
3232
import tempfile
33+
from os.path import join, dirname
3334
from warnings import warn
3435

3536
import sqlite3
@@ -40,7 +41,12 @@
4041
from .base import (
4142
TraitedSpec, traits, Str, File, Directory, BaseInterface, InputMultiPath,
4243
isdefined, OutputMultiPath, DynamicTraitedSpec, Undefined, BaseInterfaceInputSpec)
43-
from .bids_utils import BIDSDataGrabber
44+
45+
have_pybids = True
46+
try:
47+
from bids import grabbids as gb
48+
except ImportError:
49+
have_pybids = False
4450

4551
try:
4652
import pyxnat
@@ -2630,3 +2636,121 @@ def _list_outputs(self):
26302636
outputs = self.output_spec().get()
26312637
outputs['out_file'] = out_file
26322638
return outputs
2639+
2640+
2641+
class BIDSDataGrabberInputSpec(DynamicTraitedSpec):
2642+
base_dir = Directory(exists=True,
2643+
desc='Path to BIDS Directory.',
2644+
mandatory=True)
2645+
output_query = traits.Dict(key_trait=Str,
2646+
value_trait=traits.Dict,
2647+
desc='Queries for outfield outputs')
2648+
raise_on_empty = traits.Bool(True, usedefault=True,
2649+
desc='Generate exception if list is empty '
2650+
'for a given field')
2651+
return_type = traits.Enum('file', 'namedtuple', usedefault=True)
2652+
2653+
2654+
class BIDSDataGrabber(IOBase):
2655+
2656+
""" BIDS datagrabber module that wraps around pybids to allow arbitrary
2657+
querying of BIDS datasets.
2658+
2659+
Examples
2660+
--------
2661+
2662+
By default, the BIDSDataGrabber fetches anatomical and functional images
2663+
from a project, and makes BIDS entities (e.g. subject) available for
2664+
filtering outputs.
2665+
2666+
>>> bg = BIDSDataGrabber()
2667+
>>> bg.inputs.base_dir = 'ds005/'
2668+
>>> bg.inputs.subject = '01'
2669+
>>> results = bg.run() # doctest: +SKIP
2670+
2671+
2672+
Dynamically created, user-defined output fields can also be defined to
2673+
return different types of outputs from the same project. All outputs
2674+
are filtered on common entities, which can be explicitly defined as
2675+
infields.
2676+
2677+
>>> bg = BIDSDataGrabber(infields = ['subject'], outfields = ['dwi'])
2678+
>>> bg.inputs.base_dir = 'ds005/'
2679+
>>> bg.inputs.subject = '01'
2680+
>>> bg.inputs.output_query['dwi'] = dict(modality='dwi')
2681+
>>> results = bg.run() # doctest: +SKIP
2682+
2683+
"""
2684+
input_spec = BIDSDataGrabberInputSpec
2685+
output_spec = DynamicTraitedSpec
2686+
_always_run = True
2687+
2688+
def __init__(self, infields=None, outfields=None, **kwargs):
2689+
"""
2690+
Parameters
2691+
----------
2692+
infields : list of str
2693+
Indicates the input fields to be dynamically created
2694+
2695+
outfields: list of str
2696+
Indicates output fields to be dynamically created.
2697+
If no matching items, returns Undefined.
2698+
"""
2699+
super(BIDSDataGrabber, self).__init__(**kwargs)
2700+
2701+
if not isdefined(self.inputs.output_query):
2702+
self.inputs.output_query = {"func": {"modality": "func"},
2703+
"anat": {"modality": "anat"}}
2704+
2705+
# If infields is empty, use all BIDS entities
2706+
if infields is None and have_pybids:
2707+
bids_config = join(dirname(gb.__file__), 'config', 'bids.json')
2708+
bids_config = json.load(open(bids_config, 'r'))
2709+
infields = [i['name'] for i in bids_config['entities']]
2710+
# if outfields is not set, return those defined in infields
2711+
if infields and outfields is None:
2712+
outfields = [infield for infield in infields]
2713+
2714+
self._infields = infields or []
2715+
self._outfields = outfields or []
2716+
2717+
# used for mandatory inputs check
2718+
undefined_traits = {}
2719+
for key in self._infields:
2720+
self.inputs.add_trait(key, traits.Any)
2721+
undefined_traits[key] = kwargs[key] if key in kwargs else Undefined
2722+
2723+
self.inputs.trait_set(trait_change_notify=False, **undefined_traits)
2724+
2725+
def _run_interface(self, runtime):
2726+
if not have_pybids:
2727+
raise ImportError(
2728+
"The BIDSEventsGrabber interface requires pybids."
2729+
" Please make sure it is installed.")
2730+
return runtime
2731+
2732+
def _list_outputs(self):
2733+
layout = gb.BIDSLayout(self.inputs.base_dir)
2734+
2735+
# If infield is not given nm input value, silently ignore
2736+
filters = {}
2737+
for key in self._infields:
2738+
value = getattr(self.inputs, key)
2739+
if isdefined(value):
2740+
filters[key] = value
2741+
2742+
outputs = {}
2743+
for key, query in self.inputs.output_query.items():
2744+
args = query.copy()
2745+
args.update(filters)
2746+
filelist = layout.get(return_type=self.inputs.return_type, **args)
2747+
if len(filelist) == 0:
2748+
msg = 'Output key: %s returned no files' % key
2749+
if self.inputs.raise_on_empty:
2750+
raise IOError(msg)
2751+
else:
2752+
iflogger.warning(msg)
2753+
filelist = Undefined
2754+
2755+
outputs[key] = filelist
2756+
return outputs

0 commit comments

Comments
 (0)