30
30
import subprocess
31
31
import re
32
32
import tempfile
33
+ from os .path import join , dirname
33
34
from warnings import warn
34
35
35
36
import sqlite3
40
41
from .base import (
41
42
TraitedSpec , traits , Str , File , Directory , BaseInterface , InputMultiPath ,
42
43
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
44
50
45
51
try :
46
52
import pyxnat
@@ -2630,3 +2636,121 @@ def _list_outputs(self):
2630
2636
outputs = self .output_spec ().get ()
2631
2637
outputs ['out_file' ] = out_file
2632
2638
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