def flip_if_needed(data_selection, nii_path, ind,
	output_filename='flipped.nii.gz',
	):
	"""Check based on a SAMRI data selection of Bruker Paravision scans, if the orientation is incorrect (“Prone”, terminologically correct, but corresponding to an incorrect “Supine” representation in Bruker ParaVision small animal scans), and flip the scan with respect to the axial axis if so.

	Parameters
	----------

	data_selection : pandas.DataFrame
		Pandas Dataframe object, with indices corresponding to the SAMRI data source iterator and columns including 'PV_position'.
	nii_path : str
		Path to a NIfTI file.
	ind : int
		Index of the `data_selection` entry.
	output_filename : str, optional
		String specifying the desired flipped NIfTI filename for potential flipped output.
	"""

	from os import path
	from samri.manipulations import flip_axis
	position = data_selection.iloc[ind]['PV_position']
	output_filename = path.abspath(path.expanduser(output_filename))
	nii_path = path.abspath(path.expanduser(nii_path))
	# The output_filename variable may not contain a format suffix, as it is used to generate associated files.
	if output_filename[-7:] != '.nii.gz' and output_filename[-4:] != '.nii':
		output_filename = '{}.nii.gz'.format(output_filename)
	if position == 'Prone':
		output_filename = flip_axis(nii_path, axis=2, out_path=output_filename)
	else:
		output_filename = nii_path
	return output_filename def get_data_selection(workflow_base,
	match={},
	exclude={},
	measurements=[],
	exclude_measurements=[],
	count_runs=False,
	fail_suffix='_failed',
	):
	"""
	Return a `pandas.DataFrame` object of the Bruker measurement directories located under a given base directory, and their respective scans, subjects, and tasks.

	Parameters
	----------
	workflow_base : str
		The path in which to query for Bruker measurement directories.
	match : dict
		A dictionary of matching criteria.
		The keys of this dictionary must be full BIDS key names (e.g. "task" or "acquisition"), and the values must be strings (e.g. "CogB") which, combined with the respective BIDS key, identify scans to be included (e.g. scans, the names of which containthe string "task-CogB" - delimited on either side by an underscore or the limit of the string).
	exclude : dict, optional
		A dictionary of exclusion criteria.
		The keys of this dictionary must be full BIDS key names (e.g. "task" or "acquisition"), and the values must be strings (e.g. "CogB") which, combined with the respective BIDS key, identify scans to be excluded(e.g. a scans, the names of which contain the string "task-CogB" - delimited on either side by an underscore or the limit of the string).
	measurements : list of str, optional
		A list of measurement directory names to be included exclusively (i.e. whitelist).
		If the list is empty, all directories (unless explicitly excluded via `exclude_measurements`) will be queried.
	exclude_measurements : list of str, optional
		A list of measurement directory names to be excluded from querying (i.e. a blacklist).

	Notes
	-----
	This data selector function is robust to `ScanProgram.scanProgram` files which have been truncated before the first detected match, but not to files truncated after at least one match.
	"""

	workflow_base = os.path.abspath(os.path.expanduser(workflow_base))

	if not measurements:
		measurements = os.listdir(workflow_base)
	measurement_path_list = [os.path.join(workflow_base,i) for i in measurements]

	selected_measurements=[]
	# Create a dummy path for bidsgrabber to parse file names from.
	# Ideally we could avoid this: https://github.com/bids-standard/pybids/issues/633
	bids_temppath = '/var/tmp/samri_bids_temppaths/'
	try:
		os.mkdir(bids_temppath)
	except FileExistsError:
		pass
	data = {}
	data['Name'] = ''
	data['BIDSVersion'] = ''
	with open(os.path.join(bids_temppath,'dataset_description.json'), 'w') as outfile:
		json.dump(data, outfile)
	layout = BIDSLayout(bids_temppath)
	#populate a list of lists with acceptable subject names, sessions, and sub_dir's
	for sub_dir in measurement_path_list:
		if sub_dir not in exclude_measurements:
			run_counter = 0
			selected_measurement = {}
			try:
				state_file = open(os.path.join(workflow_base,sub_dir,"subject"), "r")
				read_variables=0 #count variables so that breaking takes place after both have been read
				while True:
					current_line = state_file.readline()
					if "##$SUBJECT_id=" in current_line:
						entry=re.sub("[<>\n]", "", state_file.readline())
						if not match_exclude_ss(entry, match, exclude, selected_measurement, 'subject'):
							break
						read_variables +=1 #count recorded variables
					if "##$SUBJECT_study_name=" in current_line:
						entry=re.sub("[<>\n]", "", state_file.readline())
						if not match_exclude_ss(entry, match, exclude, selected_measurement, 'session'):
							break
						read_variables +=1 #count recorded variables
					if "##$SUBJECT_position=SUBJ_POS_" in current_line:
						m = re.match(r'^##\$SUBJECT_position=SUBJ_POS_(?P<position>.+?)$', current_line)
						position = m.groupdict()['position']
						read_variables +=1 #count recorded variables
						selected_measurement['PV_position'] = position
					if read_variables == 3:
						selected_measurement['measurement'] = sub_dir
						scan_program_file = os.path.join(workflow_base,sub_dir,"ScanProgram.scanProgram")
						scan_dir_resolved = False
						try:
							with open(scan_program_file) as search:
								for line in search:
									line_considered = True
									measurement_copy = deepcopy(selected_measurement)
									if re.match(r'^[ \t]+<displayName>[a-zA-Z0-9-_]+? \(E\d+\)</displayName>[\r\n]+', line):
										if fail_suffix and re.match(r'^.+?{} \(E\d+\)</displayName>[\r\n]+'.format(fail_suffix), line):
											continue
										m = re.match(r'^[ \t]+<displayName>(?P<scan_type>.+?) \(E(?P<number>\d+)\)</displayName>[\r\n]+', line)
										number = m.groupdict()['number']
										scan_type = m.groupdict()['scan_type']
										bids_keys = layout.parse_file_entities('{}/{}'.format(bids_temppath,scan_type))
										for key in match:
											# Session and subject fields are not recorded in scan_type and were already checked at this point.
											if key in ['session', 'subject']:
												continue
											try:
												if bids_keys[key] not in match[key]:
													line_considered = False
													break
											except KeyError:
												line_considered = False
												break
										if line_considered:
											measurement_copy['scan_type'] = str(scan_type).strip(' ')
											measurement_copy['scan'] = str(int(number))
											measurement_copy['run'] = run_counter
											scan_type, measurement_copy = assign_modality(scan_type, measurement_copy)
											measurement_copy.update(bids_keys)
											run_counter += 1
											selected_measurements.append(measurement_copy)
											scan_dir_resolved = True
							if not scan_dir_resolved:
								raise IOError()
						except IOError:
							for sub_sub_dir in os.listdir(os.path.join(workflow_base,sub_dir)):
								measurement_copy = deepcopy(selected_measurement)
								acqp_file_path = os.path.join(workflow_base,sub_dir,sub_sub_dir,"acqp")
								scan_subdir_resolved = False
								try:
									with open(acqp_file_path,'r') as search:
										for line in search:
											line_considered = True
											if scan_subdir_resolved:
												break
											if re.match(r'^(?!/)<[a-zA-Z0-9-_]+?-[a-zA-Z0-9-_]+?>[\r\n]+', line):
												if fail_suffix and re.match(r'^.+?{}$'.format(fail_suffix), line):
													continue
												number = sub_sub_dir
												m = re.match(r'^(?!/)<(?P<scan_type>.+?)>[\r\n]+', line)
												scan_type = m.groupdict()['scan_type']
												bids_keys = layout.parse_file_entities('{}/{}'.format(bids_temppath,scan_type))
												for key in match:
													# Session and subject fields are not recorded in scan_type and were already checked at this point.
													if key in ['session', 'subject']:
														continue
													try:
														if bids_keys[key] not in match[key]:
															line_considered = False
															break
													except KeyError:
														line_considered = False
														break
												if line_considered:
													measurement_copy['scan_type'] = str(scan_type).strip(' ')
													measurement_copy['scan'] = str(int(number))
													measurement_copy['run'] = run_counter
													scan_type, measurement_copy= assign_modality(scan_type, measurement_copy)
													measurement_copy.update(bids_keys)
													run_counter += 1
													selected_measurements.append(measurement_copy)
													scan_subdir_resolved = True
										else:
											pass
								except IOError:
									pass
						break #prevent loop from going on forever
			except IOError:
				print('Could not open {}'.format(os.path.join(workflow_base,sub_dir,"subject")))
				pass
	data_selection = pd.DataFrame(selected_measurements)
	try:
		shutil.rmtree(bids_temppath)
	except (FileNotFoundError, PermissionError):
		pass
	return data_selection def get_bids_scan(data_selection,
	bids_base="",
	ind_type="",
	selector=None,
	subject=None,
	session=None,
	extra=['acq','run'],
	):

	"""Description...

	Parameters
	----------
	bids_base : str
		Path to the bids base path.
	data_selection : pandas.DataFrame
		A `pandas.DataFrame` object as produced by `samri.preprocessing.extra_functions.get_data_selection()`.
	selector : iterable, optional
		The first method of selecting the subject and scan, this value should be a length-2 list or tuple containing the subject and sthe session to be selected.
	subject : string, optional
		This has to be defined if `selector` is not defined. The subject for which to return a scan directory.
	session : string, optional
		This has to be defined if `selector` is not defined. The session for which to return a scan directory.
	"""
	import os #for some reason the import outside the function fails
	import pandas as pd
	from samri.pipelines.utils import bids_naming

	filtered_data = []

	if selector:
		subject = selector[0]
		session = selector[1]
		filtered_data = data_selection[(data_selection["session"] == session)&(data_selection["subject"] == subject)]
	else:
		filtered_data = data_selection[data_selection.index==ind_type]

	if filtered_data.empty:
		raise Exception("SAMRIError: Does not exist: " + str(selector[0]) + str(selector[1]) + str(ind_type))
	else:
		subject = filtered_data['subject'].item()
		session = filtered_data['session'].item()
		try:
			typ = filtered_data['type'].item()
		except:
			typ = ""
		try:
			task = filtered_data['task'].item()
		except:
			task = ""
		subject_session = [subject, session]
		#scan_path = os.path.join(bids_base, 'sub-' + subject + '/', 'ses-' + session + '/', typ )

		try:
			nii_path = filtered_data['path'].item()
		except KeyError:
			nii_path = filtered_data['measurement'].item()
			nii_path += '/'+filtered_data['scan'].item()
			nii_name = bids_naming(subject_session, filtered_data,
					extra=extra,
					extension='',
					)
			scan_path = nii_path
		else:
			scan_path = os.path.dirname(nii_path)
			nii_name = os.path.basename(nii_path)

		eventfile_name = bids_naming(subject_session, filtered_data,
				extra=extra,
				extension='.tsv',
				suffix='events'
				)
		metadata_filename = bids_naming(subject_session, filtered_data,
				extra=extra,
				extension='.json',
				)

		dict_slice = filtered_data.to_dict('records')[0]

		return scan_path, typ, task, nii_path, nii_name, eventfile_name, subject_session, metadata_filename, dict_slice, ind_type def write_bids_metadata_file(scan_dir, extraction_dicts,
	out_file="bids_metadata.json",
	task='',
	):
	"""Create a sidecar JSON file according to the BIDS standard.

	Parameters
	----------

	scan_dir : str
		Path to the scan directory containing the acquisition protocol files.
	extraction_dicts : str
		A list of dictionaries which contain keys including `query_file` (which specifies the file, relative to `scan dir`, which to query), `regex` (which gives a regex expression which will be tested against each rowin the file until a match is found), and `field_name` (which specifies under what field name to record this value in the JSON file).
		Additionally, the following keys are also supported: `type` (a python class operator, e.g. `str` to which the value should be converted), and `scale` (a float with which the value is multiplied before recording in JSON).
	out_file : str, optional
		Path under which to save the resulting JSON.
	task_name : str, optional
		String value to assign to the "TaskName" field in the BIDS JSON.
		If this parameter evaluates to false, no "TaskName" will be recorded.
	"""

	import json
	import re
	from os import path
	from samri.pipelines.utils import parse_paravision_date

	out_file = path.abspath(path.expanduser(out_file))
	scan_dir = path.abspath(path.expanduser(scan_dir))
	metadata = {}

	# Extract parameters which are nicely accessible in the Bruker files:
	for extraction_dict in extraction_dicts:
		try:
			query_file = path.abspath(path.join(scan_dir,extraction_dict['query_file']))
			with open(query_file) as search:
				for line in search:
					if re.match(extraction_dict['regex'], line):
						m = re.match(extraction_dict['regex'], line)
						value = m.groupdict()['value']
						try:
							value = extraction_dict['type'](value)
						except KeyError:
							pass
						try:
							value = value * extraction_dict['scale']
						except KeyError:
							pass
						metadata[extraction_dict['field_name']] = value
						break
		except FileNotFoundError:
			pass

	# Extract DelayAfterTrigger
	try:
		query_file = path.abspath(path.join(scan_dir,'AdjStatePerScan'))
		read_line = False
		with open(query_file) as search:
			for line in search:
				if '##$AdjScanStateTime=( 2 )' in line:
					read_line = True
					continue
				if read_line:
					m = re.match(r'^<(?P<value>.*?)> <.*?>$', line)
					adjustments_start = m.groupdict()['value']
					adjustments_start = parse_paravision_date(adjustments_start)
					break
	except IOError:
		pass
	else:
		query_file = path.abspath(path.join(scan_dir,'acqp'))
		with open(query_file) as search:
			for line in search:
				if re.match(r'^##\$ACQ_time=<.*?>$', line):
					m = re.match(r'^##\$ACQ_time=<(?P<value>.*?)>$', line)
					adjustments_end = m.groupdict()['value']
					adjustments_end = parse_paravision_date(adjustments_end)
					break
		try:
			adjustments_duration = adjustments_end - adjustments_start
		except UnboundLocalError:
			metadata['DelayAfterTrigger'] = 0
		else:
			metadata['DelayAfterTrigger'] = adjustments_duration.total_seconds()

	if task:
		metadata['TaskName'] = task

	with open(out_file, 'w') as out_file_writeable:
		json.dump(metadata, out_file_writeable, indent=1)
		out_file_writeable.write("\n")  # `json.dump` does not add a newline at the end; we do it here.

	return out_file def write_bids_events_file(scan_dir,
	db_path="~/syncdata/meta.db",
	metadata_file='',
	out_file="events.tsv",
	prefer_labbookdb=False,
	timecourse_file='',
	task='',
	forced_dummy_scans=0.,
	):
	"""Adjust a BIDS event file to reflect delays introduced after the trigger and before the scan onset.

	Parameters
	----------

	scan_dir : str
		ParaVision scan directory path.
	db_path : str, optional
		LabbookDB database file path from which to source the evets profile for the identifier assigned to the `task` parameter.
	metadata_file : str, optional
		Path to a BIDS metadata file.
	out_file : str, optional
		Path to which to write the adjusted events file
	prefer_labbookdb : bool, optional
		Whether to query the events file in the LabbookDB database file first (rather than look for the events file in the scan directory).
	timecourse_file : str, optional
		Path to a NIfTI file.
	task : str, optional
		Task identifier from a LabbookDB database.

	Returns
	-------

	str : Path to which the adjusted events file was saved.
	"""

	import csv
	import sys
	import json
	import os
	import pandas as pd
	import nibabel as nib
	import numpy as np
	from datetime import datetime

	out_file = os.path.abspath(os.path.expanduser(out_file))
	scan_dir = os.path.abspath(os.path.expanduser(scan_dir))
	db_path = os.path.abspath(os.path.expanduser(db_path))

	if not prefer_labbookdb:
		try:
			scan_dir_contents = os.listdir(scan_dir)
			sequence_files = [i for i in scan_dir_contents if ("sequence" in i and "tsv" in i)]
			if sequence_files:
				sequence_file = os.path.join(scan_dir, sequence_files[0])
			else:
				timecourse_dir = os.path.dirname(timecourse_file)
				timecourse_name = os.path.basename(timecourse_file)
				stripped_name = timecourse_name.split('.', 1)[0].rsplit('_', 1)[0]
				sequence_file = os.path.join(timecourse_dir,stripped_name+'_events.tsv')
			mydf = pd.read_csv(sequence_file, sep="\s", engine='python')
		except:
			if os.path.isfile(db_path):
				from labbookdb.report.tracking import bids_eventsfile
				mydf = bids_eventsfile(db_path, task)
			else:
				return '/dev/null'
	else:
		try:
			if os.path.isfile(db_path):
				from labbookdb.report.tracking import bids_eventsfile
				mydf = bids_eventsfile(db_path, task)
			else:
				return '/dev/null'
		except ImportError:
			scan_dir_contents = os.listdir(scan_dir)
			sequence_files = [i for i in scan_dir_contents if ("sequence" in i and "tsv" in i)]
			if sequence_files:
				sequence_file = os.path.join(scan_dir, sequence_files[0])
			else:
				timecourse_dir = os.path.dirname(timecourse_file)
				timecourse_name = os.path.basename(timecourse_file)
				stripped_name = timecourse_name.split('.', 1)[0].rsplit('_', 1)[0]
				sequence_file = os.path.join(timecourse_dir,stripped_name+'_events.tsv')
			mydf = pd.read_csv(sequence_file, sep="\s", engine='python')

	timecourse_file = os.path.abspath(os.path.expanduser(timecourse_file))
	timecourse = nib.load(timecourse_file)
	zooms = timecourse.header.get_zooms()
	tr = float(zooms[-1])
	delay = 0.
	if forced_dummy_scans:
		delay = forced_dummy_scans * tr
	elif metadata_file:
		metadata_file = os.path.abspath(os.path.expanduser(metadata_file))

		with open(metadata_file) as metadata:
			    metadata = json.load(metadata)
		try:
			delay += metadata['NumberOfVolumesDiscardedByScanner'] * tr
		except:
			pass
		try:
			delay += metadata['DelayAfterTrigger']
		except:
			pass
	mydf['onset'] = mydf['onset'] - delay

	# 'n/a' specifically required by BIDS, as a consequence of its infinite wisdom
	mydf.to_csv(out_file, sep=str('\t'), na_rep='n/a', index=False)

	return out_file def write_bids_physio_file(scan_dir,
	out_file='physio.tsv',
	forced_dummy_scans=0.,
	nii_name=False,
	):
	"""Create a BIDS physiology recording ("physio") file, based on available data files in the scan directory.

	Parameters
	----------

	scan_dir : str
		ParaVision scan directory path, containing one or more files prefixed with "physio_".
	out_file : str, optional
		Path to which to write the adjusted physiology file.

	Returns
	-------

	out_file : str
		Path to which the collapsed physiology file was saved.
	out_file : str
		Path to which the collapsed physiology metadata file was saved.
	"""

	import csv
	import json
	import os

	if nii_name:
		nii_basename, ext = os.path.splitext(nii_name)
		if ext == '.gz':
			nii_basename, ext = os.path.splitext(nii_name)
		nii_basename_segments = nii_basename.split('_')
		nii_basename_segments = [i for i in nii_basename_segments if '-' in i]
		nii_basename = '_'.join(nii_basename_segments)
		out_file = '{}_physio.tsv'.format(nii_basename)

	out_file = os.path.abspath(os.path.expanduser(out_file))
	scan_dir = os.path.abspath(os.path.expanduser(scan_dir))

	physio_prefix = 'physio_'
	scan_dir_contents = os.listdir(scan_dir)
	physio_files = [i for i in scan_dir_contents if (physio_prefix in i)]
	if physio_files:
		physio_files = [os.path.join(scan_dir, i) for i in physio_files]
	else:
		return '/dev/null'

	physio_columns = []
	column_names = []
	start_times = []
	sampling_frequencies = []
	for physio_file in physio_files:
		if physio_file.endswith('.1D'):
			f = open(physio_file, 'r')
			physio_column = f.read()
			physio_column = physio_column.split(' ')
			physio_column = [float(i.split('*')[1]) for i in physio_column if i != '']
			physio_columns.append(physio_column)
			column_name = os.path.basename(physio_file)
			column_name, _ = os.path.splitext(column_name)
			column_name = column_name[len(physio_prefix):]
			column_names.append(column_name)
			start_times.append(60)
			sampling_frequencies.append(1)
	column_lengths = [len(i) for i in physio_columns]

	# check if all regressors are of the same length, as this would otherwise break list transposition.
	if len(set(column_lengths)) != 1:
		max_len = max(column_lengths)
		for physio_column in physio_columns:
			physio_column += ['n/a'] * (max_len - len(physio_column))

	physio_rows = [list(i) for i in zip(*physio_columns)]

	out_file = os.path.abspath(os.path.expanduser(out_file))
	with open(out_file, 'w') as tsvfile:
		writer = csv.writer(tsvfile, delimiter='\t', lineterminator='\n')
		for physio_row in physio_rows:
			writer.writerow(physio_row)

	if len(set(start_times)) == 1:
		start_times = start_times[0]
	if len(set(sampling_frequencies)) == 1:
		sampling_frequencies = sampling_frequencies[0]

	physio_metadata = {}
	physio_metadata['SamplingFrequency'] = sampling_frequencies
	physio_metadata['Start_Time'] = start_times
	physio_metadata['Columns'] = column_names

	physio_metadata_name,_ = os.path.splitext(out_file)
	out_metadata_file = '{}.json'.format(physio_metadata_name)
	out_metadata_file = os.path.abspath(os.path.expanduser(out_metadata_file))
	with open(out_metadata_file, 'w') as f:
		json.dump(physio_metadata, f, indent=1)
		f.write("\n")  # `json.dump` does not add a newline at the end; we do it here.

	return out_file, out_metadata_file BIDS_METADATA_EXTRACTION_DICTS = [
	{'field_name':'EchoTime',
		'query_file':'method',
		'regex':r'^##\$EchoTime=(?P<value>.*?)$',
		'scale': 1./1000.,
		'type': float,
		},
	{'field_name':'FlipAngle',
		'query_file':'visu_pars',
		'regex':r'^##\$VisuAcqFlipAngle=(?P<value>.*?)$',
		'type': float,
		},
	{'field_name':'Manufacturer',
		'query_file':'configscan',
		'regex':r'^##ORIGIN=(?P<value>.*?)$',
		},
	{'field_name':'NumberOfVolumesDiscardedByScanner',
		'query_file':'method',
		'regex':r'^##\$PVM_DummyScans=(?P<value>.*?)$',
		'type': int,
		},
	{'field_name':'ReceiveCoilName',
		'query_file':'configscan',
		'regex':r'.*?,COILTABLE,1#\$Name,(?P<value>.*?)#\$Id.*?',
		},
	{'field_name':'RepetitionTime',
		'query_file':'method',
		'regex':r'^##\$PVM_RepetitionTime=(?P<value>.*?)$',
		'scale': 1./1000.,
		'type': float,
		},
	{'field_name':'PulseSequenceType',
		'query_file':'method',
		'regex':r'^##\$Method=<Bruker:(?P<value>.*?)>$',
		},
	] def sessions_file(out_dir, df,
	):
	"""Create new sessions file for a particular subject, given the BIDS guidelines.
	https://bids-specification.readthedocs.io/en/latest/05-longitudinal-and-multi-site-studies.html#sessions-file

	Parameters
	----------

	out_dir : str
		Path to output root.
	df : pandas.DataFrame
		Pandas Dataframe containing columns including 'measurement', 'session', and 'subject'.
	"""

	import numpy as np

	for sub_dir in os.listdir(out_dir):
		sub_path = os.path.join(out_dir,sub_dir)
		if os.path.isdir(sub_path) and sub_dir[:4] == 'sub-':
			if os.path.isfile(os.path.join(sub_path,'{}_sessions.tsv'.format(sub_dir))):
				continue
			sessions_data=[]
			for ses_dir in os.listdir(sub_path):
				d={}
				if os.path.isdir(os.path.join(out_dir,sub_dir,ses_dir)) and ses_dir[:4] == 'ses-':
					acq_time = df.loc[(df['subject'] == sub_dir[4:]) & (df['session'] == ses_dir[4:]),'measurement'].tolist()[0]
					acq_time = os.path.basename(acq_time)
					acq_time = acq_time.split('_')[:2]
					acq_time = '_'.join(acq_time)
					try:
						acq_time = datetime.strptime(acq_time, '%Y%m%d_%H%M%S')
					except ValueError:
						d['acq_time'] = np.nan
					else:
						d['session_id'] = ses_dir
						d['acq_time'] = acq_time.isoformat()
					sessions_data.append(d)
			keys = sessions_data[0].keys()
			with open(os.path.join(sub_path,'{}_sessions.tsv'.format(sub_dir)), "w+") as f:
				dict_writer = csv.DictWriter(f, keys, delimiter='\t')
				dict_writer.writeheader()
				dict_writer.writerows(sessions_data) def ss_to_path(subject_session):
	"""Concatenate a (subject, session) or (subject, session, scan) tuple to a BIDS-style path"""
	subject = "sub-" + subject_session[0]
	session = "ses-" + subject_session[1]
	return "/".join([subject,session]) class Bru2(CommandLine):
	"""Uses bru2nii's Bru2 to convert Bruker files

	Examples
	========

	>>> from nipype.interfaces.bru2nii import Bru2
	>>> converter = Bru2()
	>>> converter.inputs.input_dir = "brukerdir"
	>>> converter.cmdline  # doctest: +ELLIPSIS
	'Bru2 -o .../nipype/testing/data/brukerdir brukerdir'
	"""
	input_spec = Bru2InputSpec
	output_spec = Bru2OutputSpec
	_cmd = "Bru2"

	def _list_outputs(self):
		outputs = self._outputs().get()
		if isdefined(self.inputs.output_filename):
			output_filename1 = os.path.abspath(self.inputs.output_filename)
		else:
			output_filename1 = self._gen_filename('output_filename')
		if self.inputs.compress:
			outputs["nii_file"] = output_filename1 + ".nii.gz"
		else:
			outputs["nii_file"] = output_filename1 + ".nii"
		return outputs

	def _gen_filename(self, name):
		if name == 'output_filename':
			outfile = os.path.join(
				os.getcwd(),
				os.path.basename(os.path.normpath(self.inputs.input_dir)))
			return outfile N_PROCS=max(mp.cpu_count()-2,2)
from os import path, remove
from samri.pipelines.extra_functions import flip_if_needed, get_data_selection, get_bids_scan, write_bids_metadata_file, write_bids_events_file, write_bids_physio_file, BIDS_METADATA_EXTRACTION_DICTS
from samri.pipelines.utils import sessions_file, ss_to_path
from samri.pipelines.extra_interfaces import Bru2
from samri.utilities import N_PROCS
import os
import argh
import re
import inspect
import json
import os
import shutil
import time
import nipype.interfaces.io as nio
import nipype.interfaces.utility as util
import nipype.pipeline.engine as pe
import pandas as pd
	datasink = pe.Node(nio.DataSink(), name='datasink')
	datasink.inputs.base_directory = out_dir
	datasink.inputs.parameterization = False

	workflow_connections = [
		(infosource, datasink, [(('subject_session',ss_to_path), 'container')]),
		]

	if functional_scan_types:
		if not os.path.exists(workdir):
			os.makedirs(workdir)
		f_data_selection.to_csv(path.join(workdir,'f_data_selection.csv'))
		get_f_scan = pe.Node(name='get_f_scan', interface=util.Function(function=get_bids_scan,input_names=inspect.getargspec(get_bids_scan)[0], output_names=[
			'scan_path', 'typ', 'task', 'nii_path', 'nii_name', 'eventfile_name', 'subject_session', 'metadata_filename', 'dict_slice', 'ind_type',
			]))
		get_f_scan.inputs.ignore_exception = True
		get_f_scan.inputs.data_selection = f_data_selection
		get_f_scan.inputs.bids_base = measurements_base
		get_f_scan.iterables = ("ind_type", func_ind)

		f_bru2nii = pe.Node(interface=Bru2(), name="f_bru2nii")
		f_bru2nii.inputs.actual_size = not inflated_size
		f_bru2nii.inputs.compress = True

		f_metadata_file = pe.Node(name='metadata_file', interface=util.Function(function=write_bids_metadata_file,input_names=inspect.getargspec(write_bids_metadata_file)[0], output_names=['out_file']))
		f_metadata_file.inputs.extraction_dicts = BIDS_METADATA_EXTRACTION_DICTS

		f_flip = pe.Node(name='f_flip', interface=util.Function(function=flip_if_needed,input_names=inspect.getargspec(flip_if_needed)[0], output_names=['out_file']))
		f_flip.inputs.data_selection = f_data_selection

		events_file = pe.Node(name='events_file', interface=util.Function(function=write_bids_events_file,input_names=inspect.getargspec(write_bids_events_file)[0], output_names=['out_file']))
		events_file.ignore_exception = True

		physio_file = pe.Node(name='physio_file', interface=util.Function(function=write_bids_physio_file,input_names=inspect.getargspec(write_bids_physio_file)[0], output_names=['out_file','out_metadata_file']))

		workflow_connections = [
			(get_f_scan, datasink, [(('subject_session',ss_to_path), 'container')]),
			(get_f_scan, f_bru2nii, [('scan_path', 'input_dir')]),
			(get_f_scan, f_bru2nii, [('nii_name', 'output_filename')]),
			(get_f_scan, f_flip, [('ind_type', 'ind')]),
			(get_f_scan, f_flip, [('nii_name', 'output_filename')]),
			(f_bru2nii, f_flip, [('nii_file', 'nii_path')]),
			(f_flip, datasink, [('out_file', 'func')]),
			(f_metadata_file, events_file, [('out_file', 'metadata_file')]),
			(f_bru2nii, events_file, [('nii_file', 'timecourse_file')]),
			(get_f_scan, f_metadata_file, [
				('metadata_filename', 'out_file'),
				('task', 'task'),
				('scan_path', 'scan_dir')
				]),
			(get_f_scan, events_file, [
				('eventfile_name', 'out_file'),
				('task', 'task'),
				('scan_path', 'scan_dir')
				]),
			(get_f_scan, physio_file, [
				('nii_name', 'nii_name'),
				('scan_path', 'scan_dir')
				]),
			(events_file, datasink, [('out_file', 'func.@events')]),
			(physio_file, datasink, [('out_file', 'func.@physio')]),
			(physio_file, datasink, [('out_metadata_file', 'func.@meta_physio')]),
			(f_metadata_file, datasink, [('out_file', 'func.@metadata')]),
			]
		crashdump_dir = path.join(out_base,workflow_name+'_crashdump')
		workflow_config = {'execution': {'crashdump_dir': crashdump_dir}}
		if debug:
			workflow_config['logging'] = {
				'workflow_level':'DEBUG',
				'utils_level':'DEBUG',
				'interface_level':'DEBUG',
				'filemanip_level':'DEBUG',
				'log_to_file':'true',
				}

		workflow = pe.Workflow(name=workdir_name)
		workflow.connect(workflow_connections)
		workflow.base_dir = path.join(out_base)
		workflow.config = workflow_config
		try:
			workflow.write_graph(dotfilename=path.join(workflow.base_dir,workdir_name,"graph_functional.dot"), graph2use="hierarchical", format="png")
		except OSError:
			print('We could not write the DOT file for visualization (`dot` function from the graphviz package). This is non-critical to the processing, but you should get this fixed.')

		#Execute the workflow
		if not keep_work or not keep_crashdump:
			try:
				workflow.run(plugin="MultiProc", plugin_args={'n_procs' : n_procs})
			except RuntimeError:
				pass
		else:
			workflow.run(plugin="MultiProc", plugin_args={'n_procs' : n_procs})
		if not keep_work:
			shutil.rmtree(path.join(workflow.base_dir,workdir_name))
		if not keep_crashdump:
			try:
				shutil.rmtree(crashdump_dir)
			except (FileNotFoundError, OSError):
				pass

	if diffusion_scan_types:
		# We check for the directory, since it gets deleted after a successful execution.
		if not os.path.exists(workdir):
			os.makedirs(workdir)
		d_data_selection.to_csv(path.join(workdir,'d_data_selection.csv'))
		get_d_scan = pe.Node(name='get_d_scan', interface=util.Function(function=get_bids_scan,input_names=inspect.getargspec(get_bids_scan)[0], output_names=[
			'scan_path', 'typ', 'task', 'nii_path', 'nii_name', 'eventfile_name', 'subject_session', 'metadata_filename', 'dict_slice', 'ind_type',
			]))
		get_d_scan.inputs.ignore_exception = True
		get_d_scan.inputs.data_selection = d_data_selection
		get_d_scan.inputs.extra = ['acq']
		get_d_scan.inputs.bids_base = measurements_base
		get_d_scan.iterables = ("ind_type", dwi_ind)

		d_bru2nii = pe.Node(interface=Bru2(), name="d_bru2nii")
		d_bru2nii.inputs.force_conversion=True
		d_bru2nii.inputs.actual_size = not inflated_size
		d_bru2nii.inputs.compress = True

		d_metadata_file = pe.Node(name='metadata_file', interface=util.Function(function=write_bids_metadata_file,input_names=inspect.getargspec(write_bids_metadata_file)[0], output_names=['out_file']))

		d_metadata_file.inputs.extraction_dicts = BIDS_METADATA_EXTRACTION_DICTS