diff --git a/.circleci/config.yml b/.circleci/config.yml index c00016612..86d21638d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -394,6 +394,12 @@ jobs: /tmp/data/ds005 /tmp/ds005/derivatives participant \ --sloppy --write-graph --mem_mb 4096 \ --nthreads 2 --anat-only -vv + - run: + name: Clean-up after anatomical run + command: | + rm -rf /tmp/ds005/work/fmriprep_wf/fsdir* + rm -rf /tmp/ds005/work/reportlets + rm -rf /tmp/ds005/derivatives/fmriprep - save_cache: key: ds005-anat-v14-{{ .Branch }}-{{ epoch }} paths: @@ -525,6 +531,12 @@ jobs: /tmp/data/ds054 /tmp/ds054/derivatives participant \ --fs-no-reconall --sloppy --write-graph \ --mem_mb 4096 --nthreads 2 --anat-only -vv + - run: + name: Clean-up after anatomical run + command: | + rm -rf /tmp/ds054/work/fmriprep_wf/fsdir* + rm -rf /tmp/ds054/work/reportlets + rm -rf /tmp/ds054/derivatives/fmriprep - save_cache: key: ds054-anat-v12-{{ .Branch }}-{{ epoch }} paths: @@ -643,6 +655,12 @@ jobs: /tmp/data/ds210 /tmp/ds210/derivatives participant \ --fs-no-reconall --sloppy --write-graph \ --mem_mb 4096 --nthreads 2 --anat-only -vv + - run: + name: Clean-up after anatomical run + command: | + rm -rf /tmp/ds210/work/fmriprep_wf/fsdir* + rm -rf /tmp/ds210/work/reportlets + rm -rf /tmp/ds210/derivatives/fmriprep - save_cache: key: ds210-anat-v10-{{ .Branch }}-{{ epoch }} paths: diff --git a/docs/workflows.rst b/docs/workflows.rst index 358b558ff..13b89eb6e 100644 --- a/docs/workflows.rst +++ b/docs/workflows.rst @@ -70,17 +70,15 @@ T1w/T2w preprocessing bids_root='.', debug=False, freesurfer=True, - fs_spaces=['T1w', 'fsnative', - 'template', 'fsaverage5'], hires=True, longitudinal=False, num_t1w=1, omp_nthreads=1, output_dir='.', + output_spaces={'MNI152NLin2009cAsym': {'res': 2}}, reportlets_dir='.', skull_strip_template='MNI152NLin2009cAsym', skull_strip_fixed_seed=False, - template='MNI152NLin2009cAsym', ) The anatomical sub-workflow begins by constructing an average image by diff --git a/fmriprep/__about__.py b/fmriprep/__about__.py index fd3d2f9f7..445bb4095 100644 --- a/fmriprep/__about__.py +++ b/fmriprep/__about__.py @@ -105,9 +105,9 @@ LINKS_REQUIRES = [ 'git+https://github.com/poldracklab/niworkflows.git@' - 'b7d111c8fd36a099c74be5e7671677eedb175533#egg=niworkflows', + '076aed98962b10d107c83110c05e42466a89bbc4#egg=niworkflows', 'git+https://github.com/poldracklab/smriprep.git@' - '423bcc43ab7300177eb3b98da62817b2cad8eb87#egg=smriprep-0.1.0', + 'f1cfc37bcdc346549dbf1d037cdade3a3b32d5de#egg=smriprep-0.1.0', ] TESTS_REQUIRES = [ diff --git a/fmriprep/cli/run.py b/fmriprep/cli/run.py index 822675282..45f2068e3 100755 --- a/fmriprep/cli/run.py +++ b/fmriprep/cli/run.py @@ -260,7 +260,6 @@ def main(): """Entry point""" from nipype import logging as nlogging from multiprocessing import set_start_method, Process, Manager - from ..viz.reports import generate_reports from ..utils.bids import write_derivative_description set_start_method('forkserver') @@ -384,8 +383,6 @@ def before_send(event, hints): nlogging.getLogger('nipype.interface').setLevel(log_level) nlogging.getLogger('nipype.utils').setLevel(log_level) - errno = 0 - # Call build_workflow(opts, retval) with Manager() as mgr: retval = mgr.dict() @@ -395,9 +392,9 @@ def before_send(event, hints): retcode = p.exitcode or retval.get('return_code', 0) - bids_dir = retval.get('bids_dir') - output_dir = retval.get('output_dir') - work_dir = retval.get('work_dir') + bids_dir = Path(retval.get('bids_dir')) + output_dir = Path(retval.get('output_dir')) + work_dir = Path(retval.get('work_dir')) plugin_settings = retval.get('plugin_settings', None) subject_list = retval.get('subject_list', None) fmriprep_wf = retval.get('workflow', None) @@ -436,32 +433,48 @@ def before_send(event, hints): sentry_sdk.add_breadcrumb(message='fMRIPrep started', level='info') sentry_sdk.capture_message('fMRIPrep started', level='info') + errno = 1 # Default is error exit unless otherwise set try: fmriprep_wf.run(**plugin_settings) - except RuntimeError as e: - errno = 1 - if "Workflow did not execute cleanly" not in str(e): - sentry_sdk.capture_exception(e) - raise + except Exception as e: + if not opts.notrack: + from ..utils.sentry import process_crashfile + crashfolders = [output_dir / 'fmriprep' / 'sub-{}'.format(s) / 'log' / run_uuid + for s in subject_list] + for crashfolder in crashfolders: + for crashfile in crashfolder.glob('crash*.*'): + process_crashfile(crashfile) + + if "Workflow did not execute cleanly" not in str(e): + sentry_sdk.capture_exception(e) + logger.critical('fMRIPrep failed: %s', e) + raise else: if opts.run_reconall: from templateflow import api from niworkflows.utils.misc import _copy_any dseg_tsv = str(api.get('fsaverage', suffix='dseg', extensions=['.tsv'])) _copy_any(dseg_tsv, - str(Path(output_dir) / 'fmriprep' / 'desc-aseg_dseg.tsv')) + str(output_dir / 'fmriprep' / 'desc-aseg_dseg.tsv')) _copy_any(dseg_tsv, - str(Path(output_dir) / 'fmriprep' / 'desc-aparcaseg_dseg.tsv')) + str(output_dir / 'fmriprep' / 'desc-aparcaseg_dseg.tsv')) + errno = 0 logger.log(25, 'fMRIPrep finished without errors') + if not opts.notrack: + sentry_sdk.capture_message('fMRIPrep finished without errors', + level='info') finally: + from niworkflows.reports import generate_reports # Generate reports phase - errno += generate_reports(subject_list, output_dir, work_dir, run_uuid, - sentry_sdk=sentry_sdk) - write_derivative_description(bids_dir, str(Path(output_dir) / 'fmriprep')) + failed_reports = generate_reports( + subject_list, output_dir, work_dir, run_uuid, packagename='fmriprep') + write_derivative_description(bids_dir, output_dir / 'fmriprep') - if not opts.notrack and errno == 0: - sentry_sdk.capture_message('fMRIPrep finished without errors', level='info') - sys.exit(int(errno > 0)) + if failed_reports and not opts.notrack: + sentry_sdk.capture_message( + 'Report generation failed for %d subjects' % failed_reports, + level='error') + sys.exit(int((errno + failed_reports) > 0)) def validate_input_dir(exec_env, bids_dir, participant_label): @@ -574,9 +587,9 @@ def build_workflow(opts, retval): from nipype import logging, config as ncfg from niworkflows.utils.bids import collect_participants + from niworkflows.reports import generate_reports from ..__about__ import __version__ from ..workflows.base import init_fmriprep_wf - from ..viz.reports import generate_reports logger = logging.getLogger('nipype.workflow') @@ -734,7 +747,8 @@ def build_workflow(opts, retval): run_uuid = opts.run_uuid retval['run_uuid'] = run_uuid retval['return_code'] = generate_reports( - subject_list, str(output_dir), str(work_dir), run_uuid) + subject_list, output_dir, work_dir, run_uuid, + packagename='fmriprep') return retval # Build main workflow diff --git a/fmriprep/utils/sentry.py b/fmriprep/utils/sentry.py new file mode 100644 index 000000000..6a496c0e2 --- /dev/null +++ b/fmriprep/utils/sentry.py @@ -0,0 +1,115 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: +"""Stripped out routines for Sentry""" +import re +from niworkflows.utils.misc import read_crashfile +import sentry_sdk + +CHUNK_SIZE = 16384 +# Group common events with pre specified fingerprints +KNOWN_ERRORS = { + 'permission-denied': [ + "PermissionError: [Errno 13] Permission denied" + ], + 'memory-error': [ + "MemoryError", + "Cannot allocate memory", + "Return code: 134", + ], + 'reconall-already-running': [ + "ERROR: it appears that recon-all is already running" + ], + 'no-disk-space': [ + "[Errno 28] No space left on device", + "[Errno 122] Disk quota exceeded" + ], + 'segfault': [ + "Segmentation Fault", + "Segfault", + "Return code: 139", + ], + 'potential-race-condition': [ + "[Errno 39] Directory not empty", + "_unfinished.json", + ], + 'keyboard-interrupt': [ + "KeyboardInterrupt", + ], +} + + +def process_crashfile(crashfile): + """Parse the contents of a crashfile and submit sentry messages""" + crash_info = read_crashfile(str(crashfile)) + with sentry_sdk.push_scope() as scope: + scope.level = 'fatal' + + # Extract node name + node_name = crash_info.pop('node').split('.')[-1] + scope.set_tag("node_name", node_name) + + # Massage the traceback, extract the gist + traceback = crash_info.pop('traceback') + # last line is probably most informative summary + gist = traceback.splitlines()[-1] + exception_text_start = 1 + for line in traceback.splitlines()[1:]: + if not line[0].isspace(): + break + exception_text_start += 1 + + exception_text = '\n'.join( + traceback.splitlines()[exception_text_start:]) + + # Extract inputs, if present + inputs = crash_info.pop('inputs', None) + if inputs: + scope.set_extra('inputs', dict(inputs)) + + # Extract any other possible metadata in the crash file + for k, v in crash_info.items(): + strv = list(_chunks(str(v))) + if len(strv) == 1: + scope.set_extra(k, strv[0]) + else: + for i, chunk in enumerate(strv): + scope.set_extra('%s_%02d' % (k, i), chunk) + + fingerprint = '' + issue_title = '{}: {}'.format(node_name, gist) + for new_fingerprint, error_snippets in KNOWN_ERRORS.items(): + for error_snippet in error_snippets: + if error_snippet in traceback: + fingerprint = new_fingerprint + issue_title = new_fingerprint + break + if fingerprint: + break + + message = issue_title + '\n\n' + message += exception_text[-(8192 - len(message)):] + if fingerprint: + sentry_sdk.add_breadcrumb(message=fingerprint, level='fatal') + else: + # remove file paths + fingerprint = re.sub(r"(/[^/ ]*)+/?", '', message) + # remove words containing numbers + fingerprint = re.sub(r"([a-zA-Z]*[0-9]+[a-zA-Z]*)+", '', fingerprint) + # adding the return code if it exists + for line in message.splitlines(): + if line.startswith("Return code"): + fingerprint += line + break + + scope.fingerprint = [fingerprint] + sentry_sdk.capture_message(message, 'fatal') + + +def _chunks(string, length=CHUNK_SIZE): + """ + Splits a string into smaller chunks + >>> list(_chunks('some longer string.', length=3)) + ['som', 'e l', 'ong', 'er ', 'str', 'ing', '.'] + """ + return (string[i:i + length] + for i in range(0, len(string), length)) diff --git a/fmriprep/viz/__init__.py b/fmriprep/viz/__init__.py deleted file mode 100644 index 719c96812..000000000 --- a/fmriprep/viz/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- -# vi: set ft=python sts=4 ts=4 sw=4 et: -""" -The fmriprep reporting engine for visual assessment -""" -from .reports import run_reports, generate_reports - -__all__ = ['run_reports', 'generate_reports'] diff --git a/fmriprep/viz/config.json b/fmriprep/viz/config.json deleted file mode 100644 index 0e3f18511..000000000 --- a/fmriprep/viz/config.json +++ /dev/null @@ -1,151 +0,0 @@ -{ -"sections": -[ - { - "name": "Summary", - "reportlets": - [ - { - "name": "anat/summary", - "file_pattern": "anat/.*_summary", - "raw": true - } - ] - }, - { - "name": "Anatomical", - "reportlets": - [ - { - "name": "anat/conform", - "file_pattern": "anat/.*_conform", - "raw": true - }, - { - "name": "anat_preproc/t1_brain_seg", - "file_pattern": "anat/.*seg_brainmask", - "title": "Brain mask and brain tissue segmentation of the T1w", - "description": "This panel shows the template T1-weighted image (if several T1w images were found), with contours delineating the detected brain mask and brain tissue segmentations." - }, - { - "name": "anat_preproc/t1_2_mni", - "file_pattern": "anat/.*t1_2_mni", - "title": "T1 to MNI registration", - "description": "Nonlinear mapping of the T1w image into MNI space. Hover on the panel with the mouse to transition between both spaces." - }, - { - "name": "anat_preproc/reconall", - "file_pattern": "anat/.*reconall", - "title": "Surface reconstruction", - "description": "Surfaces (white and pial) reconstructed with FreeSurfer (recon-all) overlaid on the participant's T1w template." - } - - ] - }, - { - "name": "Fieldmaps", - "reportlets": - [ - { - "name": "fmap/magnitude_mask", - "file_pattern": "fmap/.*fmap_mask", - "title": "Skull stripped magnitude image", - "description": "Brain extraction of the magnitude image from the fieldmap" - } - ] - }, - { - "name": "Functional", - "reportlets": - [ - { - "name": "epi/summary", - "file_pattern": "func/.*_summary", - "raw": true - }, - { - "name": "epi/validation", - "file_pattern": "func/.*_validation\\.", - "raw": true - }, - { - "name": "epi/fmpa_reg", - "file_pattern": "func/.*fmap_reg\\.", - "title": "Fieldmap to EPI registration", - "description": "Results of affine coregistration between the magnitude image of the fieldmap and the reference EPI image" - }, - { - "name": "epi/fmpa_reg", - "file_pattern": "func/.*fmap_reg_vsm\\.", - "title": "Fieldmap", - "description": "Overlaid on the reference EPI image" - }, - { - "name": "epi/unwarp", - "file_pattern": "func/.*_sdc_.*\\.", - "title": "Susceptibility distortion correction", - "description": "Results of performing susceptibility distortion correction (SDC) on the EPI" - }, - { - "name": "epi/sdc_syn", - "file_pattern": "func/.*_forcedsyn", - "title": "Experimental fieldmap-less susceptibility distortion correction", - "description": "The dataset contained some fieldmap information, but the argument --force-syn was used. The higher-priority SDC method was used. Here, we show the results of performing SyN-based SDC on the EPI for comparison." - }, - { - "name": "epi/rois", - "file_pattern": "func/.*_rois", - "title": "ROIs in BOLD space", - "description": "Brain mask calculated on the BOLD signal (red contour), along with the masks used for a/tCompCor.
The aCompCor mask (magenta contour) is a conservative CSF and white-matter mask for extracting physiological and movement confounds.
The fCompCor mask (blue contour) contains the top 5% most variable voxels within a heavily-eroded brain-mask." - }, - { - "name": "epi_mean_t1_registration/flirt", - "file_pattern": "func/.*_flirtnobbr", - "title": "EPI to T1 registration", - "description": "FSL flirt was used to generate transformations from EPI space to T1 Space - BBR refinement rejected. Note that Nearest Neighbor interpolation is used in the reportlets in order to highlight potential spin-history and other artifacts, whereas final images are resampled using Lanczos interpolation." - }, - { - "name": "epi_mean_t1_registration/coreg", - "file_pattern": "func/.*_coreg", - "title": "EPI to T1 registration", - "description": "mri_coreg (FreeSurfer) was used to generate transformations from EPI space to T1 Space - bbregister refinement rejected. Note that Nearest Neighbor interpolation is used in the reportlets in order to highlight potential spin-history and other artifacts, whereas final images are resampled using Lanczos interpolation." - }, - { - "name": "epi_mean_t1_registration/flirtbbr", - "file_pattern": "func/.*_flirtbbr", - "title": "EPI to T1 registration", - "description": "FSL flirt was used to generate transformations from EPI-space to T1w-space - The white matter mask calculated with FSL fast (brain tissue segmentation) was used for BBR. Note that Nearest Neighbor interpolation is used in the reportlets in order to highlight potential spin-history and other artifacts, whereas final images are resampled using Lanczos interpolation." - }, - { - "name": "epi_mean_t1_registration/bbregister", - "file_pattern": "func/.*_bbregister", - "title": "EPI to T1 registration", - "description": "bbregister was used to generate transformations from EPI-space to T1w-space. Note that Nearest Neighbor interpolation is used in the reportlets in order to highlight potential spin-history and other artifacts, whereas final images are resampled using Lanczos interpolation." - }, - { - "name": "epi/carpetplot", - "file_pattern": "func/.*carpetplot", - "title": "BOLD Summary", - "description": "Summary statistics are plotted, which may reveal trends or artifacts in the BOLD data. Global signals calculated within the whole-brain (GS), within the white-matter (WM) and within cerebro-spinal fluid (CSF) show the mean BOLD signal in their corresponding masks. DVARS and FD show the standardized DVARS and framewise-displacement measures for each time point.
A carpet plot shows the time series for all voxels within the brain mask. Voxels are grouped into cortical (blue), and subcortical (orange) gray matter, cerebellum (green) and white matter and CSF (red), indicated by the color map on the left-hand side." - }, - { - "name": "ica_aroma", - "file_pattern": "func/.*ica_aroma", - "title": "ICA AROMA", - "description": "Maps created with maximum intensity projection (glass brain) with a black brain outline. Right hand side of each map: time series (top in seconds), frequency spectrum (bottom in Hertz). Components classified as signal are plotted in green; noise components in red." - } - ] - }, - { - "name": "About", - "reportlets": - [ - { - "name": "anat/about", - "file_pattern": "anat/.*_about", - "raw": true - } - ] - } -] -} diff --git a/fmriprep/viz/report.tpl b/fmriprep/viz/report.tpl deleted file mode 100644 index 0272dd10d..000000000 --- a/fmriprep/viz/report.tpl +++ /dev/null @@ -1,186 +0,0 @@ - - - - - - - - - - - - - - - - - - -{% for sub_report in sections %} -
-

{{ sub_report.name }}

- {% if sub_report.isnested %} - {% for run_report in sub_report.reportlets %} -
-

Reports for {{ run_report.title }}

- {% for elem in run_report.reportlets %} - {% if elem.contents %} - {% if elem.title %}

{{ elem.title }}

{% endif %} - {% if elem.description %}

{{ elem.description }}

{% endif %} - {% for content in elem.contents %} - {% if elem.raw %}{{ content }}{% else %} -

- - Problem loading figure {{ content }}. If the link below works, please try reloading the report in your browser. -
-
- Get figure file: {{ content }} -
- {% endif %} - {% endfor %} - {% endif %} - {% endfor %} -
- {% endfor %} - {% else %} - {% for elem in sub_report.reportlets %} - {% if elem.contents %} - {% if elem.title %}

{{ elem.title }}

{% endif %} - {% if elem.description %}

{{ elem.description }}


{% endif %} - {% for content in elem.contents %} - {% if elem.raw %}{{ content }}{% else %} -

- filename:{{ content }} -
-
- Get figure file: {{ content }} -
- {% endif %} - {% endfor %} - {% endif %} - {% endfor %} - {% endif %} -
-{% endfor %} - -
-

Methods

- {% if boilerplate %} -

We kindly ask to report results preprocessed with fMRIPrep using the following - boilerplate

- -
- {% for b in boilerplate %} -
{{ b[2] }}
- {% endfor %} -
- {% else %} -

Failed to generate the boilerplate

- {% endif %} -

Alternatively, an interactive boilerplate generator is available in the documentation website.

-
- -
-

Errors

- -
- - - - - diff --git a/fmriprep/viz/reports.py b/fmriprep/viz/reports.py deleted file mode 100644 index e2123eb01..000000000 --- a/fmriprep/viz/reports.py +++ /dev/null @@ -1,366 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- -# vi: set ft=python sts=4 ts=4 sw=4 et: -""" -fMRIprep reports builder -^^^^^^^^^^^^^^^^^^^^^^^^ - - -""" -from pathlib import Path -import json -import re - -import jinja2 -from nipype.utils.filemanip import copyfile -from pkg_resources import resource_filename as pkgrf - -from niworkflows.utils.misc import read_crashfile - - -class Element(object): - """ - Just a basic component of a report - """ - def __init__(self, name, title=None): - self.name = name - self.title = title - - -class Reportlet(Element): - """ - A reportlet has title, description and a list of graphical components - """ - def __init__(self, name, file_pattern=None, title=None, description=None, raw=False): - self.name = name - self.file_pattern = re.compile(file_pattern) - self.title = title - self.description = description - self.source_files = [] - self.contents = [] - self.raw = raw - - -class SubReport(Element): - """ - SubReports are sections within a Report - """ - def __init__(self, name, reportlets=None, title=''): - self.name = name - self.title = title - self.reportlets = [] - if reportlets: - self.reportlets += reportlets - self.isnested = False - - -class Report(object): - """ - The full report object - """ - def __init__(self, path, config, out_dir, run_uuid, out_filename='report.html', - sentry_sdk=None): - self.root = path - self.sections = [] - self.errors = [] - self.out_dir = Path(out_dir) - self.out_filename = out_filename - self.run_uuid = run_uuid - self.sentry_sdk = sentry_sdk - - self._load_config(config) - - def _load_config(self, config): - with open(config, 'r') as configfh: - config = json.load(configfh) - - self.index(config['sections']) - - def index(self, config): - fig_dir = 'figures' - subject_dir = self.root.split('/')[-1] - subject = re.search('^(?Psub-[a-zA-Z0-9]+)$', subject_dir).group() - svg_dir = self.out_dir / 'fmriprep' / subject / fig_dir - svg_dir.mkdir(parents=True, exist_ok=True) - reportlet_list = list(sorted([str(f) for f in Path(self.root).glob('**/*.*')])) - - for subrep_cfg in config: - reportlets = [] - for reportlet_cfg in subrep_cfg['reportlets']: - rlet = Reportlet(**reportlet_cfg) - for src in reportlet_list: - ext = src.split('.')[-1] - if rlet.file_pattern.search(src): - contents = None - if ext == 'html': - with open(src) as fp: - contents = fp.read().strip() - elif ext == 'svg': - fbase = Path(src).name - copyfile(src, str(svg_dir / fbase), - copy=True, use_hardlink=True) - contents = str(Path(subject) / fig_dir / fbase) - - if contents: - rlet.source_files.append(src) - rlet.contents.append(contents) - - if rlet.source_files: - reportlets.append(rlet) - - if reportlets: - sub_report = SubReport( - subrep_cfg['name'], reportlets=reportlets, - title=subrep_cfg.get('title')) - self.sections.append(order_by_run(sub_report)) - - error_dir = self.out_dir / "fmriprep" / subject / 'log' / self.run_uuid - if error_dir.is_dir(): - self.index_error_dir(error_dir) - - def index_error_dir(self, error_dir): - """ - Crawl subjects crash directory for the corresponding run, report to sentry, and - populate self.errors. - """ - for crashfile in error_dir.glob('crash*.*'): - crash_info = read_crashfile(str(crashfile)) - if self.sentry_sdk: - with self.sentry_sdk.push_scope() as scope: - node_name = crash_info['node'].split('.')[-1] - # last line is probably most informative summary - gist = crash_info['traceback'].split('\n')[-1] - exception_text_start = 1 - for line in crash_info['traceback'].split('\n')[1:]: - if not line[0].isspace(): - break - exception_text_start += 1 - - exception_text = '\n'.join(crash_info['traceback'].split('\n')[ - exception_text_start:]) - - scope.set_tag("node_name", node_name) - - chunk_size = 16384 - - for k, v in crash_info.items(): - if k == 'inputs': - scope.set_extra(k, dict(v)) - elif isinstance(v, str) and len(v) > chunk_size: - chunks = [v[i:i + chunk_size] for i in range(0, len(v), chunk_size)] - for i, chunk in enumerate(chunks): - scope.set_extra('%s_%02d' % (k, i), chunk) - else: - scope.set_extra(k, v) - scope.level = 'fatal' - - # Group common events with pre specified fingerprints - fingerprint_dict = {'permission-denied': [ - "PermissionError: [Errno 13] Permission denied"], - 'memory-error': ["MemoryError", "Cannot allocate memory"], - 'reconall-already-running': [ - "ERROR: it appears that recon-all is already running"], - 'no-disk-space': [ - "OSError: [Errno 28] No space left on device", - "[Errno 122] Disk quota exceeded"], - 'sigkill': ["Return code: 137"], - 'keyboard-interrupt': ["KeyboardInterrupt"]} - - fingerprint = '' - issue_title = node_name + ': ' + gist - for new_fingerprint, error_snippets in fingerprint_dict.items(): - for error_snippet in error_snippets: - if error_snippet in crash_info['traceback']: - fingerprint = new_fingerprint - issue_title = new_fingerprint - break - if fingerprint: - break - - message = issue_title + '\n\n' - message += exception_text[-(8192-len(message)):] - if fingerprint: - self.sentry_sdk.add_breadcrumb(message=fingerprint, level='fatal') - else: - # remove file paths - fingerprint = re.sub(r"(/[^/ ]*)+/?", '', message) - # remove words containing numbers - fingerprint = re.sub(r"([a-zA-Z]*[0-9]+[a-zA-Z]*)+", '', fingerprint) - # adding the return code if it exists - for line in message.split('\n'): - if line.startswith("Return code"): - fingerprint += line - break - - scope.fingerprint = [fingerprint] - self.sentry_sdk.capture_message(message, 'fatal') - - self.errors.append(crash_info) - - def generate_report(self): - logs_path = self.out_dir / 'fmriprep' / 'logs' - - boilerplate = [] - boiler_idx = 0 - - if (logs_path / 'CITATION.html').exists(): - text = (logs_path / 'CITATION.html').read_text(encoding='UTF-8') - text = '
%s
' % re.compile( - '(.*?)', - re.DOTALL | re.IGNORECASE).findall(text)[0].strip() - boilerplate.append((boiler_idx, 'HTML', text)) - boiler_idx += 1 - - if (logs_path / 'CITATION.md').exists(): - text = '
%s
\n' % (logs_path / 'CITATION.md').read_text(encoding='UTF-8') - boilerplate.append((boiler_idx, 'Markdown', text)) - boiler_idx += 1 - - if (logs_path / 'CITATION.tex').exists(): - text = (logs_path / 'CITATION.tex').read_text(encoding='UTF-8') - text = re.compile( - r'\\begin{document}(.*?)\\end{document}', - re.DOTALL | re.IGNORECASE).findall(text)[0].strip() - text = '
%s
\n' % text - text += '

Bibliography

\n' - text += '
%s
\n' % Path( - pkgrf('fmriprep', 'data/boilerplate.bib')).read_text(encoding='UTF-8') - boilerplate.append((boiler_idx, 'LaTeX', text)) - boiler_idx += 1 - - searchpath = pkgrf('fmriprep', '/') - env = jinja2.Environment( - loader=jinja2.FileSystemLoader(searchpath=searchpath), - trim_blocks=True, lstrip_blocks=True - ) - report_tpl = env.get_template('viz/report.tpl') - report_render = report_tpl.render(sections=self.sections, errors=self.errors, - boilerplate=boilerplate) - - # Write out report - (self.out_dir / 'fmriprep' / self.out_filename).write_text(report_render, encoding='UTF-8') - return len(self.errors) - - -def order_by_run(subreport): - ordered = [] - run_reps = {} - - for element in subreport.reportlets: - if len(element.source_files) == 1 and element.source_files[0]: - ordered.append(element) - continue - - for filename, file_contents in zip(element.source_files, element.contents): - name, title = generate_name_title(filename) - if not filename or not name: - continue - - new_element = Reportlet( - name=element.name, title=element.title, file_pattern=element.file_pattern, - description=element.description, raw=element.raw) - new_element.contents.append(file_contents) - new_element.source_files.append(filename) - - if name not in run_reps: - run_reps[name] = SubReport(name, title=title) - - run_reps[name].reportlets.append(new_element) - - if run_reps: - keys = list(sorted(run_reps.keys())) - for key in keys: - ordered.append(run_reps[key]) - subreport.isnested = True - - subreport.reportlets = ordered - return subreport - - -def generate_name_title(filename): - fname = Path(filename).name - expr = re.compile('^sub-(?P[a-zA-Z0-9]+)(_ses-(?P[a-zA-Z0-9]+))?' - '(_task-(?P[a-zA-Z0-9]+))?(_acq-(?P[a-zA-Z0-9]+))?' - '(_rec-(?P[a-zA-Z0-9]+))?(_run-(?P[a-zA-Z0-9]+))?') - outputs = expr.search(fname) - if outputs: - outputs = outputs.groupdict() - else: - return None, None - - name = '{session}{task}{acq}{rec}{run}'.format( - session="_ses-" + outputs['session_id'] if outputs['session_id'] else '', - task="_task-" + outputs['task_id'] if outputs['task_id'] else '', - acq="_acq-" + outputs['acq_id'] if outputs['acq_id'] else '', - rec="_rec-" + outputs['rec_id'] if outputs['rec_id'] else '', - run="_run-" + outputs['run_id'] if outputs['run_id'] else '' - ) - title = '{session}{task}{acq}{rec}{run}'.format( - session=" Session: " + outputs['session_id'] if outputs['session_id'] else '', - task=" Task: " + outputs['task_id'] if outputs['task_id'] else '', - acq=" Acquisition: " + outputs['acq_id'] if outputs['acq_id'] else '', - rec=" Reconstruction: " + outputs['rec_id'] if outputs['rec_id'] else '', - run=" Run: " + outputs['run_id'] if outputs['run_id'] else '' - ) - return name.strip('_'), title - - -def run_reports(reportlets_dir, out_dir, subject_label, run_uuid, sentry_sdk=None): - """ - Runs the reports - - >>> import os - >>> from shutil import copytree - >>> from tempfile import TemporaryDirectory - >>> filepath = os.path.dirname(os.path.realpath(__file__)) - >>> test_data_path = os.path.realpath(os.path.join(filepath, - ... '../data/tests/work')) - >>> curdir = os.getcwd() - >>> tmpdir = TemporaryDirectory() - >>> os.chdir(tmpdir.name) - >>> data_dir = copytree(test_data_path, os.path.abspath('work')) - >>> os.makedirs('out/fmriprep', exist_ok=True) - >>> run_reports(os.path.abspath('work/reportlets'), - ... os.path.abspath('out'), - ... '01', 'madeoutuuid') - 0 - >>> os.chdir(curdir) - >>> tmpdir.cleanup() - - """ - reportlet_path = str(Path(reportlets_dir) / 'fmriprep' / ("sub-%s" % subject_label)) - config = pkgrf('fmriprep', 'viz/config.json') - - out_filename = 'sub-{}.html'.format(subject_label) - report = Report(reportlet_path, config, out_dir, run_uuid, out_filename, sentry_sdk=sentry_sdk) - return report.generate_report() - - -def generate_reports(subject_list, output_dir, work_dir, run_uuid, sentry_sdk=None): - """ - A wrapper to run_reports on a given ``subject_list`` - """ - reports_dir = str(Path(work_dir) / 'reportlets') - report_errors = [ - run_reports(reports_dir, output_dir, subject_label, run_uuid=run_uuid, - sentry_sdk=sentry_sdk) - for subject_label in subject_list - ] - - errno = sum(report_errors) - if errno: - import logging - logger = logging.getLogger('cli') - error_list = ', '.join('%s (%d)' % (subid, err) - for subid, err in zip(subject_list, report_errors) if err) - - err_msg = 'Preprocessing did not finish successfully. Errors occurred while processing ' \ - 'data from participants: %s. Check the HTML reports for details.' % error_list - if sentry_sdk: - with sentry_sdk.push_scope() as scope: - scope.fingerprint = ['fMRIPrep finished with errors'] - logger.error(err_msg) - else: - logger.error(err_msg) - return errno diff --git a/fmriprep/workflows/base.py b/fmriprep/workflows/base.py index a43d2e1be..ab4c62855 100755 --- a/fmriprep/workflows/base.py +++ b/fmriprep/workflows/base.py @@ -436,12 +436,12 @@ def init_single_subject_wf(layout, subject_id, task_id, echo_idx, name, reportle ds_report_summary = pe.Node( DerivativesDataSink(base_directory=reportlets_dir, - suffix='summary'), + desc='summary', keep_dtype=True), name='ds_report_summary', run_without_submitting=True) ds_report_about = pe.Node( DerivativesDataSink(base_directory=reportlets_dir, - suffix='about'), + desc='about', keep_dtype=True), name='ds_report_about', run_without_submitting=True) # Preprocessing of T1w (includes registration to MNI) @@ -449,17 +449,17 @@ def init_single_subject_wf(layout, subject_id, task_id, echo_idx, name, reportle bids_root=layout.root, debug=debug, freesurfer=freesurfer, - fs_spaces=output_spaces, hires=hires, longitudinal=longitudinal, name="anat_preproc_wf", num_t1w=len(subject_data['t1w']), omp_nthreads=omp_nthreads, output_dir=output_dir, + output_spaces={template: {} if template_out_grid == 'native' + else {'res': template_out_grid[0]}}, reportlets_dir=reportlets_dir, skull_strip_fixed_seed=skull_strip_fixed_seed, skull_strip_template=skull_strip_template, - template=template, ) workflow.connect([ @@ -525,8 +525,10 @@ def init_single_subject_wf(layout, subject_id, task_id, echo_idx, name, reportle ('outputnode.t1_aseg', 'inputnode.t1_aseg'), ('outputnode.t1_aparc', 'inputnode.t1_aparc'), ('outputnode.t1_tpms', 'inputnode.t1_tpms'), - ('outputnode.t1_2_mni_forward_transform', 'inputnode.t1_2_mni_forward_transform'), - ('outputnode.t1_2_mni_reverse_transform', 'inputnode.t1_2_mni_reverse_transform'), + (('outputnode.joint_forward_transform', _pop), + 'inputnode.t1_2_mni_forward_transform'), + (('outputnode.joint_reverse_transform', _pop), + 'inputnode.t1_2_mni_reverse_transform'), # Undefined if --no-freesurfer, but this is safe ('outputnode.subjects_dir', 'inputnode.subjects_dir'), ('outputnode.subject_id', 'inputnode.subject_id'), diff --git a/fmriprep/workflows/bold/base.py b/fmriprep/workflows/bold/base.py index a760e0b56..5fac1259b 100755 --- a/fmriprep/workflows/bold/base.py +++ b/fmriprep/workflows/bold/base.py @@ -636,11 +636,8 @@ def init_func_preproc_wf(bold_file, ignore, freesurfer, if fmaps: from ..fieldmap.unwarp import init_fmap_unwarp_report_wf - sdc_type = fmaps[0]['suffix'] - # Report on BOLD correction - fmap_unwarp_report_wf = init_fmap_unwarp_report_wf( - suffix='sdc_%s' % sdc_type) + fmap_unwarp_report_wf = init_fmap_unwarp_report_wf() workflow.connect([ (inputnode, fmap_unwarp_report_wf, [ ('t1_seg', 'inputnode.in_seg')]), @@ -652,9 +649,9 @@ def init_func_preproc_wf(bold_file, ignore, freesurfer, ('outputnode.bold_ref', 'inputnode.in_post')]), ]) - if force_syn and sdc_type != 'syn': + if force_syn and fmaps[0]['suffix'] != 'syn': syn_unwarp_report_wf = init_fmap_unwarp_report_wf( - suffix='forcedsyn', name='syn_unwarp_report_wf') + name='syn_unwarp_report_wf', forcedsyn=True) workflow.connect([ (inputnode, syn_unwarp_report_wf, [ ('t1_seg', 'inputnode.in_seg')]), @@ -842,13 +839,13 @@ def init_func_preproc_wf(bold_file, ignore, freesurfer, # REPORTING ############################################################ ds_report_summary = pe.Node( - DerivativesDataSink(suffix='summary'), + DerivativesDataSink(desc='summary', keep_dtype=True), name='ds_report_summary', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) ds_report_validation = pe.Node( DerivativesDataSink(base_directory=reportlets_dir, - suffix='validation'), + desc='validation', keep_dtype=True), name='ds_report_validation', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) diff --git a/fmriprep/workflows/bold/confounds.py b/fmriprep/workflows/bold/confounds.py index 16b973492..4eeb6dacf 100755 --- a/fmriprep/workflows/bold/confounds.py +++ b/fmriprep/workflows/bold/confounds.py @@ -219,7 +219,7 @@ def init_bold_confs_wf(mem_gb, metadata, name="bold_confs_wf"): name='rois_plot', mem_gb=mem_gb) ds_report_bold_rois = pe.Node( - DerivativesDataSink(suffix='rois'), + DerivativesDataSink(desc='rois', keep_dtype=True), name='ds_report_bold_rois', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) @@ -377,7 +377,7 @@ def init_carpetplot_wf(mem_gb, metadata, name="bold_carpet_wf"): ('framewise_displacement', 'mm', 'FD')]), name='conf_plot', mem_gb=mem_gb) ds_report_bold_conf = pe.Node( - DerivativesDataSink(suffix='carpetplot'), + DerivativesDataSink(desc='carpetplot', keep_dtype=True), name='ds_report_bold_conf', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) @@ -579,7 +579,7 @@ def _getusans_func(image, thresh): name='ica_aroma_confound_extraction') ds_report_ica_aroma = pe.Node( - DerivativesDataSink(suffix='ica_aroma'), + DerivativesDataSink(desc='aroma', keep_dtype=True), name='ds_report_ica_aroma', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) diff --git a/fmriprep/workflows/bold/registration.py b/fmriprep/workflows/bold/registration.py index d21aa3f1e..341ec366d 100644 --- a/fmriprep/workflows/bold/registration.py +++ b/fmriprep/workflows/bold/registration.py @@ -156,7 +156,7 @@ def init_bold_reg_wf(freesurfer, use_bbr, bold2t1w_dof, mem_gb, omp_nthreads, if write_report: ds_report_reg = pe.Node( - DerivativesDataSink(), + DerivativesDataSink(keep_dtype=True), name='ds_report_reg', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) @@ -168,7 +168,7 @@ def _bold_reg_suffix(fallback, freesurfer): workflow.connect([ (bbr_wf, ds_report_reg, [ ('outputnode.out_report', 'in_file'), - (('outputnode.fallback', _bold_reg_suffix, freesurfer), 'suffix')]), + (('outputnode.fallback', _bold_reg_suffix, freesurfer), 'desc')]), ]) return workflow diff --git a/fmriprep/workflows/bold/resampling.py b/fmriprep/workflows/bold/resampling.py index 841d0f5da..329329a6a 100644 --- a/fmriprep/workflows/bold/resampling.py +++ b/fmriprep/workflows/bold/resampling.py @@ -589,20 +589,20 @@ def init_bold_preproc_report_wf(mem_gb, reportlets_dir, name='bold_preproc_repor bold_rpt = pe.Node(SimpleBeforeAfter(), name='bold_rpt', mem_gb=0.1) - bold_rpt_ds = pe.Node( - DerivativesDataSink(base_directory=reportlets_dir, - suffix='variant-preproc'), name='bold_rpt_ds', + ds_report_bold = pe.Node( + DerivativesDataSink(base_directory=reportlets_dir, desc='preproc', + keep_dtype=True), name='ds_report_bold', mem_gb=DEFAULT_MEMORY_MIN_GB, run_without_submitting=True ) workflow.connect([ - (inputnode, bold_rpt_ds, [('name_source', 'source_file')]), + (inputnode, ds_report_bold, [('name_source', 'source_file')]), (inputnode, pre_tsnr, [('in_pre', 'in_file')]), (inputnode, pos_tsnr, [('in_post', 'in_file')]), (pre_tsnr, bold_rpt, [('stddev_file', 'before')]), (pos_tsnr, bold_rpt, [('stddev_file', 'after')]), - (bold_rpt, bold_rpt_ds, [('out_report', 'in_file')]), + (bold_rpt, ds_report_bold, [('out_report', 'in_file')]), ]) return workflow diff --git a/fmriprep/workflows/fieldmap/fmap.py b/fmriprep/workflows/fieldmap/fmap.py index 9e1c4a2af..afcf1c6d2 100644 --- a/fmriprep/workflows/fieldmap/fmap.py +++ b/fmriprep/workflows/fieldmap/fmap.py @@ -64,8 +64,9 @@ def init_fmap_wf(omp_nthreads, fmap_bspline, name='fmap_wf'): name='n4_correct', n_procs=omp_nthreads) bet = pe.Node(BETRPT(generate_report=True, frac=0.6, mask=True), name='bet') - ds_fmap_mask = pe.Node(DerivativesDataSink(suffix='fmap_mask'), - name='ds_report_fmap_mask', run_without_submitting=True) + ds_report_fmap_mask = pe.Node(DerivativesDataSink( + desc='brain', suffix='mask'), name='ds_report_fmap_mask', + run_without_submitting=True) workflow.connect([ (inputnode, magmrg, [('magnitude', 'in_files')]), @@ -74,8 +75,8 @@ def init_fmap_wf(omp_nthreads, fmap_bspline, name='fmap_wf'): (n4_correct, bet, [('output_image', 'in_file')]), (bet, outputnode, [('mask_file', 'fmap_mask'), ('out_file', 'fmap_ref')]), - (inputnode, ds_fmap_mask, [('fieldmap', 'source_file')]), - (bet, ds_fmap_mask, [('out_report', 'in_file')]), + (inputnode, ds_report_fmap_mask, [('fieldmap', 'source_file')]), + (bet, ds_report_fmap_mask, [('out_report', 'in_file')]), ]) if fmap_bspline: diff --git a/fmriprep/workflows/fieldmap/phdiff.py b/fmriprep/workflows/fieldmap/phdiff.py index 168f9b1be..a2a850400 100644 --- a/fmriprep/workflows/fieldmap/phdiff.py +++ b/fmriprep/workflows/fieldmap/phdiff.py @@ -83,8 +83,9 @@ def _pick1st(inlist): name='n4', n_procs=omp_nthreads) bet = pe.Node(BETRPT(generate_report=True, frac=0.6, mask=True), name='bet') - ds_fmap_mask = pe.Node(DerivativesDataSink(suffix='fmap_mask'), name='ds_report_fmap_mask', - mem_gb=0.01, run_without_submitting=True) + ds_report_fmap_mask = pe.Node(DerivativesDataSink( + desc='brain', suffix='mask'), name='ds_report_fmap_mask', + mem_gb=0.01, run_without_submitting=True) # uses mask from bet; outputs a mask # dilate = pe.Node(fsl.maths.MathsCommand( # nan2zeros=True, args='-kernel sphere 5 -dilM'), name='MskDilate') @@ -127,8 +128,8 @@ def _pick1st(inlist): (compfmap, outputnode, [('out_file', 'fmap')]), (bet, outputnode, [('mask_file', 'fmap_mask'), ('out_file', 'fmap_ref')]), - (inputnode, ds_fmap_mask, [('phasediff', 'source_file')]), - (bet, ds_fmap_mask, [('out_report', 'in_file')]), + (inputnode, ds_report_fmap_mask, [('phasediff', 'source_file')]), + (bet, ds_report_fmap_mask, [('out_report', 'in_file')]), ]) return workflow diff --git a/fmriprep/workflows/fieldmap/unwarp.py b/fmriprep/workflows/fieldmap/unwarp.py index aa8ec9ae3..d1768d2e1 100644 --- a/fmriprep/workflows/fieldmap/unwarp.py +++ b/fmriprep/workflows/fieldmap/unwarp.py @@ -105,8 +105,9 @@ def init_sdc_unwarp_wf(omp_nthreads, fmap_demean, debug, name='sdc_unwarp_wf'): output_inverse_warped_image=True, output_warped_image=True), name='fmap2ref_reg', n_procs=omp_nthreads) - ds_reg = pe.Node(DerivativesDataSink(suffix='fmap_reg'), name='ds_report_reg', - mem_gb=0.01, run_without_submitting=True) + ds_report_reg = pe.Node(DerivativesDataSink( + desc='magnitude', suffix='bold'), name='ds_report_reg', + mem_gb=0.01, run_without_submitting=True) # Map the VSM into the EPI space fmap2ref_apply = pe.Node(ANTSApplyTransformsRPT( @@ -118,8 +119,9 @@ def init_sdc_unwarp_wf(omp_nthreads, fmap_demean, debug, name='sdc_unwarp_wf'): float=True), name='fmap_mask2ref_apply') - ds_reg_vsm = pe.Node(DerivativesDataSink(suffix='fmap_reg_vsm'), name='ds_report_vsm', - mem_gb=0.01, run_without_submitting=True) + ds_report_vsm = pe.Node(DerivativesDataSink( + desc='fieldmap', suffix='bold'), name='ds_report_vsm', + mem_gb=0.01, run_without_submitting=True) # Fieldmap to rads and then to voxels (VSM - voxel shift map) torads = pe.Node(FieldToRadS(fmap_range=0.5), name='torads') @@ -159,9 +161,9 @@ def init_sdc_unwarp_wf(omp_nthreads, fmap_demean, debug, name='sdc_unwarp_wf'): (inputnode, fmap_mask2ref_apply, [('in_reference', 'reference_image')]), (fmap2ref_reg, fmap_mask2ref_apply, [ ('composite_transform', 'transforms')]), - (fmap2ref_apply, ds_reg_vsm, [('out_report', 'in_file')]), + (fmap2ref_apply, ds_report_vsm, [('out_report', 'in_file')]), (inputnode, fmap2ref_reg, [('in_reference_brain', 'fixed_image')]), - (fmap2ref_reg, ds_reg, [('out_report', 'in_file')]), + (fmap2ref_reg, ds_report_reg, [('out_report', 'in_file')]), (inputnode, fmap2ref_apply, [('fmap', 'input_image')]), (inputnode, fmap_mask2ref_apply, [('fmap_mask', 'input_image')]), (fmap2ref_apply, torads, [('output_image', 'in_file')]), @@ -211,7 +213,7 @@ def init_sdc_unwarp_wf(omp_nthreads, fmap_demean, debug, name='sdc_unwarp_wf'): return workflow -def init_fmap_unwarp_report_wf(name='fmap_unwarp_report_wf', suffix='variant-hmcsdc_preproc'): +def init_fmap_unwarp_report_wf(name='fmap_unwarp_report_wf', forcedsyn=False): """ This workflow generates and saves a reportlet showing the effect of fieldmap unwarping a BOLD image. @@ -227,8 +229,8 @@ def init_fmap_unwarp_report_wf(name='fmap_unwarp_report_wf', suffix='variant-hmc name : str, optional Workflow name (default: fmap_unwarp_report_wf) - suffix : str, optional - Suffix to be appended to this reportlet + forcedsyn : bool, optional + Whether SyN-SDC was forced. **Inputs** @@ -264,7 +266,8 @@ def init_fmap_unwarp_report_wf(name='fmap_unwarp_report_wf', suffix='variant-hmc bold_rpt = pe.Node(SimpleBeforeAfter(), name='bold_rpt', mem_gb=0.1) ds_report_sdc = pe.Node( - DerivativesDataSink(suffix=suffix), name='ds_report_sdc', + DerivativesDataSink(desc='sdc' if not forcedsyn else 'forcedsyn', + suffix='bold'), name='ds_report_sdc', mem_gb=DEFAULT_MEMORY_MIN_GB, run_without_submitting=True ) diff --git a/requirements.txt b/requirements.txt index 570089928..0e8171479 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ nilearn!=0.5.0,!=0.5.1 -git+https://github.com/poldracklab/niworkflows.git@b7d111c8fd36a099c74be5e7671677eedb175533#egg=niworkflows -git+https://github.com/poldracklab/smriprep.git@423bcc43ab7300177eb3b98da62817b2cad8eb87#egg=smriprep-0.1.0 +git+https://github.com/poldracklab/niworkflows.git@076aed98962b10d107c83110c05e42466a89bbc4#egg=niworkflows +git+https://github.com/poldracklab/smriprep.git@f1cfc37bcdc346549dbf1d037cdade3a3b32d5de#egg=smriprep-0.1.0 templateflow<0.2.0a0,>=0.1.3 diff --git a/setup.py b/setup.py index 5d253d1e4..5d347b322 100644 --- a/setup.py +++ b/setup.py @@ -39,8 +39,6 @@ def main(): 'data/boilerplate.bib', 'data/itkIdentityTransform.txt', 'data/flirtsch/bbr.sch', - 'viz/*.tpl', - 'viz/*.json', ] } diff --git a/wrapper/fmriprep_docker.py b/wrapper/fmriprep_docker.py index 0be5cdccf..d8f1563c2 100755 --- a/wrapper/fmriprep_docker.py +++ b/wrapper/fmriprep_docker.py @@ -396,8 +396,8 @@ def main(): unknown_args.extend(['-w', '/scratch']) if opts.config: - command.extend(['-v', ':'.join((opts.config, - '/root/.nipype/nipype.cfg', 'ro'))]) + command.extend(['-v', ':'.join(( + opts.config, '/home/fmriprep/.nipype/nipype.cfg', 'ro'))]) if opts.use_plugin: command.extend(['-v', ':'.join((opts.use_plugin, '/tmp/plugin.yml',