Skip to content

FIX,REF: BIDSDataGrabber #2336

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

Merged
merged 8 commits into from
Jan 19, 2018
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
1 change: 1 addition & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
Upcoming release (0.14.1)
=========================

* REF+FIX: Move BIDSDataGrabber to `interfaces.io` + fix correct default behavior (https://github.com/nipy/nipype/pull/2336)
* ENH: Add AFNI interface for 3dConvertDset (https://github.com/nipy/nipype/pull/2337)
* MAINT: Cleanup EngineBase (https://github.com/nipy/nipype/pull/2376)
* FIX: Robustly handled outputs of 3dFWHMx across different versions of AFNI (https://github.com/nipy/nipype/pull/2373)
Expand Down
2 changes: 1 addition & 1 deletion nipype/interfaces/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@
absolute_import)
__docformat__ = 'restructuredtext'

from .io import DataGrabber, DataSink, SelectFiles
from .io import DataGrabber, DataSink, SelectFiles, BIDSDataGrabber
from .utility import IdentityInterface, Rename, Function, Select, Merge
149 changes: 0 additions & 149 deletions nipype/interfaces/bids_utils.py

This file was deleted.

128 changes: 124 additions & 4 deletions nipype/interfaces/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,30 @@
import glob
import fnmatch
import string
import json
import os
import os.path as op
import shutil
import subprocess
import re
import tempfile
from os.path import join, dirname
from warnings import warn

import sqlite3

from .. import config, logging
from ..utils.filemanip import copyfile, list_to_filename, filename_to_list
from ..utils.misc import human_order_sorted, str2bool
from .base import (TraitedSpec, traits, Str, File, Directory, BaseInterface,
InputMultiPath, isdefined, OutputMultiPath,
DynamicTraitedSpec, Undefined, BaseInterfaceInputSpec)
from .bids_utils import BIDSDataGrabber
from .base import (
TraitedSpec, traits, Str, File, Directory, BaseInterface, InputMultiPath,
isdefined, OutputMultiPath, DynamicTraitedSpec, Undefined, BaseInterfaceInputSpec)

have_pybids = True
try:
from bids import grabbids as gb
except ImportError:
have_pybids = False

try:
import pyxnat
Expand Down Expand Up @@ -2717,3 +2724,116 @@ def _list_outputs(self):
outputs = self.output_spec().get()
outputs['out_file'] = out_file
return outputs


class BIDSDataGrabberInputSpec(DynamicTraitedSpec):
base_dir = Directory(exists=True,
desc='Path to BIDS Directory.',
mandatory=True)
output_query = traits.Dict(key_trait=Str,
value_trait=traits.Dict,
desc='Queries for outfield outputs')
raise_on_empty = traits.Bool(True, usedefault=True,
desc='Generate exception if list is empty '
'for a given field')
return_type = traits.Enum('file', 'namedtuple', usedefault=True)


class BIDSDataGrabber(IOBase):

""" BIDS datagrabber module that wraps around pybids to allow arbitrary
querying of BIDS datasets.

Examples
--------

By default, the BIDSDataGrabber fetches anatomical and functional images
from a project, and makes BIDS entities (e.g. subject) available for
filtering outputs.

>>> bg = BIDSDataGrabber()
>>> bg.inputs.base_dir = 'ds005/'
>>> bg.inputs.subject = '01'
>>> results = bg.run() # doctest: +SKIP


Dynamically created, user-defined output fields can also be defined to
return different types of outputs from the same project. All outputs
are filtered on common entities, which can be explicitly defined as
infields.

>>> bg = BIDSDataGrabber(infields = ['subject'], outfields = ['dwi'])
>>> bg.inputs.base_dir = 'ds005/'
>>> bg.inputs.subject = '01'
>>> bg.inputs.output_query['dwi'] = dict(modality='dwi')
>>> results = bg.run() # doctest: +SKIP

"""
input_spec = BIDSDataGrabberInputSpec
output_spec = DynamicTraitedSpec
_always_run = True

def __init__(self, infields=None, **kwargs):
"""
Parameters
----------
infields : list of str
Indicates the input fields to be dynamically created
"""
super(BIDSDataGrabber, self).__init__(**kwargs)

if not isdefined(self.inputs.output_query):
self.inputs.output_query = {"func": {"modality": "func"},
"anat": {"modality": "anat"}}

# If infields is empty, use all BIDS entities
if infields is None and have_pybids:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch

bids_config = join(dirname(gb.__file__), 'config', 'bids.json')
bids_config = json.load(open(bids_config, 'r'))
infields = [i['name'] for i in bids_config['entities']]

self._infields = infields or []

# used for mandatory inputs check
undefined_traits = {}
for key in self._infields:
self.inputs.add_trait(key, traits.Any)
undefined_traits[key] = kwargs[key] if key in kwargs else Undefined

self.inputs.trait_set(trait_change_notify=False, **undefined_traits)

def _run_interface(self, runtime):
if not have_pybids:
raise ImportError(
"The BIDSEventsGrabber interface requires pybids."
" Please make sure it is installed.")
return runtime

def _list_outputs(self):
layout = gb.BIDSLayout(self.inputs.base_dir)

# If infield is not given nm input value, silently ignore
filters = {}
for key in self._infields:
value = getattr(self.inputs, key)
if isdefined(value):
filters[key] = value

outputs = {}
for key, query in self.inputs.output_query.items():
args = query.copy()
args.update(filters)
filelist = layout.get(return_type=self.inputs.return_type, **args)
if len(filelist) == 0:
msg = 'Output key: %s returned no files' % key
if self.inputs.raise_on_empty:
raise IOError(msg)
else:
iflogger.warning(msg)
filelist = Undefined

outputs[key] = filelist
return outputs

def _add_output_traits(self, base):
return add_traits(base, list(self.inputs.output_query.keys()))
2 changes: 1 addition & 1 deletion nipype/interfaces/tests/test_auto_BIDSDataGrabber.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
from __future__ import unicode_literals
from ..bids_utils import BIDSDataGrabber
from ..io import BIDSDataGrabber


def test_BIDSDataGrabber_inputs():
Expand Down
50 changes: 0 additions & 50 deletions nipype/interfaces/tests/test_bids.py

This file was deleted.

Loading