Skip to content

Commit f28ead6

Browse files
committed
Move BIDSGrabber to interfaces.io
1 parent 80d3f05 commit f28ead6

File tree

2 files changed

+128
-153
lines changed

2 files changed

+128
-153
lines changed

nipype/interfaces/bids_utils.py

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

nipype/interfaces/io.py

Lines changed: 128 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,23 @@
3131
import subprocess
3232
import re
3333
import tempfile
34+
from os.path import join, dirname
3435
from warnings import warn
3536

3637
import sqlite3
3738

3839
from .. import config, logging
3940
from ..utils.filemanip import copyfile, list_to_filename, filename_to_list
4041
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
42+
from .base import (
43+
TraitedSpec, traits, Str, File, Directory, BaseInterface, InputMultiPath,
44+
isdefined, OutputMultiPath, DynamicTraitedSpec, Undefined, BaseInterfaceInputSpec)
45+
46+
have_pybids = True
47+
try:
48+
from bids import grabbids as gb
49+
except ImportError:
50+
have_pybids = False
4551

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

0 commit comments

Comments
 (0)