from bids.grabbids import BIDSLayout
layout = BIDSLayout("/data/ds102/")

!tree /data/ds102/

"""
Explanation: Data input for BIDS datasets
DataGrabber and SelectFiles are great if you are dealing with generic datasets with arbitrary organization. However if you have decided to use Brain Imaging Data Structure (BIDS) to organized your data (or got your hands on a BIDS dataset) you can take advanted of a formal structure BIDS imposes. In this short tutorial you will learn how to do this.
pybids - a Python API for working with BIDS datasets
pybids is a lightweight python API for querying BIDS folder structure for specific files and metadata. You can install it from PyPi:
pip install pybids
Please note it should be already installed in the tutorial Docker image.
The layout object and simple queries
To begin working with pubids we need to initalize a layout object. We will need it to do all of our queries
End of explanation
"""


layout.get_subjects()

"""
Explanation: Let's figure out what are the subject labels in this dataset
End of explanation
"""


layout.get_modalities()

"""
Explanation: What modalities are included in this dataset?
End of explanation
"""


layout.get_types()

layout.get_types(modality='func')

"""
Explanation: What different data types are included in this dataset?
End of explanation
"""


layout.get_tasks()

"""
Explanation: What are the different tasks included in this dataset?
End of explanation
"""


layout.get(subject='01')

"""
Explanation: We can also ask for all of the data for a particular subject.
End of explanation
"""


layout.get(subject='01', type='bold', extensions=['nii', 'nii.gz'])

"""
Explanation: We can also ask for a specific subset of data. Note that we are using extension filter to get just the imaging data (BIDS allows both .nii and .nii.gz so we need to include both).
End of explanation
"""


[f.filename for f in layout.get(subject='01', type='T1w', extensions=['nii', 'nii.gz'])]

"""
Explanation: You probably noticed that this method does not only return the file paths, but objects with relevant query fields. We can easily extract just the file paths.
End of explanation
"""


def get_niftis(subject_id, data_dir):
    # Remember that all the necesary imports need to be INSIDE the function for the Function Interface to work!
    from bids.grabbids import BIDSLayout
    
    layout = BIDSLayout(data_dir)
    
    bolds = [f.filename for f in layout.get(subject=subject_id, type='bold', extensions=['nii', 'nii.gz'])]
    
    return bolds

get_niftis('01', '/data/ds102')

"""
Explanation: Exercise 1:
List all of the BOLD files for flanker task for subject 03, but only from the second run
Including pybids in your nipype workflow
This is great, but what we really want is to include this into our nipype workflows. How to do this? We can create our own custom BIDSDataGrabber using a Function Interface. First we need a plain Python function that for a given subject label and dataset location will return list of BOLD and T1w files.
End of explanation
"""


from nipype.pipeline import Node, MapNode, Workflow
from nipype.interfaces.utility import IdentityInterface, Function

BIDSDataGrabber = Node(Function(function=get_niftis, input_names=["subject_id",
                                       "data_dir"],
                                   output_names=["bolds", 
                                        "T1ws"]), name="BIDSDataGrabber")
BIDSDataGrabber.inputs.data_dir = "/data/ds102"

BIDSDataGrabber.inputs.subject_id='01'
res = BIDSDataGrabber.run()
res.outputs

"""
Explanation: Ok we got our function. Now we need to wrap it inside a Node object.
End of explanation
"""


def printMe(paths):
    print("\n\nanalyzing " + str(paths) + "\n\n")
    
analyzeBOLD = Node(Function(function=printMe, input_names=["paths"],
                            output_names=[]), name="analyzeBOLD")

wf = Workflow(name="bids_demo")
wf.connect(BIDSDataGrabber, "bolds", analyzeBOLD, "paths")
wf.run()

"""
Explanation: Works like a charm! (hopefully :) Lets put it in a workflow. We are not going to analyze any data, but for demostrantion purposes we will add a couple of nodes that pretend to analyze their inputs
End of explanation
"""


BIDSDataGrabber.iterables = ('subject_id', layout.get_subjects())
wf.run()

"""
Explanation: Exercise 2:
Modify the BIDSDataGrabber and the workflow to include T1ws.
Iterating over subject labels
In the previous example we demostrated how to use pybids to "analyze" one subject. How can we scale it for all subjects? Easy - using iterables.
End of explanation
"""


layout.get_metadata('/data/ds102/sub-01/func/sub-01_task-flanker_run-1_bold.nii.gz')

"""
Explanation: Accessing additional metadata
Querying different files is nice, but sometimes you want to access more metadata. For example RepetitionTime. pybids can help with that as well
End of explanation
"""


def printMetadata(path, data_dir):
    from bids.grabbids import BIDSLayout
    layout = BIDSLayout(data_dir)
    print("\n\nanalyzing " + path + "\nTR: "+ str(layout.get_metadata(path)["RepetitionTime"]) + "\n\n")
    
analyzeBOLD2 = MapNode(Function(function=printMetadata, input_names=["path", "data_dir"],
                             output_names=[]), name="analyzeBOLD2", iterfield="path")
analyzeBOLD2.inputs.data_dir = "/data/ds102/"

wf = Workflow(name="bids_demo")
wf.connect(BIDSDataGrabber, "bolds", analyzeBOLD2, "path")
wf.run()

"""
Explanation: Can we incorporate this into our pipeline? Yes we can!
End of explanation
"""


