diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..24c71cbd --- /dev/null +++ b/Dockerfile @@ -0,0 +1,63 @@ +# Create Docker container that can run fLoc analysis. + +# Start with the Matlab r2017b runtime container +FROM flywheel/matlab-mcr:v93 + +MAINTAINER Michael Perry + + +############################ +# Install dependencies + +RUN apt-get update && apt-get install -y --force-yes \ + xvfb \ + xfonts-100dpi \ + xfonts-75dpi \ + xfonts-cyrillic \ + zip \ + unzip \ + jq \ + bc \ + tar \ + zip \ + wget \ + gawk \ + tcsh \ + libgomp1 \ + perl-modules \ + python-pip + + +############################ +# Download Freesurfer v6.0.0 from MGH and untar to /opt + +RUN wget -N -qO- ftp://surfer.nmr.mgh.harvard.edu/pub/dist/freesurfer/6.0.0/freesurfer-Linux-centos6_x86_64-stable-pub-v6.0.0.tar.gz | tar -xz -C /opt && \ + chown -R root:root /opt/freesurfer && \ + rm -rf /opt/freesurfer/MCRv80 /opt/freesurfer/average /opt/freesurfer/subjects /opt/freesurfer/trctrain /opt/freesurfer/tktools && \ + touch /opt/freesurfer/.license && \ + chmod 777 /opt/freesurfer/.license + + +# Set the diplay env variable for xvfb +ENV DISPLAY :1.0 + +# Install Flywheel-SDK +RUN pip install flywheel-sdk>=12.04 + +# ADD the Matlab Stand-Alone (MSA) into the container. +# Must be compiled prior to gear build - this will fail otherwise +COPY gear/bin/fLocGearRun \ + gear/bin/run_fLocGearRun.sh \ + /usr/local/bin/ + +# Make directory for flywheel spec (v0) +ENV FLYWHEEL /flywheel/v0 +RUN mkdir -p ${FLYWHEEL} + +# Copy and configure run script and metadata code +COPY gear/run.py ${FLYWHEEL}/run +RUN chmod +x ${FLYWHEEL}/run +COPY gear/manifest.json ${FLYWHEEL}/manifest.json + +# Configure entrypoint +ENTRYPOINT ["/flywheel/v0/run"] diff --git a/functions/fLocAnalysis.m b/functions/fLocAnalysis.m index 4015c964..45cdefea 100644 --- a/functions/fLocAnalysis.m +++ b/functions/fLocAnalysis.m @@ -98,10 +98,17 @@ % this change has been made to account for the problem of mixed up axes in some of % the sessions. for rr = 1: length(init_params.functionals) - unix([ sprintf('mri_convert --force_ras_good %s %s ', init_params.functionals{rr}, init_params.functionals{rr}) ]) + status = system(sprintf('mri_convert --force_ras_good %s %s ', init_params.functionals{rr}, init_params.functionals{rr})); + if status ~= 0 + error('Could not run mri_convert on %s', init_params.functionals{rr}) + end + end -unix([ sprintf('mri_convert --force_ras_good %s %s ', init_params.inplane, init_params.inplane) ]) +status = system(sprintf('mri_convert --force_ras_good %s %s ', init_params.inplane, init_params.inplane)); +if status ~= 0 + error('Could not run mri_convert on %s', init_params.inplane) +end nii = readFileNifti(init_params.functionals{1}); nslices = size(nii.data, 3); @@ -170,10 +177,7 @@ hi = initHiddenInplane('MotionComp', 1); baseScan = 1; targetScans = 1:length(init_params.functionals); [hi, M] = betweenScanMotComp(hi, 'MotionComp_RefScan1', baseScan, targetScans); - %%%%%%%%%%%%%%% changed this to create local paths MN 12/2018 %%%%%%%% - % fname = fullfile(session, 'Inplane', 'MotionComp_RefScan1', 'ScanMotionCompParams'); - fname = fullfile('Inplane', 'MotionComp_RefScan1', 'ScanMotionCompParams'); - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + fname = fullfile(session, 'Inplane', 'MotionComp_RefScan1', 'ScanMotionCompParams'); save(fname, 'M', 'baseScan', 'targetScans'); hi = selectDataType(hi, 'MotionComp_RefScan1'); saveSession; close all; diff --git a/functions/fLocAnalysisParams.m b/functions/fLocAnalysisParams.m index 24d89e2c..951dd742 100644 --- a/functions/fLocAnalysisParams.m +++ b/functions/fLocAnalysisParams.m @@ -38,6 +38,7 @@ % searches for all parfiles and nifti's in session directory [~, session_id] = fileparts(session); +[~, subject_id] = fileparts(mrvDirup(session)); niifiles = dir(fullfile(session, '*.nii.gz')); niifiles = {niifiles.name}; niifiles = niifiles(~contains(niifiles,'._')); parfiles = dir(fullfile(session, '*.par')); parfiles = {parfiles.name}; @@ -47,16 +48,11 @@ while sum(contains(lower(niifiles), ['run' num2str(num_runs + 1) '.nii.gz'])) >= 1 num_runs = num_runs + 1; nii_idx = find(contains(lower(niifiles), ['run' num2str(num_runs) '.nii.gz']), 1); - %%changed this and similar cases below to create local paths, MN 12/18 %%%%%%% - %niipaths{num_runs} = fullfile(session, niifiles{nii_idx}); niipaths{num_runs} = fullfile(niifiles{nii_idx}); - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% par_idx = find(contains(lower(parfiles), ['run' num2str(num_runs) '.par']), 1); if isempty(par_idx) fprintf('Error: no .par file found for run %d. \n\n', num_runs); return; else - - %parpaths{num_runs} = fullfile(session, parfiles{par_idx}); parpaths{num_runs} = fullfile(parfiles{par_idx}); end @@ -74,16 +70,14 @@ fprintf('Warning: no inplane scan found for session %s. Generating pseudo inplane file. \n\n', session_id); nii = niftiRead(niipaths{1}); nii.data = mean(nii.data, 4); niftiWrite(nii, 'PseudoInplane.nii.gz'); - % inplane = fullfile(session, 'PseudoInplane.nii.gz'); inplane = fullfile('PseudoInplane.nii.gz'); else - % inplane = fullfile(session, niifiles{inplane_idx}); inplane = fullfile(niifiles{inplane_idx}); end % get the durations of TR and events -nii = niftiRead(niipaths{1}); TR = nii.pixdim(4); -pid = fopen(parfiles{1}); +nii = niftiRead(fullfile(session,niipaths{1})); TR = nii.pixdim(4); +pid = fopen(fullfile(session, parpaths{1})); ln1 = fgetl(pid); ln1(ln1 == sprintf('\t')) = ''; ln2 = fgetl(pid); ln2(ln2 == sprintf('\t')) = ''; prts1 = deblank(strsplit(ln1, ' ')); prts2 = deblank(strsplit(ln2, ' ')); @@ -111,7 +105,7 @@ init_params.motionCompRefScan = 1; % run number of reference scan for between-scans compensation % necessary fields -init_params.sessionCode = session_id; % char array, local session data directory +init_params.sessionCode = session_id; % char array, local session data directory init_params.doAnalParams = 1; % logical, set GLM analysis parameters during intialization init_params.doSkipFrames = 1; % logical, clip countdown frames during initialization init_params.doPreprocessing = 1; % logical, do some preprocessing during initialization @@ -126,7 +120,7 @@ init_params.keepFrames = repmat([init_params.clip -1], num_runs, 1); % descriptive fields for the mrVista session -init_params.subject = session_id(1:find(session_id == '_') - 1); % char array with participant ID +init_params.subject = subject_id; init_params.description = 'localizer'; % char array describing session init_params.comments = 'Analyzed using fLoc'; % char array of comments init_params.annotations = annotations; % cell array of descriptions for each run diff --git a/functions/fLocAnalysisWrapper.m b/functions/fLocAnalysisWrapper.m index 7fe52ba3..0db6bc72 100644 --- a/functions/fLocAnalysisWrapper.m +++ b/functions/fLocAnalysisWrapper.m @@ -67,7 +67,7 @@ function fLocAnalysisWrapper(sessions, clip, stc, QA) for ss = 1:length(sessions) try fprintf('\nStarting analysis of session %s. \n', sessions{ss}); - [T, err] = evalc('fLocAnalysis(sessions{ss}, [], [], clip(ss), QA)'); + [T, err] = evalc('fLocAnalysis(sessions{ss}, [], [], clip(ss), stc, QA)'); fid = fopen(fullfile(sessions{ss}, 'vistasoft_log.txt'), 'w+'); %Maybe change name fprintf(fid, '%s', T); fclose(fid); catch diff --git a/functions/fLocGearRun.m b/functions/fLocGearRun.m index 2b514097..afd7a12c 100755 --- a/functions/fLocGearRun.m +++ b/functions/fLocGearRun.m @@ -1,23 +1,59 @@ -function [session, init_params, glm_params] = fLocGearRun(session, config) -% Generate data structures of vistasoft parameters for preprocessing and -% analyzing fLoc data with a GLM using a Flywheel gear. +function err = fLocGearRun(session, config_file, out_dir, fshome) +% Generate data structures of vistasoft parameters for preprocessing and +% analyzing fLoc data with a GLM using a Flywheel gear. % % INPUTS % 1) session -- path to data directory in Flywheel (char array) % 2) config -- path to Flywheel config file (char array) -% +% 3) out_dir -- path to save outputs +% 4) fshome -- path to Freesurfer's directory +% % OUPUTS % 1) session -- path to data directory in Flywheel (char array) % 2) init_params -- parameters for initialization/preprocessing (struct) % 3) glm_params -- parameters for running GLM analysis (struct) -% +% % AS 8/2018 +%% Set env and parse config file for params + +% Set the env for fs bin +setenv('FREESURFER_HOME', fshome); +setenv('PATH', [getenv('PATH'), ':', fullfile(fshome, 'bin')]); +disp(getenv('FREESURFER_HOME')); +disp(getenv('PATH')); + +% Read the json file +config = jsondecode(fileread(config_file)); + +% Set the params +clip = config.config.clip; + +% Currently we're not setting those params in the config/manifest +% file, thus we set the defaults below. +[~, init_params, glm_params] = fLocAnalysisParams(session, clip); %% Run fLoc Analysis -err = fLocAnalysis(session, init_params, glm_params, clip, QA); +err = fLocAnalysis(session, init_params, glm_params, clip); -clear global +%% Move select files to output and zip outputs +if err == 0 + [base_path, session_label] = fileparts(session); + [~, subject_code] = fileparts(base_path); + jpgs = mrvFindFile('*.jpg', session); + for ii=1:numel(jpgs) + [~, f, e] = fileparts(jpgs{ii}); + copyfile(jpgs{ii}, fullfile(out_dir, [subject_code, '_', session_label, '-', f, e]), 'f'); + end + logs = mrvFindFile('*log*', session); + for ii=1:numel(logs) + [~, f, e] = fileparts(logs{ii}); + copyfile(logs{ii}, fullfile(out_dir, [subject_code, '_', session_label, '-', f, e]), 'f'); + end + fprintf('Zipping outputs [%s]...\n', mrvDirup(session)); + zip(fullfile(out_dir, [subject_code, '_', session_label, '-fLoc.zip']), mrvDirup(session)); + rmdir(session, 's'); +clear global end diff --git a/gear/README.md b/gear/README.md new file mode 100644 index 00000000..26a56140 --- /dev/null +++ b/gear/README.md @@ -0,0 +1,65 @@ +# fLoc Flywheel Gear (vpnl/floc) + +This Docker context creates a Flywheel Gear that can analyze data generated with the Functional localizer experiment used to define category-selective cortical regions (published in [Stigliani et al., 2015](http://www.jneurosci.org/content/35/36/12412)). + +By default the Gear generates the following voxel-wise parameters maps: Beta values, model residual error, proportion of variance explained, and GLM contrasts (t-values). All parameter maps are saved as .mat and nifti files in `session/Inplane/GLMs/` and can be viewed in Vistasoft. The Gear also writes a file named `fLocAnalysis_log.txt` that logs progress and saves input and glm parameters as `fLocAnalysisParams.mat`. If there are 10 conditions specified, 15 contrast maps will be generated. 10 maps will contrast each individual condition versus all others. The other 5 maps will contrast conditions 1 and 2 vs all others, 3 and 4 versus all others, and so on. If there are not 10 conditions specified in the parfiles, then the maps generated will contrast each individual condition versus all others. + +## Instructions + +Follow the instructions below to build the Gear. + +### Building + +1. Make sure you have Matlabr2017b and Docker installed on your LINUX machine +2. Clone fLoc repository on the computer you will use to build the image. +3. Run the build function in Matlab - (`fLoc/gear/bin/fLocGearRun_Build.m`) + ```bash + cd fLoc/gear/bin/ + /software/matlab/r2017b/bin/matlab -nodesktop -r fLocGearRun_Build + ``` +4. Build the image +```bash +docker build -t vpnl/floc fLoc/Dockerfile +``` + +### Execution + +Execution of this Gear is limited to a Flywheel instance. Below are some important notes to get the gear to run correctly on your data. + +1. This gear is means to run at the session level (one Subject at a time). +2. Ensure that you have uploaded the PAR files for each of the acquisitions you wish to be analyzed. +3. Run the analysis gear and set your CLIP and Freesurfer_License config parameters +4. The Gear requires no input files, as it relies on the Flywheel SDK to gather the required data. +5. Inplane logic: In the case that multiple inplane files exist, the Gear will use the last inplane prior to the first BOLD scan. + +### Results + +The main analysis results are zipped in an archive using the convention of: +``` +/-fLoc.zip +``` + +An example output looks like this (subject_code=s181, session=8111): +``` +├── s181_8111-fLocAnalysis_log.txt +├── s181_8111-fLoc.zip +├── s181_8111-input_data.json +└── s181_8111-Within_Scan_Motion_Est.jpg +``` + +The output archive contains the following directory structure: +``` +s181/ +└── 8111 + ├── Images + └── Inplane + ├── GLMs + │   ├── NiftiMaps + │   ├── RawMaps + │   └── Scan1 + ├── MotionComp + │   └── TSeries + └── MotionComp_RefScan1 + └── TSeries + +``` diff --git a/gear/bin/fLocGearRun_Build.m b/gear/bin/fLocGearRun_Build.m new file mode 100755 index 00000000..377f34f2 --- /dev/null +++ b/gear/bin/fLocGearRun_Build.m @@ -0,0 +1,38 @@ +% This script should be run on Matlab 2017b, GLNXA64. +% +% EXAMPLE USAGE +% /software/matlab/r2017b/bin/matlab -nodesktop -r fLocGearRun_Build + +% Check that we are running a compatible version +if (isempty(strfind(version, '9.3.0'))) || (isempty(strfind(computer, 'GLNXA64'))) + error('You must compile this function using R2017b (9.3.0.713579) 64-bit (glnxa64). You are using %s, %s', version, computer); +end + +disp(mfilename('fullpath')); +compileDir = fileparts(mfilename('fullpath')); +if ~strcmpi(pwd, compileDir) + disp('You must run this code from %s', compileDir); +end + + +% Download the source code +disp('Cloning source code...'); +%system('git clone https://github.com/vistalab/vistasoft'); +system('git clone https://github.com/MNordt/vistasoft --branch localPaths'); + +% Set paths +disp('Adding paths to build scope...'); +restoredefaultpath; +addpath(genpath(fullfile(pwd, 'vistasoft'))); +addpath(genpath('../../fLoc')) + +% Compile +disp('Running compile code...'); +mcc -v -R -nodisplay -m ../../functions/fLocGearRun.m + +% Clean up +disp('Cleaning up...') +rmdir(fullfile(pwd, 'vistasoft'), 's'); + +disp('Done!'); +exit diff --git a/gear/manifest.json b/gear/manifest.json new file mode 100644 index 00000000..190ec6a0 --- /dev/null +++ b/gear/manifest.json @@ -0,0 +1,36 @@ +{ + "name": "floc", + "label": "VPNL: fLoc - Face Localizer Analysis Pipeline", + "description": "Automated analysis of fMRI data from fLoc funcional localizer experiment used to define category-selective cortical regions. By default the Gear generates the following voxel-wise parameters maps: Beta values, model residual error, proportion of variance explained, and GLM contrasts (t-values). All parameter maps are saved as .mat and nifti files in session/Inplane/GLMs/ and can be viewed in Vistasoft. The Gear also writes a file named 'fLocAnalysis_log.txt' that logs progress and saves input and glm parameters as fLocAnalysisParams.mat. If there are 10 conditions specified, 15 contrast maps will be generated. 10 maps will contrast each individual condition versus all others. The other 5 maps will contrast conditions 1 and 2 vs all others, 3 and 4 versus all others, and so on. If there are not 10 conditions specified in the parfiles, then the maps generated will contrast each individual condition versus all others.", + "cite": "Temporal Processing Capacity in High-Level Visual Cortex Is Domain Specific. Anthony Stigliani, Kevin S. Weiner and Kalanit Grill-Spector. Journal of Neuroscience 9 September 2015, 35 (36) 12412-12424; DOI: https://doi.org/10.1523/JNEUROSCI.4822-14.2015", + "author": "Anthony Stigliani, VPNL, Stanford", + "maintainer": "Michael Perry ", + "url": "https://github.com/VPNL/fLoc", + "source": "https://github.com/VPNL/fLoc", + "license": "Other", + "flywheel": "0", + "version": "0.3.0", + "custom": { + "docker-image": "vpnl/floc:0.3.0", + "flywheel": { + "suite": "VPNL" + } + }, + "inputs": { + "api_key": { + "description": "Calling user's api key.", + "base": "api-key", + "read-only": true + } + }, + "config": { + "clip": { + "description": "Number of TRs to clip from the beginning of each run (int)", + "type": "integer" + }, + "freesurfer_license": { + "description": "FreeSurfer license file text, provided during registration with FreeSurfer. This text will by written to the license file within the '$FSHOME' directory and used during execution of the Gear.", + "type": "string" + } + } +} diff --git a/gear/run.py b/gear/run.py new file mode 100755 index 00000000..fe118b86 --- /dev/null +++ b/gear/run.py @@ -0,0 +1,246 @@ +#!/usr/bin/env python + +import os +import json +import time +import shutil +import logging +import flywheel +from datetime import datetime +from pprint import pprint as pp + +logging.basicConfig() +log = logging.getLogger('fLOC') + +# Parse a config file +def parse_config(config_json_file): + """ + Take a config.json file, read and return the config object. + """ + + if not os.path.isfile(config_json_file): + raise ValueError('No config file could be found!') + + # Read the config json file + with open(config_json_file, 'r') as jsonfile: + config = json.load(jsonfile) + + return config + +def get_timestamp_delta(timestamp, reference, absolute_value=False): + """ + Given two timestamps, calculate and return the delta in total seconds. + By default the delta is returned in absolute value. + """ + + from datetime import datetime + + ts = datetime.utcfromtimestamp(timestamp) + ref = datetime.utcfromtimestamp(reference) + delta = ts - ref + + if absolute_value: + return abs(delta.total_seconds()) + else: + return delta.total_seconds() + + +def fLoc_data(config=None, out_dir='/flywheel/v0/output/'): + + # Get the container info from the config doc + analysis_id = str(config['destination']['id']) + + # Get analy + analysis = fw.get_analysis(analysis_id) + + # Get container type and id from + parent_container_type = analysis['parent']['type'] + parent_container_id = analysis['parent']['id'] + + # Get the parent session's ID + if parent_container_type == 'session': + parent_session_id = parent_container_id + else: + raise ValueError("Input parent must be a 'session'" ) + + # Get the acquisitions for the session + session = fw.get_session(parent_session_id) + subject_code = session.subject.code + session_label = session.label + session_acquisitions = fw.get_session_acquisitions(parent_session_id) + + data_dir = os.path.join(out_dir, subject_code, session_label) + log.info('Data dir set to: %s' % (data_dir)) + if not os.path.isdir(data_dir): + os.makedirs(data_dir) + + # Now get the full thing from the DB. + session_acquisitions_full = [] + for s in session_acquisitions: + session_acquisitions_full.append(fw.get_acquisition(s.id)) + + + # FIND PAR FILES AND ASSOCIATED NIFTI FILES + input_files = [] + found_par_files = False + for a in session_acquisitions_full: + for f in a['files']: + if f['name'].endswith('.par'): + + log.info('Found parfile = %s ' % f['name']) + found_par_files = True + + # Find the nifti file in this acquisition and download + nifti = [ x for x in a['files'] if x['type'] == 'nifti' ] + if nifti and len(nifti) == 1: + nifti = nifti[0] + nifti_rename = f['name'].replace('.par', '.nii.gz') + this_input = {'parfile': f['name'], + 'nifti': nifti['name'], + 'nifti_out_name': nifti_rename, + 'acquisition_id': a['_id'], + 'label': a['label'], + 'timestamp': a['timestamp']} + input_files.append(this_input) + log.info(' Found associated nifti file = %s \n\tDownloading nifti as %s...' % (nifti['name'], nifti_rename)) + fw.download_file_from_acquisition(a['_id'], + f['name'], + os.path.join(data_dir, f['name'])) + fw.download_file_from_acquisition(a['_id'], + nifti['name'], + os.path.join(data_dir, nifti_rename)) + else: + log.info('No nifti file found to associate with the parfile!!!') + + if found_par_files == False: + return None + else: + num_runs = len(input_files) + + + # FIND IN-PLANE SCAN + inplanes = [] + for a in session_acquisitions_full: + for f in a['files']: + if f['classification'] and f['classification'].has_key('Features') and 'In-Plane' in f['classification']['Features']: + inplanes.append(a) + + log.info('%d Inplane acquisitions were found!' % (len(inplanes))) + + if inplanes and len(inplanes) >1: + # Find the correct inplane when one or more exist, making the assumption that + # the last inplane is the one we actually want to use. + inplanes_sorted = sorted(inplanes, key=lambda k: k['timestamp']) + + # The inplane is the last inplane prior to the first BOLD scan. + # Get the earliest timestamp in the input+files array and compare that to the list of inplane files + first_timestamp = sorted(input_files, key=lambda k: k['timestamp']) + inplane = [ x for x in inplanes_sorted if x['timestamp'] < first_timestamp[0]['timestamp'] ][-1] + else: + inplane = inplanes[0] + + + # Download the Inplane file + nifti = [ x for x in inplane['files'] if x['type'] == 'nifti' ] + if nifti and len(nifti) == 1: + nifti = nifti[0] + nifti_rename = 'Inplane.nii.gz' + this_input = {'nifti': nifti['name'], + 'nifti_out_name': nifti_rename, + 'acquisition_id': inplane['_id'], + 'label': inplane['label'], + 'timestamp': inplane['timestamp']} + input_files.append(this_input) + log.info('Using inplane file = %s \n\tDownloading nifti as %s...' % (nifti['name'], nifti_rename)) + fw.download_file_from_acquisition(inplane['_id'], + nifti['name'], + os.path.join(data_dir, nifti_rename)) + + # TRACK INPUTS AND WRITE TO FILE + def sanitize_ts(x): + x['timestamp'] = x['timestamp'].strftime('%Y-%m-%dT%H:%M:%S') + return x + sanitized_inputs = [ sanitize_ts(x) for x in input_files ] + inputs = { "session_id": parent_session_id, + "subject_code": subject_code, + "session_label": session_label, + "num_runs": str(num_runs), + "files": sanitized_inputs + } + + # Write out the json file with input files + json_file = os.path.join(out_dir, subject_code + '_' + session_label + '-input_data.json') + with open(json_file, 'w') as jf: + json.dump(inputs, jf) + + + return inputs, data_dir + + +############################################################################### +# MAIN + +if __name__ == '__main__': + + import urllib3 + import shutil + import argparse + import subprocess + import datetime + + log.setLevel(getattr(logging, 'DEBUG')) + logging.getLogger('fLOC').setLevel(logging.INFO) + log.info(' start %s' % datetime.datetime.utcnow()) + + urllib3.disable_warnings() + os.environ['FLYWHEEL_SDK_SKIP_VERSION_CHECK'] = '1' + + ap = argparse.ArgumentParser() + ap.add_argument('--config_file', + type=str, + dest="config_file", + default='/flywheel/v0/config.json', + help='Full path to the input json config file.') + ap.add_argument('--output_dir', + type=str, + dest="output_dir", + default='/flywheel/v0/output', + help='Directory in which to save the results.') + + args = ap.parse_args() + + # Load/Parse the configuration file + config = parse_config(args.config_file) + + # Create SDK client + log.info(' Creating SDK client...') + fw = flywheel.Client(config['inputs']['api_key']['key']) + + # Copy FS license into place + license_file_path = '/opt/freesurfer/.license' + with open(license_file_path, 'w') as lf: + lf.write(' '.join(config['config']['freesurfer_license'].split()).replace(" ", "\n")) + + # Download fLOC data + log.info('Gathering fLOC Data in %s...' % (args.output_dir)) + inputs, data_dir = fLoc_data(config, args.output_dir) + + if not inputs: + log.warning('Errors finding input files...') + os.sys.exit(1) + + # RUN MATLAB CODE + fs_home_dir = '/opt/freesurfer' + matlab_binary = '/usr/local/bin/run_fLocGearRun.sh' + matlab_library = '/opt/mcr/v93' + + run_command = [matlab_binary, matlab_library, data_dir, args.config_file, args.output_dir, fs_home_dir] + status = subprocess.check_call(run_command) + + # Clean up fLoc dir + shutil.rmtree(os.path.join(args.output_dir, inputs['subject_code'])) + + # EXIT + if status == 0: + log.info('Success!') + os.sys.exit(status)