31
31
import subprocess
32
32
import re
33
33
import tempfile
34
+ from os .path import join , dirname
34
35
from warnings import warn
35
36
36
37
import sqlite3
37
38
38
39
from .. import config , logging
39
40
from ..utils .filemanip import copyfile , list_to_filename , filename_to_list
40
41
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
45
51
46
52
try :
47
53
import pyxnat
@@ -2717,3 +2723,121 @@ def _list_outputs(self):
2717
2723
outputs = self .output_spec ().get ()
2718
2724
outputs ['out_file' ] = out_file
2719
2725
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