From 83b130e0e4a98a5b75ec5d711fe99efc9d4e9c58 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Thu, 13 Jun 2019 17:40:39 -0400 Subject: [PATCH 001/156] [DOC] update package requirements --- requirements.txt | 4 ++-- requirements_dev.txt | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/requirements.txt b/requirements.txt index b780e72c..98f5fedd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ nipype==1.2.0 pandas==0.24.2 parse==1.12.0 tqdm==4.32.1 -pybids==0.8.0 +pybids==0.9.1 matplotlib==3.1.0 cytoolz==0.9.0.1 -numba==0.43.1 \ No newline at end of file +numba==0.44.0 diff --git a/requirements_dev.txt b/requirements_dev.txt index 01870909..17e71717 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -3,10 +3,12 @@ bumpversion==0.5.3 wheel==0.33.4 watchdog==0.9.0 flake8==3.7.7 -tox==3.11.1 +tox==3.12.1 coverage==4.5.3 -Sphinx==2.0.1 +Sphinx==2.1.1 twine==1.13.0 +black==19.3b0 +versioneer==0.18 -pytest==4.5.0 -pytest-runner==4.4 +pytest==4.6.3 +pytest-runner==5.1 From 19111adcecdc2f096ccb8421beafd718f111545c Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Thu, 13 Jun 2019 17:46:15 -0400 Subject: [PATCH 002/156] [REF] refactor dwi code into separate workflow --- dmriprep/utils.py | 144 +++++++++++++++++- dmriprep/workflows/__init__.py | 0 dmriprep/workflows/base.py | 51 +++++++ dmriprep/workflows/dwi/__init__.py | 3 + dmriprep/workflows/dwi/base.py | 229 +++++++++++++++++++++++++++++ 5 files changed, 426 insertions(+), 1 deletion(-) create mode 100644 dmriprep/workflows/__init__.py create mode 100644 dmriprep/workflows/base.py create mode 100644 dmriprep/workflows/dwi/__init__.py create mode 100755 dmriprep/workflows/dwi/base.py diff --git a/dmriprep/utils.py b/dmriprep/utils.py index cd92cc17..d88fb120 100644 --- a/dmriprep/utils.py +++ b/dmriprep/utils.py @@ -2,10 +2,12 @@ Utility functions for other submodules """ + +import warnings import itertools import numpy as np - +from bids.layout import BIDSLayout mod_logger = logging.getLogger(__name__) @@ -64,3 +66,143 @@ def is_hemispherical(vecs): else: pole = np.array([0.0, 0.0, 0.0]) return is_hemi, pole + + +class BIDSError(ValueError): + def __init__(self, message, bids_root): + indent = 10 + header = '{sep} BIDS root folder: "{bids_root}" {sep}'.format( + bids_root=bids_root, sep=''.join(['-'] * indent)) + self.msg = '\n{header}\n{indent}{message}\n{footer}'.format( + header=header, indent=''.join([' '] * (indent + 1)), + message=message, footer=''.join(['-'] * len(header)) + ) + super(BIDSError, self).__init__(self.msg) + self.bids_root = bids_root + + +class BIDSWarning(RuntimeWarning): + pass + + +def collect_participants(bids_dir, participant_label=None, strict=False, + bids_validate=True): + """ + List the participants under the BIDS root and checks that participants + designated with the participant_label argument exist in that folder. + Returns the list of participants to be finally processed. + Requesting all subjects in a BIDS directory root: + >>> collect_participants(str(datadir / 'ds114'), bids_validate=False) + ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10'] + Requesting two subjects, given their IDs: + >>> collect_participants(str(datadir / 'ds114'), participant_label=['02', '04'], + ... bids_validate=False) + ['02', '04'] + Requesting two subjects, given their IDs (works with 'sub-' prefixes): + >>> collect_participants(str(datadir / 'ds114'), participant_label=['sub-02', 'sub-04'], + ... bids_validate=False) + ['02', '04'] + Requesting two subjects, but one does not exist: + >>> collect_participants(str(datadir / 'ds114'), participant_label=['02', '14'], + ... bids_validate=False) + ['02'] + >>> collect_participants( + ... str(datadir / 'ds114'), participant_label=['02', '14'], + ... strict=True, bids_validate=False) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + fmriprep.utils.bids.BIDSError: + ... + """ + + if isinstance(bids_dir, BIDSLayout): + layout = bids_dir + else: + layout = BIDSLayout(str(bids_dir), validate=bids_validate) + + all_participants = set(layout.get_subjects()) + + # Error: bids_dir does not contain subjects + if not all_participants: + raise BIDSError( + 'Could not find participants. Please make sure the BIDS data ' + 'structure is present and correct. Datasets can be validated online ' + 'using the BIDS Validator (http://bids-standard.github.io/bids-validator/).\n' + 'If you are using Docker for Mac or Docker for Windows, you ' + 'may need to adjust your "File sharing" preferences.', bids_dir) + + # No --participant-label was set, return all + if not participant_label: + return sorted(all_participants) + + if isinstance(participant_label, str): + participant_label = [participant_label] + + # Drop sub- prefixes + participant_label = [sub[4:] if sub.startswith('sub-') else sub for sub in participant_label] + # Remove duplicates + participant_label = sorted(set(participant_label)) + # Remove labels not found + found_label = sorted(set(participant_label) & all_participants) + if not found_label: + raise BIDSError('Could not find participants [{}]'.format( + ', '.join(participant_label)), bids_dir) + + # Warn if some IDs were not found + notfound_label = sorted(set(participant_label) - all_participants) + if notfound_label: + exc = BIDSError('Some participants were not found: {}'.format( + ', '.join(notfound_label)), bids_dir) + if strict: + raise exc + warnings.warn(exc.msg, BIDSWarning) + + return found_label + + +def collect_data(bids_dir, participant_label, task=None, echo=None, + bids_validate=True): + """ + Uses pybids to retrieve the input data for a given participant + >>> bids_root, _ = collect_data(str(datadir / 'ds054'), '100185', + ... bids_validate=False) + >>> bids_root['fmap'] # doctest: +ELLIPSIS + ['.../ds054/sub-100185/fmap/sub-100185_magnitude1.nii.gz', \ +'.../ds054/sub-100185/fmap/sub-100185_magnitude2.nii.gz', \ +'.../ds054/sub-100185/fmap/sub-100185_phasediff.nii.gz'] + >>> bids_root['bold'] # doctest: +ELLIPSIS + ['.../ds054/sub-100185/func/sub-100185_task-machinegame_run-01_bold.nii.gz', \ +'.../ds054/sub-100185/func/sub-100185_task-machinegame_run-02_bold.nii.gz', \ +'.../ds054/sub-100185/func/sub-100185_task-machinegame_run-03_bold.nii.gz', \ +'.../ds054/sub-100185/func/sub-100185_task-machinegame_run-04_bold.nii.gz', \ +'.../ds054/sub-100185/func/sub-100185_task-machinegame_run-05_bold.nii.gz', \ +'.../ds054/sub-100185/func/sub-100185_task-machinegame_run-06_bold.nii.gz'] + >>> bids_root['sbref'] # doctest: +ELLIPSIS + ['.../ds054/sub-100185/func/sub-100185_task-machinegame_run-01_sbref.nii.gz', \ +'.../ds054/sub-100185/func/sub-100185_task-machinegame_run-02_sbref.nii.gz', \ +'.../ds054/sub-100185/func/sub-100185_task-machinegame_run-03_sbref.nii.gz', \ +'.../ds054/sub-100185/func/sub-100185_task-machinegame_run-04_sbref.nii.gz', \ +'.../ds054/sub-100185/func/sub-100185_task-machinegame_run-05_sbref.nii.gz', \ +'.../ds054/sub-100185/func/sub-100185_task-machinegame_run-06_sbref.nii.gz'] + >>> bids_root['t1w'] # doctest: +ELLIPSIS + ['.../ds054/sub-100185/anat/sub-100185_T1w.nii.gz'] + >>> bids_root['t2w'] # doctest: +ELLIPSIS + [] + """ + if isinstance(bids_dir, BIDSLayout): + layout = bids_dir + else: + layout = BIDSLayout(str(bids_dir), validate=bids_validate) + + queries = { + 'fmap': {'datatype': 'fmap'}, + 't2w': {'datatype': 'anat', 'suffix': 'T2w'}, + 't1w': {'datatype': 'anat', 'suffix': 'T1w'}, + 'dwi': {'datatype': 'dwi', 'suffix': 'dwi'}, + } + + subj_data = { + dtype: sorted(layout.get(return_type='file', subject=participant_label, + extensions=['nii', 'nii.gz'], **query)) + for dtype, query in queries.items()} + + return subj_data, layout diff --git a/dmriprep/workflows/__init__.py b/dmriprep/workflows/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/dmriprep/workflows/base.py b/dmriprep/workflows/base.py new file mode 100644 index 00000000..95b0dcaf --- /dev/null +++ b/dmriprep/workflows/base.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python + +import os +from copy import deepcopy + +from nipype.pipeline import engine as pe +from .dwi import init_dwi_preproc_wf + +def init_dmriprep_wf(layout, subject_list, work_dir, output_dir): + dmriprep_wf = pe.Workflow(name='dmriprep_wf') + dmriprep_wf.base_dir = work_dir + + for subject_id in subject_list: + + single_subject_wf = init_single_subject_wf( + layout=layout, + subject_id=subject_id, + name='single_subject_' + subject_id + '_wf', + work_dir=work_dir, + output_dir=output_dir) + + single_subject_wf.config['execution']['crashdump_dir'] = ( + os.path.join(output_dir, 'dmriprep', 'sub-' + subject_id, 'log') + ) + + for node in single_subject_wf._get_all_nodes(): + node.config = deepcopy(single_subject_wf.config) + + dmriprep_wf.add_nodes([single_subject_wf]) + + return dmriprep_wf + + +def init_single_subject_wf(layout, subject_id, name, work_dir, output_dir): + from ..utils import collect_data + + subject_data = collect_data(layout, subject_id)[0] + + if not subject_data['dwi']: + raise Exception("No dwi images found for participant {}. " + "All workflows require dwi images".format(subject_id)) + + subject_wf = pe.Workflow(name=name) + + for dwi_file in subject_data['dwi']: + dwi_preproc_wf = init_dwi_preproc_wf(dwi_file=dwi_file, + layout=layout) + + subject_wf.add_nodes([dwi_preproc_wf]) + + return subject_wf diff --git a/dmriprep/workflows/dwi/__init__.py b/dmriprep/workflows/dwi/__init__.py new file mode 100644 index 00000000..4133e319 --- /dev/null +++ b/dmriprep/workflows/dwi/__init__.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python + +from .base import init_dwi_preproc_wf diff --git a/dmriprep/workflows/dwi/base.py b/dmriprep/workflows/dwi/base.py new file mode 100755 index 00000000..5afff6a9 --- /dev/null +++ b/dmriprep/workflows/dwi/base.py @@ -0,0 +1,229 @@ +#!/usr/bin/env python + +def init_dwi_preproc_wf(dwi_file): + from nipype.pipeline import engine as pe + from nipype.interfaces import (freesurfer as fs, fsl, mrtrix3, ants, \ + io as nio, utility as niu) + from nipype import logging + + fmaps = [] + fmaps = layout.get_fieldmap(dwi_file, return_list=True) + + for fmap in fmaps: + if fmap['suffix'] == 'phase': + fmap_key = 'phase1' + else: + fmap_key = fmap['suffix'] + fmap['metadata'] = layout.get_metadata(fmap[fmap_key]) + + dwi_wf = pe.Workflow(name="dwi_preproc_wf") + + inputnode = pe.Node(niu.IdentityInterface(fields=[ + 'subject_id', + 'dwi_file', + 'metadata', + 'bvec_file', + 'bval_file', + 'out_dir', + 'eddy_niter', + 'slice_outlier_threshold']), + name="inputnode") + + outputnode = pe.Node( + niu.IdentityInterface(fields=['out_file', 'out_mask', 'out_bvec']), + name="outputnode") + + # name noise and out_file using fname_presuffix + denoise = pe.Node(mrtrix3.DWIDenoise(noise='noise.nii.gz', + out_file='denoised.nii.gz'), + name="denoise") + + # name unring using fname_presuffix + unring = pe.Node(mrtrix3.MRDeGibbs(out_file='unringed.nii.gz'), + name="unring") + + def gen_index(in_file): + import os.path as op + import numpy as np + import nibabel as nb + from nipype.utils import NUMPY_MMAP + from nipype.utils.filemanip import fname_presuffix + + out_file = fname_presuffix(in_file, suffix="_index.txt", newpath=op.abspath('.'), use_ext=False) + vols = nb.load(in_file, mmap=NUMPY_MMAP).get_data().shape[-1] + index_lines = np.ones((vols, )) + index_lines_reshape = index_lines.reshape(1, index_lines.shape[0]) + np.savetxt(out_file, index_lines_reshape, fmt='%i') + return out_file + + gen_idx = pe.Node( + niu.Function( + input_names=['in_file'], + output_names=['out_file'], + function=gen_index), + name='gen_index') + + def gen_acqparams(in_file, metadata): + import os.path as op + from nipype.utils.filemanip import fname_presuffix + + out_file = fname_presuffix(in_file, suffix="_acqparams.txt", newpath=op.abspath('.'), use_ext=False) + + acq_param_dict = { + 'j': '0 1 0 %.7f', + 'j-': '0 -1 0 %.7f', + 'i': '1 0 0 %.7f', + 'i-': '-1 0 0 %.7f', + 'k': '0 0 1 %.7f', + 'k-': '0 0 -1 %.7f' + } + + pe_dir = metadata.get('PhaseEncodingDirection') + total_readout = metadata.get('TotalReadoutTime') + + acq_param_lines = acq_param_dict[pe_dir] % total_readout + + with open(out_file, 'w') as f: + f.write(acq_param_lines) + + return out_file + + acqp = pe.Node( + niu.Function( + input_names=['in_file', 'metadata'], + output_names=['out_file'], + function=gen_acqparams), + name='acqp') + + def b0_average(in_dwi, in_bval, b0_thresh=10.0, out_file=None): + """ + A function that averages the *b0* volumes from a DWI dataset. + As current dMRI data are being acquired with all b-values > 0.0, + the *lowb* volumes are selected by specifying the parameter b0_thresh. + .. warning:: *b0* should be already registered (head motion artifact should + be corrected). + """ + import numpy as np + import nibabel as nb + import os.path as op + from nipype.utils import NUMPY_MMAP + from nipype.utils.filemanip import fname_presuffix + + if out_file is None: + out_file = fname_presuffix(in_dwi, suffix="_avg_b0", newpath=op.abspath('.')) + + imgs = np.array(nb.four_to_three(nb.load(in_dwi, mmap=NUMPY_MMAP))) + bval = np.loadtxt(in_bval) + index = np.argwhere(bval <= b0_thresh).flatten().tolist() + + b0s = [im.get_data().astype(np.float32) for im in imgs[index]] + b0 = np.average(np.array(b0s), axis=0) + + hdr = imgs[0].header.copy() + hdr.set_data_shape(b0.shape) + hdr.set_xyzt_units('mm') + hdr.set_data_dtype(np.float32) + nb.Nifti1Image(b0, imgs[0].affine, hdr).to_filename(out_file) + return out_file + + avg_b0_0 = pe.Node( + niu.Function( + input_names=['in_dwi', 'in_bval'], + output_names=['out_file'], + function=b0_average), + name='b0_avg_pre') + + # dilate mask + bet_dwi0 = pe.Node( + fsl.BET(frac=0.3, mask=True, robust=True), name='bet_dwi_pre') + + mrtrix3.MaskFilter + + ecc = pe.Node(fsl.Eddy(repol=True, + cnr_maps=True, + residuals=True, + method='jac'), + name='fsl_eddy') + + import multiprocessing + ecc.inputs.num_threads = multiprocessing.cpu_count() + + from numba import cuda + try: + if cuda.gpus: + ecc.inputs.use_cuda = True + except: + ecc.inputs.use_cuda = False + + avg_b0_1 = pe.Node( + niu.Function( + input_names=['in_dwi', 'in_bval'], + output_names=['out_file'], + function=b0_average), + name='b0_avg_post') + + bet_dwi1 = pe.Node( + fsl.BET(frac=0.3, mask=True, robust=True), name='bet_dwi_post') + + eddy_quad = pe.Node(fsl.EddyQuad(verbose=True), name="eddy_quad") + + get_path = lambda x: x.split('.nii.gz')[0].split('_fix')[0] + get_qc_path = lambda x: x.split('.nii.gz')[0] + '.qc' + + dwi_bias_corr = pe.Node(ants.N4BiasFieldCorrection(), name='dwi_bias_corr') + + fslroi = pe.Node(fsl.ExtractROI(t_min=0, t_size=1), name="fslroi") + + def get_b0_mask_fn(b0_file): + import nibabel as nib + from nipype.utils.filemanip import fname_presuffix + from dipy.segment.mask import median_otsu + import os + + mask_file = fname_presuffix(b0_file, suffix="_mask", newpath=os.path.abspath('.')) + img = nib.load(b0_file) + data, aff = img.get_data(), img.affine + _, mask = median_otsu(data, 2, 1) + nib.Nifti1Image(mask.astype(float), aff).to_filename(mask_file) + return mask_file + + b0mask_node = pe.Node(niu.Function(input_names=['b0_file'], + output_names=['mask_file'], + function=get_b0_mask_fn), + name="getB0Mask") + + dwi_wf.connect( + [(inputnode, denoise, [('dwi_file', 'in_file')]), + (denoise, unring, [('out_file', 'in_file')]), + (inputnode, avg_b0_0, [('bval_file', 'in_bval')]), + (unring, avg_b0_0, [('out_file', 'in_dwi')]), + (avg_b0_0, bet_dwi0, [('out_file', 'in_file')]), + (inputnode, gen_idx, [('dwi_file', 'in_file')]), + (inputnode, acqp, [('dwi_file', 'in_file'), + ('metadata', 'metadata')]), + (unring, ecc, [('out_file', 'in_file')]), + (inputnode, ecc, [('bval_file', 'in_bval'), + ('bvec_file', 'in_bvec')]), + (bet_dwi0, ecc, [('mask_file', 'in_mask')]), + (gen_idx, ecc, [('out_file', 'in_index')]), + (acqp, ecc, [('out_file', 'in_acqp')]), + + (ecc, fslroi, [('out_corrected', 'in_file')]), + (fslroi, b0mask_node, [('roi_file', 'b0_file')]), + (ecc, eddy_quad, [(('out_corrected', get_path), 'base_name'), + (('out_corrected', get_qc_path), 'output_dir')]), + (inputnode, eddy_quad, [('bval_file', 'bval_file')]), + (ecc, eddy_quad, [('out_rotated_bvecs', 'bvec_file')]), + (b0mask_node, eddy_quad, [('mask_file', 'mask_file')]), + (gen_idx, eddy_quad, [('out_file', 'idx_file')]), + (acqp, eddy_quad, [('out_file', 'param_file')]), + # does avg_b0_1 need to be run? + (ecc, avg_b0_1, [('out_corrected', 'in_dwi')]), + (inputnode, avg_b0_1, [('bval_file', 'in_bval')]), + (avg_b0_1, bet_dwi1, [('out_file', 'in_file')]), + (ecc, outputnode, [('out_corrected', 'out_file')]), + (bet_dwi1, outputnode, [('mask_file', 'out_mask')]) + (ecc, outputnode, [('out_rotated_bvecs', 'out_bvec')])] + ) + + return dwi_wf From ecef8f2e38c8a6ae782c86e5c4ade583f2d30457 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Fri, 14 Jun 2019 09:43:49 -0400 Subject: [PATCH 003/156] [REF] remove io module --- dmriprep/io.py | 84 -------------------------------------------------- 1 file changed, 84 deletions(-) delete mode 100644 dmriprep/io.py diff --git a/dmriprep/io.py b/dmriprep/io.py deleted file mode 100644 index a1543873..00000000 --- a/dmriprep/io.py +++ /dev/null @@ -1,84 +0,0 @@ -""" -BIDS-functions to return inputs for the run.py functions. - -""" -import os -import os.path as op -from glob import glob - -import bids - - -def get_bids_subject_input_files(subject_id, bids_input_directory): - """ - Function to return the needed files for dmriprep based on subject id and a bids directory. - - :param subject_id: string - :param bids_input_directory: string to bids dir - :return: dict of inputs - """ - layout = bids.layout.BIDSLayout(bids_input_directory, validate=False) - subjects = layout.get_subjects() - assert subject_id in subjects, "subject {} is not in the bids folder".format(subject_id) - - ap_file = layout.get(subject=subject_id, - datatype='fmap', - suffix='epi', - dir='AP', - extensions=['.nii', '.nii.gz']) - assert len(ap_file) == 1, 'found {} ap fieldmap files and we need just 1'.format(len(ap_file)) - - pa_file = layout.get(subject=subject_id, - datatype='fmap', - suffix='epi', - dir='PA', - extensions=['.nii', '.nii.gz']) - assert len(pa_file) == 1, 'found {} pa fieldmap files and we need just 1'.format(len(pa_file)) - - dwi_files = layout.get(subject=subject_id, datatype='dwi', suffix='dwi') - valid_dwi_files = [] - - for d in dwi_files: - if d.path.startswith(op.abspath(op.join(bids_input_directory, 'sub-' + subject_id))): - valid_dwi_files.append(d.path) - - dwi_file = [d for d in valid_dwi_files if d.endswith('.nii.gz') and not "TRACE" in d] - assert len(dwi_file) == 1, 'found {} dwi files and we need just 1'.format(len(dwi_file)) - - bval_file = [d.path for d in dwi_files if d.filename.endswith('.bval')] - assert len(bval_file) == 1, 'found {} bval files and we need just 1'.format(len(bval_file)) - - bvec_file = [d.path for d in dwi_files if d.filename.endswith('.bvec')] - assert len(bvec_file) == 1, 'found {} bvec files and we need just 1'.format(len(bvec_file)) - - subjects_dir = op.join(bids_input_directory, 'derivatives', 'sub-'+subject_id) - - if not op.exists(op.join(subjects_dir, 'freesurfer')): - raise NotImplementedError('we have not yet implemented a version of dmriprep that runs freesurfer for you.' - 'please run freesurfer and try again' - ) - - outputs = dict(subject_id="sub-"+subject_id, - dwi_file=dwi_file[0], - dwi_file_AP=ap_file[0].path, - dwi_file_PA=pa_file[0].path, - bvec_file=bvec_file[0], - bval_file=bval_file[0], - subjects_dir=op.abspath(subjects_dir)) - return outputs - - -def get_bids_files(subject_id, bids_input_directory): - """ - subject to get all bids files for am optional subject id and bids dir. if subject id is blank then all subjects - are used. - :param subject_id: - :param bids_input_directory: - :return: - """ - if not subject_id: - subjects = [s.split("/")[-1].replace("sub-", "") for s in glob(os.path.join(bids_input_directory, "sub-*"))] - assert len(subjects), "No subject files found in bids directory" - return [get_bids_subject_input_files(sub, bids_input_directory) for sub in subjects] - else: - return [get_bids_subject_input_files(subject_id, bids_input_directory)] From feb25ab29ee92296fad99d4dfe54b54d9be9ab7d Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Fri, 14 Jun 2019 09:53:19 -0400 Subject: [PATCH 004/156] [REF] modify cli to use refactored workflows --- dmriprep/cli.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/dmriprep/cli.py b/dmriprep/cli.py index 8b45d7eb..215b6af8 100644 --- a/dmriprep/cli.py +++ b/dmriprep/cli.py @@ -7,9 +7,10 @@ import click -from . import io from . import run +from . import utils from .data import get_dataset +from ..workflows.base import init_dmriprep_wf # Filter warnings that are visible whenever you import another package that # was compiled against an older numpy than is installed. @@ -66,14 +67,15 @@ def main(participant_label, bids_dir, output_dir, raise NotImplementedError('The only valid analysis level for dmriprep ' 'is participant at the moment.') - inputs = io.get_bids_files(participant_label, bids_dir) + layout = BIDSLayout(bids_dir, validate=False) + subject_list = utils.collect_participants(layout, + participant_label=participant_label) - for subject_inputs in inputs: - run.run_dmriprep_pe(**subject_inputs, - working_dir=os.path.join(output_dir, 'scratch'), - out_dir=output_dir, - eddy_niter=eddy_niter, - slice_outlier_threshold=slice_outlier_threshold) + wf = init_dmriprep_wf(layout, subject_list, work_dir, output_dir) + wf.write_graph(graph2use='colored') + wf.config['execution']['remove_unnecessary_outputs'] = False + wf.config['execution']['keep_inputs'] = True + wf.run() return 0 From 65525ab85dbddde50ee1b7d50b5827f394ac973d Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Fri, 14 Jun 2019 10:21:05 -0400 Subject: [PATCH 005/156] [REF] remove io module import --- dmriprep/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/dmriprep/__init__.py b/dmriprep/__init__.py index 6150760e..cebc3634 100644 --- a/dmriprep/__init__.py +++ b/dmriprep/__init__.py @@ -17,7 +17,6 @@ warnings.filterwarnings("ignore", message="numpy.ufunc size changed") from . import data -from . import io from . import qc from . import run From 77a18b109769e3dac9e6279da3b1e4a7e7313d5a Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Fri, 14 Jun 2019 10:28:10 -0400 Subject: [PATCH 006/156] [REF] remove unused dmriprep module --- dmriprep/dmriprep.py | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 dmriprep/dmriprep.py diff --git a/dmriprep/dmriprep.py b/dmriprep/dmriprep.py deleted file mode 100644 index 47911d9e..00000000 --- a/dmriprep/dmriprep.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- - -"""Main module.""" - -import logging -import os -import os.path as op -import subprocess - -from .run import run_dmriprep - - -mod_logger = logging.getLogger(__name__) From 377a9b518ea0eda3f5543340c219b07563be28b0 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Fri, 14 Jun 2019 11:35:46 -0400 Subject: [PATCH 007/156] [FIX] defined work_dir --- dmriprep/cli.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dmriprep/cli.py b/dmriprep/cli.py index 215b6af8..aec29e00 100644 --- a/dmriprep/cli.py +++ b/dmriprep/cli.py @@ -71,6 +71,7 @@ def main(participant_label, bids_dir, output_dir, subject_list = utils.collect_participants(layout, participant_label=participant_label) + work_dir = os.path.join(output_dir, 'scratch') wf = init_dmriprep_wf(layout, subject_list, work_dir, output_dir) wf.write_graph(graph2use='colored') wf.config['execution']['remove_unnecessary_outputs'] = False From f8eb6ef0a350e08cbf918bf7348336251e1a3335 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Fri, 14 Jun 2019 14:30:12 -0400 Subject: [PATCH 008/156] [STY] black formatting changes --- dmriprep/cli.py | 171 ++++++++++++++++++++++++++++------------------ dmriprep/utils.py | 69 ++++++++++++------- 2 files changed, 148 insertions(+), 92 deletions(-) diff --git a/dmriprep/cli.py b/dmriprep/cli.py index aec29e00..261e2985 100644 --- a/dmriprep/cli.py +++ b/dmriprep/cli.py @@ -10,7 +10,7 @@ from . import run from . import utils from .data import get_dataset -from ..workflows.base import init_dmriprep_wf +from .workflows.base import init_dmriprep_wf # Filter warnings that are visible whenever you import another package that # was compiled against an older numpy than is installed. @@ -19,37 +19,47 @@ @click.command() -@click.option('--participant-label', - help="The label(s) of the participant(s) that should be" - "analyzed. The label corresponds to" - "sub- from the BIDS spec (so it does" - "not include 'sub-'). If this parameter is not provided" - "all subjects will be analyzed. Multiple participants" - "can be specified with a space separated list.", - default=None) -@click.option('--eddy-niter', - help="Fixed number of eddy iterations. See " - "https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/eddy/UsersGuide" - "#A--niter", - default=5, type=(int)) -@click.option('--slice-outlier-threshold', - help="Number of allowed outlier slices per volume. " - "If this is exceeded the volume is dropped from analysis. " - "If an int is provided, it is treated as number of allowed " - "outlier slices. If a float between 0 and 1 " - "(exclusive) is provided, it is treated the fraction of " - "allowed outlier slices.", - default=0.02) -@click.argument('bids_dir', - ) -@click.argument('output_dir', - ) -@click.argument('analysis_level', - type=click.Choice(['participant', 'group']), - default='participant') -def main(participant_label, bids_dir, output_dir, - eddy_niter=5, slice_outlier_threshold=0.02, - analysis_level="participant"): +@click.option( + "--participant-label", + help="The label(s) of the participant(s) that should be" + "analyzed. The label corresponds to" + "sub- from the BIDS spec (so it does" + "not include 'sub-'). If this parameter is not provided" + "all subjects will be analyzed. Multiple participants" + "can be specified with a space separated list.", + default=None, +) +@click.option( + "--eddy-niter", + help="Fixed number of eddy iterations. See " + "https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/eddy/UsersGuide" + "#A--niter", + default=5, + type=(int), +) +@click.option( + "--slice-outlier-threshold", + help="Number of allowed outlier slices per volume. " + "If this is exceeded the volume is dropped from analysis. " + "If an int is provided, it is treated as number of allowed " + "outlier slices. If a float between 0 and 1 " + "(exclusive) is provided, it is treated the fraction of " + "allowed outlier slices.", + default=0.02, +) +@click.argument("bids_dir") +@click.argument("output_dir") +@click.argument( + "analysis_level", type=click.Choice(["participant", "group"]), default="participant" +) +def main( + participant_label, + bids_dir, + output_dir, + eddy_niter=5, + slice_outlier_threshold=0.02, + analysis_level="participant", +): """ BIDS_DIR: The directory with the input dataset formatted according to the BIDS standard. @@ -63,30 +73,39 @@ def main(participant_label, bids_dir, output_dir, participant level analyses can be run independently (in parallel). """ - if analysis_level is not 'participant': - raise NotImplementedError('The only valid analysis level for dmriprep ' - 'is participant at the moment.') + if analysis_level is not "participant": + raise NotImplementedError( + "The only valid analysis level for dmriprep " + "is participant at the moment." + ) layout = BIDSLayout(bids_dir, validate=False) - subject_list = utils.collect_participants(layout, - participant_label=participant_label) + subject_list = utils.collect_participants( + layout, participant_label=participant_label + ) - work_dir = os.path.join(output_dir, 'scratch') + work_dir = os.path.join(output_dir, "scratch") wf = init_dmriprep_wf(layout, subject_list, work_dir, output_dir) - wf.write_graph(graph2use='colored') - wf.config['execution']['remove_unnecessary_outputs'] = False - wf.config['execution']['keep_inputs'] = True + wf.write_graph(graph2use="colored") + wf.config["execution"]["remove_unnecessary_outputs"] = False + wf.config["execution"]["keep_inputs"] = True wf.run() return 0 + @click.command() -@click.argument('output_dir', - ) -@click.option('--subject', help="subject id to download (will choose 1 subject if not specified", - default="sub-NDARBA507GCT") -@click.option('--study', help="which study to download. Right now we only support the HBN dataset", - default="HBN") +@click.argument("output_dir") +@click.option( + "--subject", + help="subject id to download (will choose 1 subject if not specified", + default="sub-NDARBA507GCT", +) +@click.option( + "--study", + help="which study to download. Right now we only support the HBN dataset", + default="HBN", +) def data(output_dir, study="HBN", subject="sub-NDARBA507GCT"): """ Download dwi raw data in BIDS format from public datasets @@ -99,21 +118,27 @@ def data(output_dir, study="HBN", subject="sub-NDARBA507GCT"): if not os.path.exists(output_dir): os.makedirs(output_dir) - if study.upper() != 'HBN': - raise NotImplementedError('We only support data downloads from the HBN dataset right now.') + if study.upper() != "HBN": + raise NotImplementedError( + "We only support data downloads from the HBN dataset right now." + ) get_dataset(os.path.abspath(output_dir), source=study.upper(), subject_id=subject) - print('done') + print("done") @click.command() -@click.argument('output_dir') -@click.argument('bucket') -@click.option('--access_key', help="your AWS access key") -@click.option('--secret_key', help="your AWS access secret") -@click.option('--provider', default='s3', help="Cloud storage provider. Only S3 is supported right now.") -@click.option('--subject', default=None, help="Subject id to upload (optional)") -def upload(output_dir, bucket, access_key, secret_key, provider='s3', subject=None): +@click.argument("output_dir") +@click.argument("bucket") +@click.option("--access_key", help="your AWS access key") +@click.option("--secret_key", help="your AWS access secret") +@click.option( + "--provider", + default="s3", + help="Cloud storage provider. Only S3 is supported right now.", +) +@click.option("--subject", default=None, help="Subject id to upload (optional)") +def upload(output_dir, bucket, access_key, secret_key, provider="s3", subject=None): """ OUTPUT_DIR: The directory where the output files were stored. @@ -125,31 +150,43 @@ def upload(output_dir, bucket, access_key, secret_key, provider='s3', subject=No from tqdm.auto import tqdm output_dir = os.path.abspath(output_dir) - if not output_dir.endswith('/'): - output_dir += '/' + if not output_dir.endswith("/"): + output_dir += "/" - if provider == 's3' or provider == 'S3': - client = boto3.client('s3', aws_access_key_id=access_key, aws_secret_access_key=secret_key) + if provider == "s3" or provider == "S3": + client = boto3.client( + "s3", aws_access_key_id=access_key, aws_secret_access_key=secret_key + ) if subject is not None: - assert os.path.exists(os.path.join(output_dir, subject)), 'this subject id does not exist!' + assert os.path.exists( + os.path.join(output_dir, subject) + ), "this subject id does not exist!" subjects = [subject] else: - subjects = [os.path.split(s)[1] for s in glob(os.path.join(output_dir, 'sub-*'))] + subjects = [ + os.path.split(s)[1] for s in glob(os.path.join(output_dir, "sub-*")) + ] def upload_subject(sub, sub_idx): - base_dir = os.path.join(output_dir, sub, 'dmriprep') + base_dir = os.path.join(output_dir, sub, "dmriprep") for root, dirs, files in os.walk(base_dir): if len(files): - for f in tqdm(files, desc=f"Uploading {sub} {root.split('/')[-1]}", position=sub_idx): + for f in tqdm( + files, + desc=f"Uploading {sub} {root.split('/')[-1]}", + position=sub_idx, + ): filepath = os.path.join(root, f) - key = root.replace(output_dir, '') + key = root.replace(output_dir, "") client.upload_file(filepath, bucket, os.path.join(key, f)) uploads = [delayed(upload_subject)(s, idx) for idx, s in enumerate(subjects)] _ = list(compute(*uploads, scheduler="threads")) else: - raise NotImplementedError('Only S3 is the only supported provider for data uploads at the moment') + raise NotImplementedError( + "Only S3 is the only supported provider for data uploads at the moment" + ) if __name__ == "__main__": diff --git a/dmriprep/utils.py b/dmriprep/utils.py index d88fb120..58799bc8 100644 --- a/dmriprep/utils.py +++ b/dmriprep/utils.py @@ -5,6 +5,7 @@ import warnings import itertools +import logging import numpy as np from bids.layout import BIDSLayout @@ -72,10 +73,13 @@ class BIDSError(ValueError): def __init__(self, message, bids_root): indent = 10 header = '{sep} BIDS root folder: "{bids_root}" {sep}'.format( - bids_root=bids_root, sep=''.join(['-'] * indent)) - self.msg = '\n{header}\n{indent}{message}\n{footer}'.format( - header=header, indent=''.join([' '] * (indent + 1)), - message=message, footer=''.join(['-'] * len(header)) + bids_root=bids_root, sep="".join(["-"] * indent) + ) + self.msg = "\n{header}\n{indent}{message}\n{footer}".format( + header=header, + indent="".join([" "] * (indent + 1)), + message=message, + footer="".join(["-"] * len(header)), ) super(BIDSError, self).__init__(self.msg) self.bids_root = bids_root @@ -85,8 +89,9 @@ class BIDSWarning(RuntimeWarning): pass -def collect_participants(bids_dir, participant_label=None, strict=False, - bids_validate=True): +def collect_participants( + bids_dir, participant_label=None, strict=False, bids_validate=True +): """ List the participants under the BIDS root and checks that participants designated with the participant_label argument exist in that folder. @@ -124,11 +129,13 @@ def collect_participants(bids_dir, participant_label=None, strict=False, # Error: bids_dir does not contain subjects if not all_participants: raise BIDSError( - 'Could not find participants. Please make sure the BIDS data ' - 'structure is present and correct. Datasets can be validated online ' - 'using the BIDS Validator (http://bids-standard.github.io/bids-validator/).\n' - 'If you are using Docker for Mac or Docker for Windows, you ' - 'may need to adjust your "File sharing" preferences.', bids_dir) + "Could not find participants. Please make sure the BIDS data " + "structure is present and correct. Datasets can be validated online " + "using the BIDS Validator (http://bids-standard.github.io/bids-validator/).\n" + "If you are using Docker for Mac or Docker for Windows, you " + 'may need to adjust your "File sharing" preferences.', + bids_dir, + ) # No --participant-label was set, return all if not participant_label: @@ -138,20 +145,26 @@ def collect_participants(bids_dir, participant_label=None, strict=False, participant_label = [participant_label] # Drop sub- prefixes - participant_label = [sub[4:] if sub.startswith('sub-') else sub for sub in participant_label] + participant_label = [ + sub[4:] if sub.startswith("sub-") else sub for sub in participant_label + ] # Remove duplicates participant_label = sorted(set(participant_label)) # Remove labels not found found_label = sorted(set(participant_label) & all_participants) if not found_label: - raise BIDSError('Could not find participants [{}]'.format( - ', '.join(participant_label)), bids_dir) + raise BIDSError( + "Could not find participants [{}]".format(", ".join(participant_label)), + bids_dir, + ) # Warn if some IDs were not found notfound_label = sorted(set(participant_label) - all_participants) if notfound_label: - exc = BIDSError('Some participants were not found: {}'.format( - ', '.join(notfound_label)), bids_dir) + exc = BIDSError( + "Some participants were not found: {}".format(", ".join(notfound_label)), + bids_dir, + ) if strict: raise exc warnings.warn(exc.msg, BIDSWarning) @@ -159,8 +172,7 @@ def collect_participants(bids_dir, participant_label=None, strict=False, return found_label -def collect_data(bids_dir, participant_label, task=None, echo=None, - bids_validate=True): +def collect_data(bids_dir, participant_label, task=None, echo=None, bids_validate=True): """ Uses pybids to retrieve the input data for a given participant >>> bids_root, _ = collect_data(str(datadir / 'ds054'), '100185', @@ -194,15 +206,22 @@ def collect_data(bids_dir, participant_label, task=None, echo=None, layout = BIDSLayout(str(bids_dir), validate=bids_validate) queries = { - 'fmap': {'datatype': 'fmap'}, - 't2w': {'datatype': 'anat', 'suffix': 'T2w'}, - 't1w': {'datatype': 'anat', 'suffix': 'T1w'}, - 'dwi': {'datatype': 'dwi', 'suffix': 'dwi'}, + "fmap": {"datatype": "fmap"}, + "t2w": {"datatype": "anat", "suffix": "T2w"}, + "t1w": {"datatype": "anat", "suffix": "T1w"}, + "dwi": {"datatype": "dwi", "suffix": "dwi"}, } subj_data = { - dtype: sorted(layout.get(return_type='file', subject=participant_label, - extensions=['nii', 'nii.gz'], **query)) - for dtype, query in queries.items()} + dtype: sorted( + layout.get( + return_type="file", + subject=participant_label, + extensions=["nii", "nii.gz"], + **query + ) + ) + for dtype, query in queries.items() + } return subj_data, layout From 457965c6d1c42a1a983da2882a3291efe96ad5a4 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Fri, 14 Jun 2019 14:30:35 -0400 Subject: [PATCH 009/156] [FIX] remove unnecessary mask step --- dmriprep/workflows/dwi/base.py | 237 ++++++++++++++++++--------------- 1 file changed, 128 insertions(+), 109 deletions(-) diff --git a/dmriprep/workflows/dwi/base.py b/dmriprep/workflows/dwi/base.py index 5afff6a9..4b5c0d5b 100755 --- a/dmriprep/workflows/dwi/base.py +++ b/dmriprep/workflows/dwi/base.py @@ -1,46 +1,59 @@ #!/usr/bin/env python + def init_dwi_preproc_wf(dwi_file): from nipype.pipeline import engine as pe - from nipype.interfaces import (freesurfer as fs, fsl, mrtrix3, ants, \ - io as nio, utility as niu) + from nipype.interfaces import ( + freesurfer as fs, + fsl, + mrtrix3, + ants, + io as nio, + utility as niu, + ) from nipype import logging fmaps = [] fmaps = layout.get_fieldmap(dwi_file, return_list=True) for fmap in fmaps: - if fmap['suffix'] == 'phase': - fmap_key = 'phase1' + if fmap["suffix"] == "phase": + fmap_key = "phase1" else: - fmap_key = fmap['suffix'] - fmap['metadata'] = layout.get_metadata(fmap[fmap_key]) + fmap_key = fmap["suffix"] + fmap["metadata"] = layout.get_metadata(fmap[fmap_key]) dwi_wf = pe.Workflow(name="dwi_preproc_wf") - inputnode = pe.Node(niu.IdentityInterface(fields=[ - 'subject_id', - 'dwi_file', - 'metadata', - 'bvec_file', - 'bval_file', - 'out_dir', - 'eddy_niter', - 'slice_outlier_threshold']), - name="inputnode") + inputnode = pe.Node( + niu.IdentityInterface( + fields=[ + "subject_id", + "dwi_file", + "metadata", + "bvec_file", + "bval_file", + "out_dir", + "eddy_niter", + "slice_outlier_threshold", + ] + ), + name="inputnode", + ) outputnode = pe.Node( - niu.IdentityInterface(fields=['out_file', 'out_mask', 'out_bvec']), - name="outputnode") + niu.IdentityInterface(fields=["out_file", "out_mask", "out_bvec"]), + name="outputnode", + ) # name noise and out_file using fname_presuffix - denoise = pe.Node(mrtrix3.DWIDenoise(noise='noise.nii.gz', - out_file='denoised.nii.gz'), - name="denoise") + denoise = pe.Node( + mrtrix3.DWIDenoise(noise="noise.nii.gz", out_file="denoised.nii.gz"), + name="denoise", + ) # name unring using fname_presuffix - unring = pe.Node(mrtrix3.MRDeGibbs(out_file='unringed.nii.gz'), - name="unring") + unring = pe.Node(mrtrix3.MRDeGibbs(out_file="unringed.nii.gz"), name="unring") def gen_index(in_file): import os.path as op @@ -49,59 +62,65 @@ def gen_index(in_file): from nipype.utils import NUMPY_MMAP from nipype.utils.filemanip import fname_presuffix - out_file = fname_presuffix(in_file, suffix="_index.txt", newpath=op.abspath('.'), use_ext=False) + out_file = fname_presuffix( + in_file, suffix="_index.txt", newpath=op.abspath("."), use_ext=False + ) vols = nb.load(in_file, mmap=NUMPY_MMAP).get_data().shape[-1] - index_lines = np.ones((vols, )) + index_lines = np.ones((vols,)) index_lines_reshape = index_lines.reshape(1, index_lines.shape[0]) - np.savetxt(out_file, index_lines_reshape, fmt='%i') + np.savetxt(out_file, index_lines_reshape, fmt="%i") return out_file gen_idx = pe.Node( niu.Function( - input_names=['in_file'], - output_names=['out_file'], - function=gen_index), - name='gen_index') + input_names=["in_file"], output_names=["out_file"], function=gen_index + ), + name="gen_index", + ) def gen_acqparams(in_file, metadata): import os.path as op from nipype.utils.filemanip import fname_presuffix - out_file = fname_presuffix(in_file, suffix="_acqparams.txt", newpath=op.abspath('.'), use_ext=False) + out_file = fname_presuffix( + in_file, suffix="_acqparams.txt", newpath=op.abspath("."), use_ext=False + ) acq_param_dict = { - 'j': '0 1 0 %.7f', - 'j-': '0 -1 0 %.7f', - 'i': '1 0 0 %.7f', - 'i-': '-1 0 0 %.7f', - 'k': '0 0 1 %.7f', - 'k-': '0 0 -1 %.7f' + "j": "0 1 0 %.7f", + "j-": "0 -1 0 %.7f", + "i": "1 0 0 %.7f", + "i-": "-1 0 0 %.7f", + "k": "0 0 1 %.7f", + "k-": "0 0 -1 %.7f", } - pe_dir = metadata.get('PhaseEncodingDirection') - total_readout = metadata.get('TotalReadoutTime') + pe_dir = metadata.get("PhaseEncodingDirection") + total_readout = metadata.get("TotalReadoutTime") acq_param_lines = acq_param_dict[pe_dir] % total_readout - with open(out_file, 'w') as f: + with open(out_file, "w") as f: f.write(acq_param_lines) return out_file acqp = pe.Node( niu.Function( - input_names=['in_file', 'metadata'], - output_names=['out_file'], - function=gen_acqparams), - name='acqp') + input_names=["in_file", "metadata"], + output_names=["out_file"], + function=gen_acqparams, + ), + name="acqp", + ) def b0_average(in_dwi, in_bval, b0_thresh=10.0, out_file=None): """ A function that averages the *b0* volumes from a DWI dataset. As current dMRI data are being acquired with all b-values > 0.0, the *lowb* volumes are selected by specifying the parameter b0_thresh. - .. warning:: *b0* should be already registered (head motion artifact should - be corrected). + .. warning:: *b0* should be already registered (head motion artifact + should be corrected). """ import numpy as np import nibabel as nb @@ -110,7 +129,9 @@ def b0_average(in_dwi, in_bval, b0_thresh=10.0, out_file=None): from nipype.utils.filemanip import fname_presuffix if out_file is None: - out_file = fname_presuffix(in_dwi, suffix="_avg_b0", newpath=op.abspath('.')) + out_file = fname_presuffix( + in_dwi, suffix="_avg_b0", newpath=op.abspath(".") + ) imgs = np.array(nb.four_to_three(nb.load(in_dwi, mmap=NUMPY_MMAP))) bval = np.loadtxt(in_bval) @@ -121,56 +142,48 @@ def b0_average(in_dwi, in_bval, b0_thresh=10.0, out_file=None): hdr = imgs[0].header.copy() hdr.set_data_shape(b0.shape) - hdr.set_xyzt_units('mm') + hdr.set_xyzt_units("mm") hdr.set_data_dtype(np.float32) nb.Nifti1Image(b0, imgs[0].affine, hdr).to_filename(out_file) return out_file avg_b0_0 = pe.Node( niu.Function( - input_names=['in_dwi', 'in_bval'], - output_names=['out_file'], - function=b0_average), - name='b0_avg_pre') + input_names=["in_dwi", "in_bval"], + output_names=["out_file"], + function=b0_average, + ), + name="b0_avg_pre", + ) # dilate mask - bet_dwi0 = pe.Node( - fsl.BET(frac=0.3, mask=True, robust=True), name='bet_dwi_pre') + bet_dwi0 = pe.Node(fsl.BET(frac=0.3, mask=True, robust=True), name="bet_dwi_pre") mrtrix3.MaskFilter - ecc = pe.Node(fsl.Eddy(repol=True, - cnr_maps=True, - residuals=True, - method='jac'), - name='fsl_eddy') + ecc = pe.Node( + fsl.Eddy(repol=True, cnr_maps=True, residuals=True, method="jac"), + name="fsl_eddy", + ) import multiprocessing + ecc.inputs.num_threads = multiprocessing.cpu_count() from numba import cuda + try: if cuda.gpus: ecc.inputs.use_cuda = True except: ecc.inputs.use_cuda = False - avg_b0_1 = pe.Node( - niu.Function( - input_names=['in_dwi', 'in_bval'], - output_names=['out_file'], - function=b0_average), - name='b0_avg_post') - - bet_dwi1 = pe.Node( - fsl.BET(frac=0.3, mask=True, robust=True), name='bet_dwi_post') - eddy_quad = pe.Node(fsl.EddyQuad(verbose=True), name="eddy_quad") - get_path = lambda x: x.split('.nii.gz')[0].split('_fix')[0] - get_qc_path = lambda x: x.split('.nii.gz')[0] + '.qc' + get_path = lambda x: x.split(".nii.gz")[0].split("_fix")[0] + get_qc_path = lambda x: x.split(".nii.gz")[0] + ".qc" - dwi_bias_corr = pe.Node(ants.N4BiasFieldCorrection(), name='dwi_bias_corr') + dwi_bias_corr = pe.Node(ants.N4BiasFieldCorrection(), name="dwi_bias_corr") fslroi = pe.Node(fsl.ExtractROI(t_min=0, t_size=1), name="fslroi") @@ -180,50 +193,56 @@ def get_b0_mask_fn(b0_file): from dipy.segment.mask import median_otsu import os - mask_file = fname_presuffix(b0_file, suffix="_mask", newpath=os.path.abspath('.')) + mask_file = fname_presuffix( + b0_file, suffix="_mask", newpath=os.path.abspath(".") + ) img = nib.load(b0_file) data, aff = img.get_data(), img.affine _, mask = median_otsu(data, 2, 1) nib.Nifti1Image(mask.astype(float), aff).to_filename(mask_file) return mask_file - b0mask_node = pe.Node(niu.Function(input_names=['b0_file'], - output_names=['mask_file'], - function=get_b0_mask_fn), - name="getB0Mask") + b0mask_node = pe.Node( + niu.Function( + input_names=["b0_file"], output_names=["mask_file"], function=get_b0_mask_fn + ), + name="getB0Mask", + ) dwi_wf.connect( - [(inputnode, denoise, [('dwi_file', 'in_file')]), - (denoise, unring, [('out_file', 'in_file')]), - (inputnode, avg_b0_0, [('bval_file', 'in_bval')]), - (unring, avg_b0_0, [('out_file', 'in_dwi')]), - (avg_b0_0, bet_dwi0, [('out_file', 'in_file')]), - (inputnode, gen_idx, [('dwi_file', 'in_file')]), - (inputnode, acqp, [('dwi_file', 'in_file'), - ('metadata', 'metadata')]), - (unring, ecc, [('out_file', 'in_file')]), - (inputnode, ecc, [('bval_file', 'in_bval'), - ('bvec_file', 'in_bvec')]), - (bet_dwi0, ecc, [('mask_file', 'in_mask')]), - (gen_idx, ecc, [('out_file', 'in_index')]), - (acqp, ecc, [('out_file', 'in_acqp')]), - - (ecc, fslroi, [('out_corrected', 'in_file')]), - (fslroi, b0mask_node, [('roi_file', 'b0_file')]), - (ecc, eddy_quad, [(('out_corrected', get_path), 'base_name'), - (('out_corrected', get_qc_path), 'output_dir')]), - (inputnode, eddy_quad, [('bval_file', 'bval_file')]), - (ecc, eddy_quad, [('out_rotated_bvecs', 'bvec_file')]), - (b0mask_node, eddy_quad, [('mask_file', 'mask_file')]), - (gen_idx, eddy_quad, [('out_file', 'idx_file')]), - (acqp, eddy_quad, [('out_file', 'param_file')]), - # does avg_b0_1 need to be run? - (ecc, avg_b0_1, [('out_corrected', 'in_dwi')]), - (inputnode, avg_b0_1, [('bval_file', 'in_bval')]), - (avg_b0_1, bet_dwi1, [('out_file', 'in_file')]), - (ecc, outputnode, [('out_corrected', 'out_file')]), - (bet_dwi1, outputnode, [('mask_file', 'out_mask')]) - (ecc, outputnode, [('out_rotated_bvecs', 'out_bvec')])] - ) + [ + (inputnode, denoise, [("dwi_file", "in_file")]), + (denoise, unring, [("out_file", "in_file")]), + (inputnode, avg_b0_0, [("bval_file", "in_bval")]), + (unring, avg_b0_0, [("out_file", "in_dwi")]), + (avg_b0_0, bet_dwi0, [("out_file", "in_file")]), + (inputnode, gen_idx, [("dwi_file", "in_file")]), + (inputnode, acqp, [("dwi_file", "in_file"), ("metadata", "metadata")]), + (unring, ecc, [("out_file", "in_file")]), + (inputnode, ecc, [("bval_file", "in_bval"), ("bvec_file", "in_bvec")]), + (bet_dwi0, ecc, [("mask_file", "in_mask")]), + (gen_idx, ecc, [("out_file", "in_index")]), + (acqp, ecc, [("out_file", "in_acqp")]), + (ecc, fslroi, [("out_corrected", "in_file")]), + (fslroi, b0mask_node, [("roi_file", "b0_file")]), + ( + ecc, + eddy_quad, + [ + (("out_corrected", get_path), "base_name"), + (("out_corrected", get_qc_path), "output_dir"), + ], + ), + (inputnode, eddy_quad, [("bval_file", "bval_file")]), + (ecc, eddy_quad, [("out_rotated_bvecs", "bvec_file")]), + (b0mask_node, eddy_quad, [("mask_file", "mask_file")]), + (gen_idx, eddy_quad, [("out_file", "idx_file")]), + (acqp, eddy_quad, [("out_file", "param_file")]), + (ecc, outputnode, [("out_corrected", "out_file")]), + (b0mask_node, outputnode, [("mask_file", "out_mask")])( + ecc, outputnode, [("out_rotated_bvecs", "out_bvec")] + ), + ] + ) return dwi_wf From d9e1fd7ae0c6fe897b60bb6f28082ef051ff512e Mon Sep 17 00:00:00 2001 From: Salim Mansour Date: Fri, 14 Jun 2019 16:18:35 -0400 Subject: [PATCH 010/156] Fixed module recognition and added input spec --- dmriprep/cli.py | 1 + dmriprep/workflows/base.py | 11 +++++++++++ dmriprep/workflows/dwi/base.py | 17 ++++++++--------- setup.py | 2 +- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/dmriprep/cli.py b/dmriprep/cli.py index 261e2985..2681c922 100644 --- a/dmriprep/cli.py +++ b/dmriprep/cli.py @@ -4,6 +4,7 @@ import os import sys import warnings +from bids import BIDSLayout import click diff --git a/dmriprep/workflows/base.py b/dmriprep/workflows/base.py index 95b0dcaf..089c86d0 100644 --- a/dmriprep/workflows/base.py +++ b/dmriprep/workflows/base.py @@ -45,6 +45,17 @@ def init_single_subject_wf(layout, subject_id, name, work_dir, output_dir): for dwi_file in subject_data['dwi']: dwi_preproc_wf = init_dwi_preproc_wf(dwi_file=dwi_file, layout=layout) + dwi_preproc_wf.base_dir = os.path.join(os.path.abspath(work_dir), subject_id) + entities = layout.parse_file_entities(dwi_file) + session_id = entities['session'] + + inputspec = dwi_preproc_wf.get_node('inputnode') + inputspec.inputs.subject_id = subject_id + inputspec.inputs.dwi_file = dwi_file + inputspec.inputs.metadata = layout.get_metadata(dwi_file) + inputspec.inputs.bvec_file = layout.get_bvec(dwi_file) + inputspec.inputs.bval_file = layout.get_bval(dwi_file) + inputspec.inputs.out_dir = os.path.abspath(output_dir) subject_wf.add_nodes([dwi_preproc_wf]) diff --git a/dmriprep/workflows/dwi/base.py b/dmriprep/workflows/dwi/base.py index 4b5c0d5b..de4fef1c 100755 --- a/dmriprep/workflows/dwi/base.py +++ b/dmriprep/workflows/dwi/base.py @@ -1,7 +1,7 @@ #!/usr/bin/env python -def init_dwi_preproc_wf(dwi_file): +def init_dwi_preproc_wf(dwi_file, layout): from nipype.pipeline import engine as pe from nipype.interfaces import ( freesurfer as fs, @@ -43,13 +43,13 @@ def init_dwi_preproc_wf(dwi_file): outputnode = pe.Node( niu.IdentityInterface(fields=["out_file", "out_mask", "out_bvec"]), - name="outputnode", + name="outputnode" ) # name noise and out_file using fname_presuffix denoise = pe.Node( mrtrix3.DWIDenoise(noise="noise.nii.gz", out_file="denoised.nii.gz"), - name="denoise", + name="denoise" ) # name unring using fname_presuffix @@ -159,7 +159,7 @@ def b0_average(in_dwi, in_bval, b0_thresh=10.0, out_file=None): # dilate mask bet_dwi0 = pe.Node(fsl.BET(frac=0.3, mask=True, robust=True), name="bet_dwi_pre") - mrtrix3.MaskFilter + #mrtrix3.MaskFilter ecc = pe.Node( fsl.Eddy(repol=True, cnr_maps=True, residuals=True, method="jac"), @@ -230,8 +230,8 @@ def get_b0_mask_fn(b0_file): eddy_quad, [ (("out_corrected", get_path), "base_name"), - (("out_corrected", get_qc_path), "output_dir"), - ], + (("out_corrected", get_qc_path), "output_dir") + ] ), (inputnode, eddy_quad, [("bval_file", "bval_file")]), (ecc, eddy_quad, [("out_rotated_bvecs", "bvec_file")]), @@ -239,9 +239,8 @@ def get_b0_mask_fn(b0_file): (gen_idx, eddy_quad, [("out_file", "idx_file")]), (acqp, eddy_quad, [("out_file", "param_file")]), (ecc, outputnode, [("out_corrected", "out_file")]), - (b0mask_node, outputnode, [("mask_file", "out_mask")])( - ecc, outputnode, [("out_rotated_bvecs", "out_bvec")] - ), + (b0mask_node, outputnode, [("mask_file", "out_mask")]), + (ecc, outputnode, [("out_rotated_bvecs", "out_bvec")]) ] ) diff --git a/setup.py b/setup.py index 38597c97..54452b5b 100644 --- a/setup.py +++ b/setup.py @@ -59,7 +59,7 @@ include_package_data=True, keywords="dmriprep", name="dmriprep", - packages=find_packages(include=["dmriprep"]), + packages=find_packages(include=["dmriprep*"]), setup_requires=setup_requirements, test_suite="tests", tests_require=test_requirements, From 556ecab707c89d6b382436050a11bdb4ebfe3e52 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Fri, 14 Jun 2019 17:11:29 -0400 Subject: [PATCH 011/156] [STY] changes created by black --- dmriprep/__init__.py | 18 +- dmriprep/data.py | 472 ++++++++++++++++----------------- dmriprep/qc.py | 128 +++++---- dmriprep/workflows/base.py | 29 +- dmriprep/workflows/dwi/base.py | 12 +- 5 files changed, 334 insertions(+), 325 deletions(-) diff --git a/dmriprep/__init__.py b/dmriprep/__init__.py index cebc3634..e0d2b3df 100644 --- a/dmriprep/__init__.py +++ b/dmriprep/__init__.py @@ -3,8 +3,8 @@ """Top-level package for dmriprep.""" __author__ = """Anisha Keshavan""" -__email__ = 'anishakeshavan@gmail.com' -__version__ = '0.1.0' +__email__ = "anishakeshavan@gmail.com" +__version__ = "0.1.0" import errno import logging @@ -24,34 +24,32 @@ # get the log level from environment variable if "DMIRPREP_LOGLEVEL" in os.environ: - loglevel = os.environ['DMRIPREP_LOGLEVEL'] + loglevel = os.environ["DMRIPREP_LOGLEVEL"] module_logger.setLevel(getattr(logging, loglevel.upper())) else: module_logger.setLevel(logging.WARNING) # create a file handler -logpath = os.path.join(os.path.expanduser('~'), '.dmriprep', 'dmriprep.log') +logpath = os.path.join(os.path.expanduser("~"), ".dmriprep", "dmriprep.log") # Create the config directory if it doesn't exist logdir = os.path.dirname(logpath) try: os.makedirs(logdir) except OSError as e: - pre_existing = (e.errno == errno.EEXIST and os.path.isdir(logdir)) + pre_existing = e.errno == errno.EEXIST and os.path.isdir(logdir) if pre_existing: pass else: raise e -handler = logging.FileHandler(logpath, mode='w') +handler = logging.FileHandler(logpath, mode="w") handler.setLevel(logging.DEBUG) # create a logging format -formatter = logging.Formatter( - '%(asctime)s - %(name)s - %(levelname)s - %(message)s' -) +formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") handler.setFormatter(formatter) # add the handlers to the logger module_logger.addHandler(handler) -module_logger.info('Started new dmriprep session') +module_logger.info("Started new dmriprep session") diff --git a/dmriprep/data.py b/dmriprep/data.py index d06767e5..15b4366e 100644 --- a/dmriprep/data.py +++ b/dmriprep/data.py @@ -20,11 +20,11 @@ mod_logger = logging.getLogger(__name__) -def get_dataset(output_dir, source='HBN', subject_id='sub-NDARBA507GCT'): - if source in ['HBN']: +def get_dataset(output_dir, source="HBN", subject_id="sub-NDARBA507GCT"): + if source in ["HBN"]: get_hbn_data(output_dir, subject_id) else: - raise ValueError('Invalid dataset source') + raise ValueError("Invalid dataset source") def get_hbn_data(output_dir, subject_id): @@ -47,11 +47,11 @@ def get_s3_client(): from botocore.client import Config # Global s3 client to preserve anonymous config - s3_client = boto3.client('s3', config=Config(signature_version=UNSIGNED)) + s3_client = boto3.client("s3", config=Config(signature_version=UNSIGNED)) return s3_client -def _get_matching_s3_keys(bucket, prefix='', suffix=''): +def _get_matching_s3_keys(bucket, prefix="", suffix=""): """Generate all the matching keys in an S3 bucket. Parameters @@ -71,12 +71,12 @@ def _get_matching_s3_keys(bucket, prefix='', suffix=''): S3 keys that match the prefix and suffix """ s3 = get_s3_client() - kwargs = {'Bucket': bucket, "MaxKeys": 1000} + kwargs = {"Bucket": bucket, "MaxKeys": 1000} # If the prefix is a single string (not a tuple of strings), we can # do the filtering directly in the S3 API. if isinstance(prefix, str): - kwargs['Prefix'] = prefix + kwargs["Prefix"] = prefix while True: # The S3 API response is a large blob of metadata. @@ -84,12 +84,12 @@ def _get_matching_s3_keys(bucket, prefix='', suffix=''): resp = s3.list_objects_v2(**kwargs) try: - contents = resp['Contents'] + contents = resp["Contents"] except KeyError: return for obj in contents: - key = obj['Key'] + key = obj["Key"] if key.startswith(prefix) and key.endswith(suffix): yield key @@ -97,7 +97,7 @@ def _get_matching_s3_keys(bucket, prefix='', suffix=''): # Pass the continuation token into the next response, until we # reach the final page (when this field is missing). try: - kwargs['ContinuationToken'] = resp['NextContinuationToken'] + kwargs["ContinuationToken"] = resp["NextContinuationToken"] except KeyError: break @@ -129,7 +129,7 @@ def _download_from_s3(fname, bucket, key, overwrite=False): # Download the file s3.download_file(Bucket=bucket, Key=key, Filename=fname) except FileExistsError: - mod_logger.info(f'File {fname} already exists. Continuing...') + mod_logger.info(f"File {fname} already exists. Continuing...") pass @@ -179,12 +179,12 @@ def _cumulative_paths(path_parts, add_ext=""): except IndexError: pass - return [''.join(path_parts[:i+1]) + add_ext - for i in range(len(path_parts))] + return ["".join(path_parts[: i + 1]) + add_ext for i in range(len(path_parts))] class Study: """A dMRI based study with a BIDS compliant directory structure""" + def __init__(self, study_id, bucket, s3_prefix, subjects=None): """Initialize a Study instance @@ -214,16 +214,18 @@ def __init__(self, study_id, bucket, s3_prefix, subjects=None): if not isinstance(s3_prefix, str): raise TypeError("s3_prefix must be a string.") - if not (subjects is None or - isinstance(subjects, int) or - isinstance(subjects, str) or - all(isinstance(s, str) for s in subjects)): - raise TypeError("subjects must be an int, string or a " - "sequence of strings.") + if not ( + subjects is None + or isinstance(subjects, int) + or isinstance(subjects, str) + or all(isinstance(s, str) for s in subjects) + ): + raise TypeError( + "subjects must be an int, string or a " "sequence of strings." + ) if isinstance(subjects, int) and subjects < 1: - raise ValueError("If subjects is an int, it must be " - "greater than 0.") + raise ValueError("If subjects is an int, it must be " "greater than 0.") self._study_id = study_id self._bucket = bucket @@ -256,9 +258,7 @@ def __init__(self, study_id, bucket, s3_prefix, subjects=None): f"{set(subjects) - set(self._all_subjects.keys())}" ) - subs = [ - delayed(self._get_subject)(s) for s in set(subjects) - ] + subs = [delayed(self._get_subject)(s) for s in set(subjects)] print("Retrieving subject S3 keys") with ProgressBar(): @@ -274,15 +274,13 @@ def __init__(self, study_id, bucket, s3_prefix, subjects=None): if n_needed == 1: subjects = [sorted(self._all_subjects.keys())[idx_lo]] else: - subjects = sorted( - self._all_subjects.keys() - )[idx_lo:idx_lo + n_needed] + subjects = sorted(self._all_subjects.keys())[ + idx_lo : idx_lo + n_needed + ] idx_lo += n_needed - subs = [ - delayed(self._get_subject)(s) for s in set(subjects) - ] + subs = [delayed(self._get_subject)(s) for s in set(subjects)] with ProgressBar(): subjects = list(compute(*subs, scheduler="threads")) @@ -293,7 +291,6 @@ def __init__(self, study_id, bucket, s3_prefix, subjects=None): else: self._n_discarded = len([s for s in subjects if not s.valid]) - @property def study_id(self): """An identifier string for the study""" @@ -315,14 +312,16 @@ def subjects(self): return self._subjects def __repr__(self): - return (f"{type(self).__name__}(study_id={self.study_id}, " - f"bucket={self.bucket}, s3_prefix={self.s3_prefix})") + return ( + f"{type(self).__name__}(study_id={self.study_id}, " + f"bucket={self.bucket}, s3_prefix={self.s3_prefix})" + ) def _get_subject(self, subject_id): """Return a Subject instance from a subject-ID""" - return Subject(subject_id=subject_id, - site=self._all_subjects[subject_id], - study=self) + return Subject( + subject_id=subject_id, site=self._all_subjects[subject_id], study=self + ) def list_all_subjects(self): """Return a study-specific list of subjects. @@ -354,8 +353,7 @@ def postprocess(self, subject): """ pass - def download(self, directory, include_site=False, overwrite=False, - pbar=True): + def download(self, directory, include_site=False, overwrite=False, pbar=True): """Download files for each subject in the study Parameters @@ -376,13 +374,16 @@ def download(self, directory, include_site=False, overwrite=False, -------- dmriprep.data.Subject.download() """ - results = [delayed(sub.download)( - directory=directory, - include_site=include_site, - overwrite=overwrite, - pbar=pbar, - pbar_idx=idx, - ) for idx, sub in enumerate(self.subjects)] + results = [ + delayed(sub.download)( + directory=directory, + include_site=include_site, + overwrite=overwrite, + pbar=pbar, + pbar_idx=idx, + ) + for idx, sub in enumerate(self.subjects) + ] compute(*results, scheduler="threads") @@ -391,8 +392,8 @@ class S3BidsStudy(Study): """ """ - def __init__(self, study_id, bucket, s3_prefix=None, - subjects=None): + + def __init__(self, study_id, bucket, s3_prefix=None, subjects=None): """ Initialize a study which is organized as BIDS compliant S3 bucket, or a sub-path of this bucket. @@ -415,10 +416,8 @@ def __init__(self, study_id, bucket, s3_prefix=None, if s3_prefix is None: s3_prefix = "" super().__init__( - study_id=study_id, - bucket=bucket, - s3_prefix=s3_prefix, - subjects=subjects) + study_id=study_id, bucket=bucket, s3_prefix=s3_prefix, subjects=subjects + ) def list_all_subjects(self): """ @@ -427,8 +426,6 @@ def list_all_subjects(self): # XXX Ariel will figure this out. - - class HBN(Study): """The HBN study @@ -436,6 +433,7 @@ class HBN(Study): -------- dmriprep.data.Study """ + def __init__(self, subjects=None): """Initialize the HBN instance @@ -450,7 +448,7 @@ def __init__(self, subjects=None): study_id="HBN", bucket="fcp-indi", s3_prefix="data/Projects/HBN/MRI", - subjects=subjects + subjects=subjects, ) def list_all_subjects(self): @@ -463,32 +461,27 @@ def list_all_subjects(self): dict dict with participant_id as keys and site_id as values """ + def get_site_tsv_keys(site_id): - pre = 'data/Projects/HBN/MRI/' - raw = pre + f'{site_id}/participants.tsv' - deriv = pre + f'{site_id}/derivatives/participants.tsv' - return {'raw': raw, 'deriv': deriv} + pre = "data/Projects/HBN/MRI/" + raw = pre + f"{site_id}/participants.tsv" + deriv = pre + f"{site_id}/derivatives/participants.tsv" + return {"raw": raw, "deriv": deriv} - sites = ['Site-CBIC', 'Site-RU', 'Site-SI'] + sites = ["Site-CBIC", "Site-RU", "Site-SI"] tsv_keys = {site: get_site_tsv_keys(site) for site in sites} s3 = get_s3_client() def get_subs_from_tsv_key(s3_key): - response = s3.get_object( - Bucket=self.bucket, - Key=s3_key - ) + response = s3.get_object(Bucket=self.bucket, Key=s3_key) - return set(pd.read_csv( - response.get('Body') - ).participant_id.values) + return set(pd.read_csv(response.get("Body")).participant_id.values) subjects = {} for site, s3_keys in tsv_keys.items(): - site_subs = {k: get_subs_from_tsv_key(v) - for k, v in s3_keys.items()} - subjects[site] = site_subs['raw'] & site_subs['deriv'] + site_subs = {k: get_subs_from_tsv_key(v) for k, v in s3_keys.items()} + subjects[site] = site_subs["raw"] & site_subs["deriv"] all_subjects = {} for site, subs in subjects.items(): @@ -511,19 +504,15 @@ def filter_keys(self, subject): subject instance """ if subject.site == "Site-CBIC": - t1w_keys = subject.s3_keys['t1w'] - freesurfer_keys = subject.s3_keys['freesurfer'] + t1w_keys = subject.s3_keys["t1w"] + freesurfer_keys = subject.s3_keys["freesurfer"] correct_dir = "T1w_VNavNorm" - subject._s3_keys['t1w'] = list(filter( - lambda x: correct_dir in x, - t1w_keys - )) + subject._s3_keys["t1w"] = list(filter(lambda x: correct_dir in x, t1w_keys)) - subject._s3_keys['freesurfer'] = list(filter( - lambda x: correct_dir in x, - freesurfer_keys - )) + subject._s3_keys["freesurfer"] = list( + filter(lambda x: correct_dir in x, freesurfer_keys) + ) def postprocess(self, subject): """Move the T1 file back into the freesurfer directory. @@ -538,27 +527,27 @@ def postprocess(self, subject): subject instance """ for sess in subject.files.keys(): - t1_file = subject.files[sess]['t1w'][0] - freesurfer_path = op.join(op.dirname(t1_file), 'freesurfer') + t1_file = subject.files[sess]["t1w"][0] + freesurfer_path = op.join(op.dirname(t1_file), "freesurfer") - convert_cmd = 'mri_convert {in_:s} {out_:s}'.format( - in_=t1_file, out_=op.join(freesurfer_path, 'mri', 'orig.mgz') + convert_cmd = "mri_convert {in_:s} {out_:s}".format( + in_=t1_file, out_=op.join(freesurfer_path, "mri", "orig.mgz") ) - fnull = open(os.devnull, 'w') - subprocess.call(convert_cmd.split(), - stdout=fnull, - stderr=subprocess.STDOUT) + fnull = open(os.devnull, "w") + subprocess.call(convert_cmd.split(), stdout=fnull, stderr=subprocess.STDOUT) # if the site is CBIC, then the freesurfer directory has an additional level. # move that level up by 1 (e.g. removing the T1w_VNavNorm folder - if subject.site == 'Site-CBIC': - newpath = freesurfer_path.replace('T1w_VNavNorm/', '') - move_cmd = 'mv {oldpath} {newpath}'.format(oldpath=freesurfer_path, newpath=newpath) - fnull1 = open(os.devnull, 'w') - subprocess.call(move_cmd.split(), - stdout=fnull1, - stderr=subprocess.STDOUT) + if subject.site == "Site-CBIC": + newpath = freesurfer_path.replace("T1w_VNavNorm/", "") + move_cmd = "mv {oldpath} {newpath}".format( + oldpath=freesurfer_path, newpath=newpath + ) + fnull1 = open(os.devnull, "w") + subprocess.call( + move_cmd.split(), stdout=fnull1, stderr=subprocess.STDOUT + ) # now check that the AP/PA files are named correctly # eg it should look like "sub-{id}_dir-{dir}_acq-dwi_epi.nii.gz @@ -568,6 +557,7 @@ def postprocess(self, subject): class Subject: """A single dMRI study subject""" + def __init__(self, subject_id, study, site=None): """Initialize a Subject instance @@ -652,9 +642,11 @@ def files(self): return self._files def __repr__(self): - return (f"{type(self).__name__}(subject_id={self.subject_id}, " - f"study_id={self.study.study_id}, site={self.site}, " - f"valid={self.valid})") + return ( + f"{type(self).__name__}(subject_id={self.subject_id}, " + f"study_id={self.study.study_id}, site={self.site}, " + f"valid={self.valid})" + ) def _list_s3_keys(self): """Get all required S3 keys for this subject @@ -665,20 +657,15 @@ def _list_s3_keys(self): S3 keys organized into "raw" and "deriv" lists """ prefixes = { - 'raw': '/'.join([self.study.s3_prefix, - self.site, - self.subject_id]), - 'deriv': '/'.join([self.study.s3_prefix, - self.site, - 'derivatives', - self.subject_id]), + "raw": "/".join([self.study.s3_prefix, self.site, self.subject_id]), + "deriv": "/".join( + [self.study.s3_prefix, self.site, "derivatives", self.subject_id] + ), } s3_keys = { - rd: list(_get_matching_s3_keys( - bucket=self.study.bucket, - prefix=prefix, - )) for rd, prefix in prefixes.items() + rd: list(_get_matching_s3_keys(bucket=self.study.bucket, prefix=prefix)) + for rd, prefix in prefixes.items() } return s3_keys @@ -691,29 +678,26 @@ def _organize_s3_keys(self): """ # Retrieve and unpack the s3_keys s3_keys = self._list_s3_keys() - dwi_keys = [k for k in s3_keys['raw'] if '/dwi/' in k] - fmap_keys = [k for k in s3_keys['raw'] if '/fmap/' in k] - deriv_keys = s3_keys['deriv'] - all_json_keys = [k for k in s3_keys['raw'] if k.endswith('.json')] + dwi_keys = [k for k in s3_keys["raw"] if "/dwi/" in k] + fmap_keys = [k for k in s3_keys["raw"] if "/fmap/" in k] + deriv_keys = s3_keys["deriv"] + all_json_keys = [k for k in s3_keys["raw"] if k.endswith(".json")] # Get the dwi files, bvec files, and bval files - dwi = [f for f in dwi_keys - if f.endswith('.nii.gz') and 'TRACEW' not in f] - bvec = [f for f in dwi_keys if f.endswith('.bvec')] - bval = [f for f in dwi_keys if f.endswith('.bval')] - epi_nii = [f for f in fmap_keys if f.endswith('epi.nii.gz') - and 'fMRI' not in f] - epi_json = [f for f in fmap_keys if f.endswith('epi.json') - and 'fMRI' not in f] - t1w = [f for f in deriv_keys if f.endswith('/T1w.nii.gz')] - freesurfer = [f for f in deriv_keys - if '/freesurfer/' in f] + dwi = [f for f in dwi_keys if f.endswith(".nii.gz") and "TRACEW" not in f] + bvec = [f for f in dwi_keys if f.endswith(".bvec")] + bval = [f for f in dwi_keys if f.endswith(".bval")] + epi_nii = [f for f in fmap_keys if f.endswith("epi.nii.gz") and "fMRI" not in f] + epi_json = [f for f in fmap_keys if f.endswith("epi.json") and "fMRI" not in f] + t1w = [f for f in deriv_keys if f.endswith("/T1w.nii.gz")] + freesurfer = [f for f in deriv_keys if "/freesurfer/" in f] json_keys = [] for file_list in [dwi, bvec, bval, epi_nii, t1w]: for f in file_list: - potential_keys = _cumulative_paths(_recursive_split_ext(f), - add_ext="json") + potential_keys = _cumulative_paths( + _recursive_split_ext(f), add_ext="json" + ) json_keys += [k for k in potential_keys if k in all_json_keys] # Use truthiness of non-empty lists to verify that all @@ -736,8 +720,9 @@ def _organize_s3_keys(self): self._valid = False self._s3_keys = None - def download(self, directory, include_site=False, - overwrite=False, pbar=True, pbar_idx=0): + def download( + self, directory, include_site=False, overwrite=False, pbar=True, pbar_idx=0 + ): """Download files from S3 Parameters @@ -768,9 +753,11 @@ def download(self, directory, include_site=False, directory = op.join(directory, self.site) files = { - k: [op.abspath(op.join( - directory, p.split('/' + self.site + '/')[-1] - )) for p in v] for k, v in self.s3_keys.items() + k: [ + op.abspath(op.join(directory, p.split("/" + self.site + "/")[-1])) + for p in v + ] + for k, v in self.s3_keys.items() } # Generate list of (key, file) tuples @@ -812,15 +799,16 @@ def download(self, directory, include_site=False, # Now iterate through the list and download each item if pbar: - progress = tqdm(desc=f"Download {self.subject_id}", - position=pbar_idx, - total=len(key_file_pairs) + 1) + progress = tqdm( + desc=f"Download {self.subject_id}", + position=pbar_idx, + total=len(key_file_pairs) + 1, + ) for (key, fname) in key_file_pairs: - _download_from_s3(fname=fname, - bucket=self.study.bucket, - key=key, - overwrite=overwrite) + _download_from_s3( + fname=fname, bucket=self.study.bucket, key=key, overwrite=overwrite + ) if pbar: progress.update() @@ -834,12 +822,15 @@ def download(self, directory, include_site=False, progress.update() progress.close() - def _determine_directions(self, - input_files, - input_type='s3', - metadata_source='json', - json_key='PhaseEncodingDirection', - ap_value='j-', pa_value='j'): + def _determine_directions( + self, + input_files, + input_type="s3", + metadata_source="json", + json_key="PhaseEncodingDirection", + ap_value="j-", + pa_value="j", + ): """Determine direction ['AP', 'PA'] of single subject's EPI nifty files Use either metadata in associated json file or filename @@ -872,42 +863,39 @@ def _determine_directions(self, self.s3_keys except that in the "epi_nii" and "epi_json" keys have been replaced with "epi_ap" and "epi_pa." """ - if metadata_source not in ['filename', 'json']: + if metadata_source not in ["filename", "json"]: raise ValueError('metadata_source must be "filename" or "json".') - if input_type not in ['s3', 'local']: + if input_type not in ["s3", "local"]: raise ValueError('input_type must be "local" or "s3".') - epi_files = input_files['epi_nii'] - json_files = input_files['epi_json'] - if metadata_source == 'filename': - ap_files = [f for f in epi_files if 'dir-AP' in f] - pa_files = [f for f in epi_files if 'dir-PA' in f] + epi_files = input_files["epi_nii"] + json_files = input_files["epi_json"] + if metadata_source == "filename": + ap_files = [f for f in epi_files if "dir-AP" in f] + pa_files = [f for f in epi_files if "dir-PA" in f] else: # Confirm that each nifty file has a corresponding json file. - required_json = set([f.replace('.nii.gz', '.json') for f in epi_files]) + required_json = set([f.replace(".nii.gz", ".json") for f in epi_files]) if set(json_files) != required_json: self._valid = False mod_logger.warning( - f'Subject {self.subject_id} does not have json files ' - f'corresponding to its fmap NIFTI files. Failed to ' - f'find the following expected files: ' - f'{required_json - set(json_files)}. Subject deemed ' - f'invalid.' + f"Subject {self.subject_id} does not have json files " + f"corresponding to its fmap NIFTI files. Failed to " + f"find the following expected files: " + f"{required_json - set(json_files)}. Subject deemed " + f"invalid." ) return input_files def get_json(json_file): - if input_type == 'local': - with open(json_file, 'r') as fp: + if input_type == "local": + with open(json_file, "r") as fp: meta = json.load(fp) else: s3 = get_s3_client() - response = s3.get_object( - Bucket=self.study.bucket, - Key=json_file, - ) - meta = json.loads(response.get('Body').read()) + response = s3.get_object(Bucket=self.study.bucket, Key=json_file) + meta = json.loads(response.get("Body").read()) return meta @@ -918,76 +906,76 @@ def get_json(json_file): direction = metadata.get(json_key) if direction == ap_value: - if 'dir-PA' in jfile: + if "dir-PA" in jfile: mod_logger.warning( - 'The key {key:s}={val:s} does not match the direction ' - 'suggested by the filename {fn:s}'.format( + "The key {key:s}={val:s} does not match the direction " + "suggested by the filename {fn:s}".format( key=json_key, val=direction, fn=jfile ) ) - ap_files.append(jfile.replace('.json', '.nii.gz')) + ap_files.append(jfile.replace(".json", ".nii.gz")) elif direction == pa_value: - if 'dir-AP' in jfile: + if "dir-AP" in jfile: mod_logger.warning( - 'The key {key:s}={val:s} does not match the direction ' - 'suggested by the filename {fn:s}'.format( + "The key {key:s}={val:s} does not match the direction " + "suggested by the filename {fn:s}".format( key=json_key, val=direction, fn=jfile ) ) - pa_files.append(jfile.replace('.json', '.nii.gz')) + pa_files.append(jfile.replace(".json", ".nii.gz")) elif direction is None: mod_logger.warning( - 'The key {key:s} does not exist in file {jfile:s}. ' - 'Falling back on filename to determine directionality.' - '\n\n'.format(key=json_key, jfile=jfile) + "The key {key:s} does not exist in file {jfile:s}. " + "Falling back on filename to determine directionality." + "\n\n".format(key=json_key, jfile=jfile) ) - if 'dir-AP' in jfile: - ap_files.append(jfile.replace('.json', '.nii.gz')) - elif 'dir-PA' in jfile: - pa_files.append(jfile.replace('.json', '.nii.gz')) + if "dir-AP" in jfile: + ap_files.append(jfile.replace(".json", ".nii.gz")) + elif "dir-PA" in jfile: + pa_files.append(jfile.replace(".json", ".nii.gz")) else: self._valid = False mod_logger.warning( - f'Subject {self.subject_id} lacks the expected ' - f'{json_key} key in file {jfile} and the ' - f'directionality could not be inferred from the ' - f'file name. Setting subject validity to False.' + f"Subject {self.subject_id} lacks the expected " + f"{json_key} key in file {jfile} and the " + f"directionality could not be inferred from the " + f"file name. Setting subject validity to False." ) return input_files else: mod_logger.warning( - 'The metadata in file {jfile:s} does not match the dir-PA ' - 'or dir-AP values that you provided. {key:s} = {val:s}. ' - 'Falling back on filename to determine directionality.\n\n' - ''.format(jfile=jfile, key=json_key, val=direction) + "The metadata in file {jfile:s} does not match the dir-PA " + "or dir-AP values that you provided. {key:s} = {val:s}. " + "Falling back on filename to determine directionality.\n\n" + "".format(jfile=jfile, key=json_key, val=direction) ) - if 'dir-AP' in jfile: - ap_files.append(jfile.replace('.json', '.nii.gz')) - elif 'dir-PA' in jfile: - pa_files.append(jfile.replace('.json', '.nii.gz')) + if "dir-AP" in jfile: + ap_files.append(jfile.replace(".json", ".nii.gz")) + elif "dir-PA" in jfile: + pa_files.append(jfile.replace(".json", ".nii.gz")) else: self._valid = False mod_logger.warning( - 'The metadata for key {key:s} in file {jfile:s} does ' - 'not match the dir-PA or dir-AP values that you ' - 'provided. {key:s} = {val:s}. And the directionality ' - 'could not be inferred from the file name.'.format( - key=json_key, - jfile=jfile, - val=direction, - )) + "The metadata for key {key:s} in file {jfile:s} does " + "not match the dir-PA or dir-AP values that you " + "provided. {key:s} = {val:s}. And the directionality " + "could not be inferred from the file name.".format( + key=json_key, jfile=jfile, val=direction + ) + ) return input_files files = copy.deepcopy(input_files) - del files['epi_nii'] - del files['epi_json'] - files['epi_ap'] = ap_files - files['epi_pa'] = pa_files + del files["epi_nii"] + del files["epi_json"] + files["epi_ap"] = ap_files + files["epi_pa"] = pa_files return files - def _separate_sessions(self, input_files, multiples_policy='sessions', - assign_empty_sessions=True): + def _separate_sessions( + self, input_files, multiples_policy="sessions", assign_empty_sessions=True + ): """Separate input file register into different sessions Parameters @@ -1010,22 +998,23 @@ def _separate_sessions(self, input_files, multiples_policy='sessions', dict of dicts Dict of Dicts of file names """ - if multiples_policy not in ['sessions', 'concatenate']: - raise ValueError('`multiples_policy` must be either "sessions" or ' - '"concatenate"') + if multiples_policy not in ["sessions", "concatenate"]: + raise ValueError( + '`multiples_policy` must be either "sessions" or ' '"concatenate"' + ) # Take only the first of the T1W nifty files - if len(input_files['t1w']) > 1: + if len(input_files["t1w"]) > 1: mod_logger.warning( f"Found more than one T1W file for subject {self.subject_id} " f"at site {self.site}. Discarding the others.\n\n" ) - t1w = input_files['t1w'] + t1w = input_files["t1w"] # Take only the first freesurfer directory freesurfer_dirs = { - f.split('/freesurfer/')[0] for f in input_files['freesurfer'] + f.split("/freesurfer/")[0] for f in input_files["freesurfer"] } if len(freesurfer_dirs) > 1: @@ -1036,25 +1025,24 @@ def _separate_sessions(self, input_files, multiples_policy='sessions', ) freesurfer_dir = freesurfer_dirs.pop() - freesurfer = [f for f in input_files['freesurfer'] - if f.startswith(freesurfer_dir)] + freesurfer = [ + f for f in input_files["freesurfer"] if f.startswith(freesurfer_dir) + ] # Organize the files by session ID - def get_sess_id(filename, fallback='null'): + def get_sess_id(filename, fallback="null"): # Retrieve the session ID from a filename - match = re.search('/ses-[0-9a-zA-Z]*/', filename) + match = re.search("/ses-[0-9a-zA-Z]*/", filename) if match is not None: - return match.group().strip('/') + return match.group().strip("/") else: return fallback - ftypes = ['dwi', 'bvec', 'bval', 'epi_ap', 'epi_pa'] + ftypes = ["dwi", "bvec", "bval", "epi_ap", "epi_pa"] - sess_ids = {ft: {get_sess_id(fn) for fn in input_files[ft]} - for ft in ftypes} + sess_ids = {ft: {get_sess_id(fn) for fn in input_files[ft]} for ft in ftypes} - if not all([s == list(sess_ids.values())[0] - for s in sess_ids.values()]): + if not all([s == list(sess_ids.values())[0] for s in sess_ids.values()]): mod_logger.warning( "Session numbers are inconsistent for subject {sub:s} at site " "{site:s}. Ses-IDs: {sess_ids!s}.\n" @@ -1063,8 +1051,9 @@ def get_sess_id(filename, fallback='null'): site=self.site, sess_ids=sess_ids, files={ - k: (v) for k, v in input_files.items() - if k in ['dwi', 'bvec', 'bval', 'epi_ap', 'epi_pa'] + k: (v) + for k, v in input_files.items() + if k in ["dwi", "bvec", "bval", "epi_ap", "epi_pa"] }, ) ) @@ -1077,9 +1066,7 @@ def get_sess_id(filename, fallback='null'): # Collect files by session ID and then file type files_by_session = { sess: { - ft: [ - f for f in input_files[ft] if get_sess_id(f) == sess - ] + ft: [f for f in input_files[ft] if get_sess_id(f) == sess] for ft in ftypes } for sess in sess_ids @@ -1090,8 +1077,11 @@ def get_sess_id(filename, fallback='null'): # Loop over each session ID for session, files in files_by_session.items(): # Confirm that the subject has an equal number of each type of file - n_files = {k: len(v) for k, v in files.items() - if k in ['dwi', 'bvec', 'bval', 'epi_ap', 'epi_pa']} + n_files = { + k: len(v) + for k, v in files.items() + if k in ["dwi", "bvec", "bval", "epi_ap", "epi_pa"] + } if len(set(n_files.values())) != 1: mod_logger.warning( @@ -1102,32 +1092,34 @@ def get_sess_id(filename, fallback='null'): elif len(set(n_files.values())) == 1: # There is only one set of files in this session. # Append to output. - if session == 'null': - output_session = 'ses-01' if assign_empty_sessions else None + if session == "null": + output_session = "ses-01" if assign_empty_sessions else None else: output_session = session output_files[output_session] = dict( - dwi=input_files['dwi'], - bvec=input_files['bvec'], - bval=input_files['bval'], - epi_ap=input_files['epi_ap'], - epi_pa=input_files['epi_pa'], - json=input_files['json'], + dwi=input_files["dwi"], + bvec=input_files["bvec"], + bval=input_files["bval"], + epi_ap=input_files["epi_ap"], + epi_pa=input_files["epi_pa"], + json=input_files["json"], t1w=t1w, freesurfer=freesurfer, ) else: # There are multiple copies of files for this one session ID. - if multiples_policy == 'concatenate': + if multiples_policy == "concatenate": # The multiple copies represent one session and should be # concatenated - raise NotImplementedError("Concatenation of multiples not " - "yet implemented.") + raise NotImplementedError( + "Concatenation of multiples not " "yet implemented." + ) else: # The multiple copies represent multiple sessions and # should be further subdivided into sessions - raise NotImplementedError("Session subdivision not yet " - "implemented.") + raise NotImplementedError( + "Session subdivision not yet " "implemented." + ) return output_files diff --git a/dmriprep/qc.py b/dmriprep/qc.py index b6663f22..be3a91a9 100644 --- a/dmriprep/qc.py +++ b/dmriprep/qc.py @@ -3,7 +3,8 @@ from io import BytesIO import matplotlib -matplotlib.use('agg') + +matplotlib.use("agg") import matplotlib.pyplot as plt import nibabel as nib import numpy as np @@ -18,15 +19,14 @@ def reorient_array(data, aff): data_RAS = nib.orientations.apply_orientation(data, orientation) # In RAS return nib.orientations.apply_orientation( - data_RAS, - nib.orientations.axcodes2ornt("IPL") + data_RAS, nib.orientations.axcodes2ornt("IPL") ) def mplfig(data, outfile=None, as_bytes=False): fig = plt.figure(frameon=False, dpi=data.shape[0]) - fig.set_size_inches(float(data.shape[1])/data.shape[0], 1) - ax = plt.Axes(fig, [0., 0., 1., 1.]) + fig.set_size_inches(float(data.shape[1]) / data.shape[0], 1) + ax = plt.Axes(fig, [0.0, 0.0, 1.0, 1.0]) ax.set_axis_off() fig.add_axes(ax) ax.imshow(data, aspect=1, cmap=plt.cm.Greys_r) # previous aspect="normal" @@ -36,7 +36,7 @@ def mplfig(data, outfile=None, as_bytes=False): return outfile if as_bytes: IObytes = BytesIO() - plt.savefig(IObytes, format='png', dpi=data.shape[0], transparent=True) + plt.savefig(IObytes, format="png", dpi=data.shape[0], transparent=True) IObytes.seek(0) base64_jpgData = base64.b64encode(IObytes.read()) return base64_jpgData.decode("ascii") @@ -44,8 +44,8 @@ def mplfig(data, outfile=None, as_bytes=False): def mplfigcontour(data, outfile=None, as_bytes=False): fig = plt.figure(frameon=False) - fig.set_size_inches(float(data.shape[1])/data.shape[0], 1) - ax = plt.Axes(fig, [0., 0., 1., 1.]) + fig.set_size_inches(float(data.shape[1]) / data.shape[0], 1) + ax = plt.Axes(fig, [0.0, 0.0, 1.0, 1.0]) ax.set_axis_off() fig.add_axes(ax) @@ -59,7 +59,7 @@ def mplfigcontour(data, outfile=None, as_bytes=False): return outfile if as_bytes: IObytes = BytesIO() - plt.savefig(IObytes, format='png', dpi=data.shape[0], transparent=True) + plt.savefig(IObytes, format="png", dpi=data.shape[0], transparent=True) IObytes.seek(0) base64_jpgData = base64.b64encode(IObytes.read()) return base64_jpgData.decode("ascii") @@ -73,31 +73,42 @@ def load_and_reorient(filename): def reshape3D(data, n=256): - return np.pad(data, ( - ( - (n-data.shape[0]) // 2, - ((n-data.shape[0]) + (data.shape[0] % 2 > 0)) // 2 - ), + return np.pad( + data, ( - (n-data.shape[1]) // 2, - ((n-data.shape[1]) + (data.shape[1] % 2 > 0)) // 2 + ( + (n - data.shape[0]) // 2, + ((n - data.shape[0]) + (data.shape[0] % 2 > 0)) // 2, + ), + ( + (n - data.shape[1]) // 2, + ((n - data.shape[1]) + (data.shape[1] % 2 > 0)) // 2, + ), + (0, 0), ), - (0, 0) - ), "constant", constant_values=(0, 0)) + "constant", + constant_values=(0, 0), + ) def reshape4D(data, n=256): - return np.pad(data, ( + return np.pad( + data, ( - (n-data.shape[0]) // 2, - ((n-data.shape[0]) + (data.shape[0] % 2 > 0)) // 2 + ( + (n - data.shape[0]) // 2, + ((n - data.shape[0]) + (data.shape[0] % 2 > 0)) // 2, + ), + ( + (n - data.shape[1]) // 2, + ((n - data.shape[1]) + (data.shape[1] % 2 > 0)) // 2, + ), + (0, 0), + (0, 0), ), - ( - (n-data.shape[1]) // 2, - ((n-data.shape[1]) + (data.shape[1] % 2 > 0)) // 2 - ), - (0, 0), (0, 0) - ), "constant", constant_values=(0, 0)) + "constant", + constant_values=(0, 0), + ) def get_middle_slices(data, slice_direction): @@ -117,7 +128,7 @@ def get_middle_slices(data, slice_direction): def nearest_square(limit): answer = 0 - while (answer+1)**2 < limit: + while (answer + 1) ** 2 < limit: answer += 1 if (answer ** 2) == limit: return answer @@ -128,17 +139,17 @@ def nearest_square(limit): def create_sprite_from_tiles(tile, out_file=None, as_bytes=False): num_slices = tile.shape[-1] N = nearest_square(num_slices) - M = int(np.ceil(num_slices/N)) + M = int(np.ceil(num_slices / N)) # tile is square, so just make a big arr pix = tile.shape[0] if len(tile.shape) == 3: - mosaic = np.zeros((N*tile.shape[0], M*tile.shape[0])) + mosaic = np.zeros((N * tile.shape[0], M * tile.shape[0])) else: - mosaic = np.zeros((N*tile.shape[0], M*tile.shape[0], tile.shape[-2])) + mosaic = np.zeros((N * tile.shape[0], M * tile.shape[0], tile.shape[-2])) mosaic[:] = np.nan - helper = np.arange(N*M).reshape((N, M)) + helper = np.arange(N * M).reshape((N, M)) for t in range(num_slices): x, y = np.nonzero(helper == t) @@ -171,13 +182,13 @@ def createSprite4D(dwi_file): dwi = load_and_reorient(dwi_file)[:, :, :, 1:] # create tiles from center slice on each orientation - for orient in ['sag', 'ax', 'cor']: + for orient in ["sag", "ax", "cor"]: tile = get_middle_slices(dwi, orient) # create sprite images for each results = create_sprite_from_tiles(tile, as_bytes=True) - results['img_type'] = '4dsprite' - results['orientation'] = orient + results["img_type"] = "4dsprite" + results["orientation"] = orient output.append(results) return output @@ -194,45 +205,50 @@ def createB0_ColorFA_Mask_Sprites(b0_file, colorFA_file, mask_file): b0 = reshape3D(b0, N) _, mask = median_otsu(b0) outb0 = create_sprite_from_tiles(b0, as_bytes=True) - outb0['img_type'] = 'brainsprite' + outb0["img_type"] = "brainsprite" # make a colorFA sprite, masked by b0 Q = reshape4D(colorfa, N) Q[np.logical_not(mask)] = np.nan - Q = np.moveaxis(Q, -2, -1) + Q = np.moveaxis(Q, -2, -1) outcolorFA = create_sprite_from_tiles(Q, as_bytes=True) - outcolorFA['img_type'] = 'brainsprite' + outcolorFA["img_type"] = "brainsprite" # make an anat mask contour sprite outmask = create_sprite_from_tiles(reshape3D(anat_mask, N)) img = mplfigcontour(outmask.pop("mosaic"), as_bytes=True) - outmask['img'] = img + outmask["img"] = img return outb0, outcolorFA, outmask -def create_report_json(dwi_corrected_file, eddy_rms, eddy_report, - color_fa_file, anat_mask_file, - outlier_indices, - eddy_qc_file, - outpath=op.abspath('./report.json')): +def create_report_json( + dwi_corrected_file, + eddy_rms, + eddy_report, + color_fa_file, + anat_mask_file, + outlier_indices, + eddy_qc_file, + outpath=op.abspath("./report.json"), +): report = {} - report['dwi_corrected'] = createSprite4D(dwi_corrected_file) + report["dwi_corrected"] = createSprite4D(dwi_corrected_file) - b0, colorFA, mask = createB0_ColorFA_Mask_Sprites(dwi_corrected_file, - color_fa_file, - anat_mask_file) - report['b0'] = b0 - report['colorFA'] = colorFA - report['anat_mask'] = mask - report['outlier_volumes'] = outlier_indices.tolist() + b0, colorFA, mask = createB0_ColorFA_Mask_Sprites( + dwi_corrected_file, color_fa_file, anat_mask_file + ) + report["b0"] = b0 + report["colorFA"] = colorFA + report["anat_mask"] = mask + report["outlier_volumes"] = outlier_indices.tolist() - with open(eddy_report, 'r') as f: - report['eddy_report'] = f.readlines() + with open(eddy_report, "r") as f: + report["eddy_report"] = f.readlines() - report['eddy_params'] = np.genfromtxt(eddy_rms).tolist() + report["eddy_params"] = np.genfromtxt(eddy_rms).tolist() eddy_qc = load_json(eddy_qc_file) - report['eddy_quad'] = eddy_qc + report["eddy_quad"] = eddy_qc save_json(outpath, report) return outpath diff --git a/dmriprep/workflows/base.py b/dmriprep/workflows/base.py index 089c86d0..3de8e7c3 100644 --- a/dmriprep/workflows/base.py +++ b/dmriprep/workflows/base.py @@ -6,8 +6,9 @@ from nipype.pipeline import engine as pe from .dwi import init_dwi_preproc_wf + def init_dmriprep_wf(layout, subject_list, work_dir, output_dir): - dmriprep_wf = pe.Workflow(name='dmriprep_wf') + dmriprep_wf = pe.Workflow(name="dmriprep_wf") dmriprep_wf.base_dir = work_dir for subject_id in subject_list: @@ -15,12 +16,13 @@ def init_dmriprep_wf(layout, subject_list, work_dir, output_dir): single_subject_wf = init_single_subject_wf( layout=layout, subject_id=subject_id, - name='single_subject_' + subject_id + '_wf', + name="single_subject_" + subject_id + "_wf", work_dir=work_dir, - output_dir=output_dir) + output_dir=output_dir, + ) - single_subject_wf.config['execution']['crashdump_dir'] = ( - os.path.join(output_dir, 'dmriprep', 'sub-' + subject_id, 'log') + single_subject_wf.config["execution"]["crashdump_dir"] = os.path.join( + output_dir, "dmriprep", "sub-" + subject_id, "log" ) for node in single_subject_wf._get_all_nodes(): @@ -36,20 +38,21 @@ def init_single_subject_wf(layout, subject_id, name, work_dir, output_dir): subject_data = collect_data(layout, subject_id)[0] - if not subject_data['dwi']: - raise Exception("No dwi images found for participant {}. " - "All workflows require dwi images".format(subject_id)) + if not subject_data["dwi"]: + raise Exception( + "No dwi images found for participant {}. " + "All workflows require dwi images".format(subject_id) + ) subject_wf = pe.Workflow(name=name) - for dwi_file in subject_data['dwi']: - dwi_preproc_wf = init_dwi_preproc_wf(dwi_file=dwi_file, - layout=layout) + for dwi_file in subject_data["dwi"]: + dwi_preproc_wf = init_dwi_preproc_wf(dwi_file=dwi_file, layout=layout) dwi_preproc_wf.base_dir = os.path.join(os.path.abspath(work_dir), subject_id) entities = layout.parse_file_entities(dwi_file) - session_id = entities['session'] + session_id = entities["session"] - inputspec = dwi_preproc_wf.get_node('inputnode') + inputspec = dwi_preproc_wf.get_node("inputnode") inputspec.inputs.subject_id = subject_id inputspec.inputs.dwi_file = dwi_file inputspec.inputs.metadata = layout.get_metadata(dwi_file) diff --git a/dmriprep/workflows/dwi/base.py b/dmriprep/workflows/dwi/base.py index de4fef1c..7d3e37b2 100755 --- a/dmriprep/workflows/dwi/base.py +++ b/dmriprep/workflows/dwi/base.py @@ -43,13 +43,13 @@ def init_dwi_preproc_wf(dwi_file, layout): outputnode = pe.Node( niu.IdentityInterface(fields=["out_file", "out_mask", "out_bvec"]), - name="outputnode" + name="outputnode", ) # name noise and out_file using fname_presuffix denoise = pe.Node( mrtrix3.DWIDenoise(noise="noise.nii.gz", out_file="denoised.nii.gz"), - name="denoise" + name="denoise", ) # name unring using fname_presuffix @@ -159,7 +159,7 @@ def b0_average(in_dwi, in_bval, b0_thresh=10.0, out_file=None): # dilate mask bet_dwi0 = pe.Node(fsl.BET(frac=0.3, mask=True, robust=True), name="bet_dwi_pre") - #mrtrix3.MaskFilter + # mrtrix3.MaskFilter ecc = pe.Node( fsl.Eddy(repol=True, cnr_maps=True, residuals=True, method="jac"), @@ -230,8 +230,8 @@ def get_b0_mask_fn(b0_file): eddy_quad, [ (("out_corrected", get_path), "base_name"), - (("out_corrected", get_qc_path), "output_dir") - ] + (("out_corrected", get_qc_path), "output_dir"), + ], ), (inputnode, eddy_quad, [("bval_file", "bval_file")]), (ecc, eddy_quad, [("out_rotated_bvecs", "bvec_file")]), @@ -240,7 +240,7 @@ def get_b0_mask_fn(b0_file): (acqp, eddy_quad, [("out_file", "param_file")]), (ecc, outputnode, [("out_corrected", "out_file")]), (b0mask_node, outputnode, [("mask_file", "out_mask")]), - (ecc, outputnode, [("out_rotated_bvecs", "out_bvec")]) + (ecc, outputnode, [("out_rotated_bvecs", "out_bvec")]), ] ) From 9586fed57a5009491e8b2469b7ce2f91a64efb6b Mon Sep 17 00:00:00 2001 From: Salim Mansour Date: Mon, 17 Jun 2019 15:25:32 -0400 Subject: [PATCH 012/156] Added Datasink output workflow --- dmriprep/workflows/base.py | 26 ++++++++++++++-- dmriprep/workflows/datasink/__init__.py | 3 ++ dmriprep/workflows/datasink/base.py | 40 +++++++++++++++++++++++++ dmriprep/workflows/dwi/base.py | 4 +-- 4 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 dmriprep/workflows/datasink/__init__.py create mode 100644 dmriprep/workflows/datasink/base.py diff --git a/dmriprep/workflows/base.py b/dmriprep/workflows/base.py index 089c86d0..b307da07 100644 --- a/dmriprep/workflows/base.py +++ b/dmriprep/workflows/base.py @@ -5,6 +5,7 @@ from nipype.pipeline import engine as pe from .dwi import init_dwi_preproc_wf +from .datasink import init_output_wf def init_dmriprep_wf(layout, subject_list, work_dir, output_dir): dmriprep_wf = pe.Workflow(name='dmriprep_wf') @@ -43,11 +44,15 @@ def init_single_subject_wf(layout, subject_id, name, work_dir, output_dir): subject_wf = pe.Workflow(name=name) for dwi_file in subject_data['dwi']: + entities = layout.parse_file_entities(dwi_file) + session_id = entities['session'] dwi_preproc_wf = init_dwi_preproc_wf(dwi_file=dwi_file, layout=layout) + datasink_wf = init_output_wf(subject=subject_id, + session=session_id, + output_folder=output_dir) + dwi_preproc_wf.base_dir = os.path.join(os.path.abspath(work_dir), subject_id) - entities = layout.parse_file_entities(dwi_file) - session_id = entities['session'] inputspec = dwi_preproc_wf.get_node('inputnode') inputspec.inputs.subject_id = subject_id @@ -57,6 +62,21 @@ def init_single_subject_wf(layout, subject_id, name, work_dir, output_dir): inputspec.inputs.bval_file = layout.get_bval(dwi_file) inputspec.inputs.out_dir = os.path.abspath(output_dir) - subject_wf.add_nodes([dwi_preproc_wf]) + ds_inputspec = datasink_wf.get_node('inputnode') + ds_inputspec.inputs.subject = subject_id + ds_inputspec.inputs.session = session_id + ds_inputspec.inputs.output_folder = output_dir + + wf_name = "sub_" + subject_id + "_ses_" + session_id + "_preproc_wf" + full_wf = pe.Workflow(name=wf_name) + + full_wf.connect( + [ + (dwi_preproc_wf, datasink_wf, [('fsl_eddy.out_corrected', 'inputnode.out_file'), + ('fsl_eddy.out_rotated_bvecs', 'inputnode.out_bvec'), + ('getB0Mask.mask_file', 'inputnode.out_mask')]) + ] + ) + subject_wf.add_nodes([full_wf]) return subject_wf diff --git a/dmriprep/workflows/datasink/__init__.py b/dmriprep/workflows/datasink/__init__.py new file mode 100644 index 00000000..6fdf35eb --- /dev/null +++ b/dmriprep/workflows/datasink/__init__.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python + +from .base import init_output_wf diff --git a/dmriprep/workflows/datasink/base.py b/dmriprep/workflows/datasink/base.py new file mode 100644 index 00000000..530b6ba6 --- /dev/null +++ b/dmriprep/workflows/datasink/base.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python + +def init_output_wf(subject, session, output_folder): + from nipype.pipeline import engine as pe + from nipype.interfaces import (freesurfer as fs, fsl, mrtrix3, io as nio, \ + utility as niu) + from nipype import logging + + op_wf = pe.Workflow(name="output_wf") + + inputnode = pe.Node(niu.IdentityInterface(fields=[ + 'subject', + 'session', + 'out_file', + 'out_mask', + 'out_bvec', + 'output_folder']), + name="inputnode") + + def build_path(output_folder, subject, session): + import os.path as op + return op.join(output_folder, "sub-" + subject, "ses-" + session, "dwi") + + concat = pe.Node(niu.Function(input_names=["output_folder", "subject", "session"], output_names=["built_folder"], function=build_path), name="build_path") + + datasink = pe.Node(nio.DataSink(), name="datasink") + + op_wf.connect( + [(inputnode, concat, [('subject', 'subject'), + ('session', 'session'), + ('output_folder', 'output_folder')]), + (concat, datasink, [('built_folder', 'base_directory')]), + (inputnode, datasink, [('out_file', '@result.@dwi'), + ('out_bvec', '@result.@bvec'), + ('out_mask', '@result.@mask')]) + ] + ) + + + return op_wf diff --git a/dmriprep/workflows/dwi/base.py b/dmriprep/workflows/dwi/base.py index de4fef1c..d55f2a8f 100755 --- a/dmriprep/workflows/dwi/base.py +++ b/dmriprep/workflows/dwi/base.py @@ -43,13 +43,13 @@ def init_dwi_preproc_wf(dwi_file, layout): outputnode = pe.Node( niu.IdentityInterface(fields=["out_file", "out_mask", "out_bvec"]), - name="outputnode" + name="outputnode", ) # name noise and out_file using fname_presuffix denoise = pe.Node( mrtrix3.DWIDenoise(noise="noise.nii.gz", out_file="denoised.nii.gz"), - name="denoise" + name="denoise", ) # name unring using fname_presuffix From 347e68db5d088c291521b9e47942d2e02bf0b420 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Mon, 17 Jun 2019 15:45:41 -0400 Subject: [PATCH 013/156] [ENH] use pybids instead of collect_data --- dmriprep/utils.py | 55 -------------------------------------- dmriprep/workflows/base.py | 40 ++++++++++++++++----------- 2 files changed, 25 insertions(+), 70 deletions(-) diff --git a/dmriprep/utils.py b/dmriprep/utils.py index 58799bc8..abda5f3c 100644 --- a/dmriprep/utils.py +++ b/dmriprep/utils.py @@ -170,58 +170,3 @@ def collect_participants( warnings.warn(exc.msg, BIDSWarning) return found_label - - -def collect_data(bids_dir, participant_label, task=None, echo=None, bids_validate=True): - """ - Uses pybids to retrieve the input data for a given participant - >>> bids_root, _ = collect_data(str(datadir / 'ds054'), '100185', - ... bids_validate=False) - >>> bids_root['fmap'] # doctest: +ELLIPSIS - ['.../ds054/sub-100185/fmap/sub-100185_magnitude1.nii.gz', \ -'.../ds054/sub-100185/fmap/sub-100185_magnitude2.nii.gz', \ -'.../ds054/sub-100185/fmap/sub-100185_phasediff.nii.gz'] - >>> bids_root['bold'] # doctest: +ELLIPSIS - ['.../ds054/sub-100185/func/sub-100185_task-machinegame_run-01_bold.nii.gz', \ -'.../ds054/sub-100185/func/sub-100185_task-machinegame_run-02_bold.nii.gz', \ -'.../ds054/sub-100185/func/sub-100185_task-machinegame_run-03_bold.nii.gz', \ -'.../ds054/sub-100185/func/sub-100185_task-machinegame_run-04_bold.nii.gz', \ -'.../ds054/sub-100185/func/sub-100185_task-machinegame_run-05_bold.nii.gz', \ -'.../ds054/sub-100185/func/sub-100185_task-machinegame_run-06_bold.nii.gz'] - >>> bids_root['sbref'] # doctest: +ELLIPSIS - ['.../ds054/sub-100185/func/sub-100185_task-machinegame_run-01_sbref.nii.gz', \ -'.../ds054/sub-100185/func/sub-100185_task-machinegame_run-02_sbref.nii.gz', \ -'.../ds054/sub-100185/func/sub-100185_task-machinegame_run-03_sbref.nii.gz', \ -'.../ds054/sub-100185/func/sub-100185_task-machinegame_run-04_sbref.nii.gz', \ -'.../ds054/sub-100185/func/sub-100185_task-machinegame_run-05_sbref.nii.gz', \ -'.../ds054/sub-100185/func/sub-100185_task-machinegame_run-06_sbref.nii.gz'] - >>> bids_root['t1w'] # doctest: +ELLIPSIS - ['.../ds054/sub-100185/anat/sub-100185_T1w.nii.gz'] - >>> bids_root['t2w'] # doctest: +ELLIPSIS - [] - """ - if isinstance(bids_dir, BIDSLayout): - layout = bids_dir - else: - layout = BIDSLayout(str(bids_dir), validate=bids_validate) - - queries = { - "fmap": {"datatype": "fmap"}, - "t2w": {"datatype": "anat", "suffix": "T2w"}, - "t1w": {"datatype": "anat", "suffix": "T1w"}, - "dwi": {"datatype": "dwi", "suffix": "dwi"}, - } - - subj_data = { - dtype: sorted( - layout.get( - return_type="file", - subject=participant_label, - extensions=["nii", "nii.gz"], - **query - ) - ) - for dtype, query in queries.items() - } - - return subj_data, layout diff --git a/dmriprep/workflows/base.py b/dmriprep/workflows/base.py index df774b4e..b6f84550 100644 --- a/dmriprep/workflows/base.py +++ b/dmriprep/workflows/base.py @@ -35,11 +35,16 @@ def init_dmriprep_wf(layout, subject_list, work_dir, output_dir): def init_single_subject_wf(layout, subject_id, name, work_dir, output_dir): - from ..utils import collect_data - subject_data = collect_data(layout, subject_id)[0] + dwi_files = layout.get( + subject=subject_id, + datatype="dwi", + suffix="dwi", + extensions=[".nii", ".nii.gz"], + return_type="filename", + ) - if not subject_data["dwi"]: + if not dwi_files: raise Exception( "No dwi images found for participant {}. " "All workflows require dwi images".format(subject_id) @@ -47,14 +52,13 @@ def init_single_subject_wf(layout, subject_id, name, work_dir, output_dir): subject_wf = pe.Workflow(name=name) - for dwi_file in subject_data['dwi']: + for dwi_file in dwi_files: entities = layout.parse_file_entities(dwi_file) - session_id = entities['session'] - dwi_preproc_wf = init_dwi_preproc_wf(dwi_file=dwi_file, - layout=layout) - datasink_wf = init_output_wf(subject=subject_id, - session=session_id, - output_folder=output_dir) + session_id = entities["session"] + dwi_preproc_wf = init_dwi_preproc_wf(dwi_file=dwi_file, layout=layout) + datasink_wf = init_output_wf( + subject=subject_id, session=session_id, output_folder=output_dir + ) dwi_preproc_wf.base_dir = os.path.join(os.path.abspath(work_dir), subject_id) entities = layout.parse_file_entities(dwi_file) @@ -68,7 +72,7 @@ def init_single_subject_wf(layout, subject_id, name, work_dir, output_dir): inputspec.inputs.bval_file = layout.get_bval(dwi_file) inputspec.inputs.out_dir = os.path.abspath(output_dir) - ds_inputspec = datasink_wf.get_node('inputnode') + ds_inputspec = datasink_wf.get_node("inputnode") ds_inputspec.inputs.subject = subject_id ds_inputspec.inputs.session = session_id ds_inputspec.inputs.output_folder = output_dir @@ -78,11 +82,17 @@ def init_single_subject_wf(layout, subject_id, name, work_dir, output_dir): full_wf.connect( [ - (dwi_preproc_wf, datasink_wf, [('fsl_eddy.out_corrected', 'inputnode.out_file'), - ('fsl_eddy.out_rotated_bvecs', 'inputnode.out_bvec'), - ('getB0Mask.mask_file', 'inputnode.out_mask')]) + ( + dwi_preproc_wf, + datasink_wf, + [ + ("fsl_eddy.out_corrected", "inputnode.out_file"), + ("fsl_eddy.out_rotated_bvecs", "inputnode.out_bvec"), + ("getB0Mask.mask_file", "inputnode.out_mask"), + ], + ) ] - ) + ) subject_wf.add_nodes([full_wf]) return subject_wf From 992c0aa2df1c696f248ff95accde7fc5686d5503 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Mon, 17 Jun 2019 17:00:20 -0400 Subject: [PATCH 014/156] [REF] remove run module after refactor --- dmriprep/run.py | 855 ------------------------------------------------ 1 file changed, 855 deletions(-) delete mode 100644 dmriprep/run.py diff --git a/dmriprep/run.py b/dmriprep/run.py deleted file mode 100644 index 9ac70d8b..00000000 --- a/dmriprep/run.py +++ /dev/null @@ -1,855 +0,0 @@ -import os -import os.path as op -from shutil import copyfile - - -def run_dmriprep(dwi_file, bvec_file, bval_file, - subjects_dir, working_dir, out_dir): - - """ - Runs dmriprep for acquisitions with just one PE direction. - - """ - from glob import glob - import nibabel as nib - import nipype.interfaces.freesurfer as fs - import nipype.interfaces.fsl as fsl - import nipype.interfaces.io as nio - import nipype.interfaces.utility as niu - import nipype.pipeline.engine as pe - import numpy as np - from nipype.algorithms.rapidart import ArtifactDetect - from nipype.interfaces.dipy import DTI - from nipype.interfaces.fsl.utils import AvScale - from nipype.utils.filemanip import fname_presuffix - from nipype.workflows.dmri.fsl.epi import create_dmri_preprocessing - - wf = create_dmri_preprocessing(name='dmriprep', - use_fieldmap=False, - fieldmap_registration=False) - wf.inputs.inputnode.ref_num = 0 - wf.inputs.inputnode.in_file = dwi_file - wf.inputs.inputnode.in_bvec = bvec_file - - dwi_fname = op.split(dwi_file)[1].split(".nii.gz")[0] - bids_sub_name = dwi_fname.split("_")[0] - assert bids_sub_name.startswith("sub-") - - # inputnode = wf.get_node("inputnode") - outputspec = wf.get_node("outputnode") - - # QC: FLIRT translation and rotation parameters - flirt = wf.get_node("motion_correct.coregistration") - # flirt.inputs.save_mats = True - - get_tensor = pe.Node(DTI(), name="dipy_tensor") - wf.connect(outputspec, "dmri_corrected", get_tensor, "in_file") - # wf.connect(inputspec2,"bvals", get_tensor, "in_bval") - get_tensor.inputs.in_bval = bval_file - wf.connect(outputspec, "bvec_rotated", get_tensor, "in_bvec") - - scale_tensor = pe.Node(name='scale_tensor', interface=fsl.BinaryMaths()) - scale_tensor.inputs.operation = 'mul' - scale_tensor.inputs.operand_value = 1000 - wf.connect(get_tensor, 'out_file', scale_tensor, 'in_file') - - fslroi = pe.Node(fsl.ExtractROI(t_min=0, t_size=1), name="fslroi") - wf.connect(outputspec, "dmri_corrected", fslroi, "in_file") - - bbreg = pe.Node(fs.BBRegister(contrast_type="t2", init="fsl", - out_fsl_file=True, subjects_dir=subjects_dir, - epi_mask=True), name="bbreg") - # wf.connect(inputspec2,"fsid", bbreg,"subject_id") - bbreg.inputs.subject_id = 'freesurfer' # bids_sub_name - wf.connect(fslroi, "roi_file", bbreg, "source_file") - - voltransform = pe.Node(fs.ApplyVolTransform(inverse=True), - iterfield=['source_file', 'reg_file'], - name='transform') - voltransform.inputs.subjects_dir = subjects_dir - - vt2 = voltransform.clone("transform_aparcaseg") - vt2.inputs.interp = "nearest" - - def binarize_aparc(aparc_aseg): - img = nib.load(aparc_aseg) - data, aff = img.get_data(), img.affine - outfile = fname_presuffix( - aparc_aseg, suffix="bin.nii.gz", - newpath=op.abspath("."), use_ext=False - ) - nib.Nifti1Image((data > 0).astype(float), aff).to_filename(outfile) - return outfile - - # wf.connect(inputspec2, "mask_nii", voltransform, "target_file") - create_mask = pe.Node(niu.Function(input_names=["aparc_aseg"], - output_names=["outfile"], - function=binarize_aparc), - name="bin_aparc") - - def get_aparc_aseg(subjects_dir, sub): - return op.join(subjects_dir, sub, "mri", "aparc+aseg.mgz") - - create_mask.inputs.aparc_aseg = get_aparc_aseg(subjects_dir, 'freesurfer') - wf.connect(create_mask, "outfile", voltransform, "target_file") - - wf.connect(fslroi, "roi_file", voltransform, "source_file") - wf.connect(bbreg, "out_reg_file", voltransform, "reg_file") - - vt2.inputs.target_file = get_aparc_aseg(subjects_dir, 'freesurfer') - # wf.connect(inputspec2, "aparc_aseg", vt2, "target_file") - wf.connect(fslroi, "roi_file", vt2, "source_file") - wf.connect(bbreg, "out_reg_file", vt2, "reg_file") - - # AK (2017): THIS NODE MIGHT NOT BE NECESSARY - # AK (2018) doesn't know why she said that above.. - threshold2 = pe.Node(fs.Binarize(min=0.5, out_type='nii.gz', dilate=1), - iterfield=['in_file'], - name='threshold2') - wf.connect(voltransform, "transformed_file", threshold2, "in_file") - - # wf.connect(getmotion, "motion_params", datasink, "dti.@motparams") - - def get_flirt_motion_parameters(flirt_out_mats): - def get_params(A): - """This is a copy of spm's spm_imatrix where - we already know the rotations and translations matrix, - shears and zooms (as outputs from fsl FLIRT/avscale) - Let A = the 4x4 rotation and translation matrix - R = [ c5*c6, c5*s6, s5] - [-s4*s5*c6-c4*s6, -s4*s5*s6+c4*c6, s4*c5] - [-c4*s5*c6+s4*s6, -c4*s5*s6-s4*c6, c4*c5] - """ - def rang(b): - a = min(max(b, -1), 1) - return a - Ry = np.arcsin(A[0, 2]) - # Rx = np.arcsin(A[1, 2] / np.cos(Ry)) - # Rz = np.arccos(A[0, 1] / np.sin(Ry)) - - if (abs(Ry)-np.pi/2)**2 < 1e-9: - Rx = 0 - Rz = np.arctan2(-rang(A[1, 0]), rang(-A[2, 0]/A[0, 2])) - else: - c = np.cos(Ry) - Rx = np.arctan2(rang(A[1, 2]/c), rang(A[2, 2]/c)) - Rz = np.arctan2(rang(A[0, 1]/c), rang(A[0, 0]/c)) - - rotations = [Rx, Ry, Rz] - translations = [A[0, 3], A[1, 3], A[2, 3]] - - return rotations, translations - - motion_params = open(op.abspath('motion_parameters.par'), 'w') - for mat in flirt_out_mats: - res = AvScale(mat_file=mat).run() - A = np.asarray(res.outputs.rotation_translation_matrix) - rotations, translations = get_params(A) - for i in rotations+translations: - motion_params.write('%f ' % i) - motion_params.write('\n') - motion_params.close() - motion_params = op.abspath('motion_parameters.par') - return motion_params - - getmotion = pe.Node( - niu.Function(input_names=["flirt_out_mats"], - output_names=["motion_params"], - function=get_flirt_motion_parameters), - name="get_motion_parameters", - iterfield="flirt_out_mats" - ) - - wf.connect(flirt, "out_matrix_file", getmotion, "flirt_out_mats") - - art = pe.Node(interface=ArtifactDetect(), name="art") - art.inputs.use_differences = [True, True] - art.inputs.save_plot = False - art.inputs.use_norm = True - art.inputs.norm_threshold = 3 - art.inputs.zintensity_threshold = 9 - art.inputs.mask_type = 'spm_global' - art.inputs.parameter_source = 'FSL' - - wf.connect(getmotion, "motion_params", art, "realignment_parameters") - wf.connect(outputspec, "dmri_corrected", art, "realigned_files") - - datasink = pe.Node(nio.DataSink(), name="sinker") - datasink.inputs.base_directory = out_dir - datasink.inputs.substitutions = [ - ("vol0000_flirt_merged.nii.gz", dwi_fname + '.nii.gz'), - ("stats.vol0000_flirt_merged.txt", dwi_fname + ".art.json"), - ("motion_parameters.par", dwi_fname + ".motion.txt"), - ("_rotated.bvec", ".bvec"), - ("aparc+aseg_warped_out", dwi_fname.replace("_dwi", "_aparc+aseg")), - ("art.vol0000_flirt_merged_outliers.txt", dwi_fname + ".outliers.txt"), - ("vol0000_flirt_merged", dwi_fname), - ("_roi_bbreg_freesurfer", "_register"), - ("aparc+asegbin_warped_thresh", dwi_fname.replace("_dwi", "_mask")), - ("derivatives/dmriprep", "derivatives/{}/dmriprep".format(bids_sub_name)) - ] - - wf.connect(art, "statistic_files", datasink, "dmriprep.art.@artstat") - wf.connect(art, "outlier_files", datasink, "dmriprep.art.@artoutlier") - wf.connect(outputspec, "dmri_corrected", datasink, "dmriprep.dwi.@corrected") - wf.connect(outputspec, "bvec_rotated", datasink, "dmriprep.dwi.@rotated") - wf.connect(getmotion, "motion_params", datasink, "dmriprep.art.@motion") - - wf.connect(get_tensor, "out_file", datasink, "dmriprep.dti.@tensor") - wf.connect(get_tensor, "fa_file", datasink, "dmriprep.dti.@fa") - wf.connect(get_tensor, "md_file", datasink, "dmriprep.dti.@md") - wf.connect(get_tensor, "ad_file", datasink, "dmriprep.dti.@ad") - wf.connect(get_tensor, "rd_file", datasink, "dmriprep.dti.@rd") - wf.connect(get_tensor, "out_file", datasink, "dmriprep.dti.@scaled_tensor") - - wf.connect(bbreg, "min_cost_file", datasink, "dmriprep.reg.@mincost") - wf.connect(bbreg, "out_fsl_file", datasink, "dmriprep.reg.@fslfile") - wf.connect(bbreg, "out_reg_file", datasink, "dmriprep.reg.@reg") - wf.connect(threshold2, "binary_file", datasink, "dmriprep.anat.@mask") - # wf.connect(vt2, "transformed_file", datasink, "dwi.@aparc_aseg") - - convert = pe.Node(fs.MRIConvert(out_type="niigz"), name="convert2nii") - wf.connect(vt2, "transformed_file", convert, "in_file") - wf.connect(convert, "out_file", datasink, "dmriprep.anat.@aparc_aseg") - - wf.base_dir = working_dir - wf.run() - - copyfile(bval_file, op.join( - out_dir, bids_sub_name, "dmriprep", "dwi", - op.split(bval_file)[1] - )) - - dmri_corrected = glob(op.join(out_dir, '*/dmriprep/dwi', '*.nii.gz'))[0] - bvec_rotated = glob(op.join(out_dir, '*/dmriprep/dwi', '*.bvec'))[0] - bval_file = glob(op.join(out_dir, '*/dmriprep/dwi', '*.bval'))[0] - art_file = glob(op.join(out_dir, '*/dmriprep/art', '*.art.json'))[0] - motion_file = glob(op.join(out_dir, '*/dmriprep/art', '*.motion.txt'))[0] - outlier_file = glob(op.join(out_dir, '*/dmriprep/art', '*.outliers.txt'))[0] - return dmri_corrected, bvec_rotated, art_file, motion_file, outlier_file - - -def run_dmriprep_pe(subject_id, dwi_file, dwi_file_AP, dwi_file_PA, - bvec_file, bval_file, - subjects_dir, working_dir, out_dir, - eddy_niter=5, slice_outlier_threshold=0.02): - """Run the dmriprep (phase encoded) nipype workflow - - Parameters - ---------- - subject_id : str - Subject identifier - - dwi_file : str - Path to dwi nifti file - - dwi_file_AP : str - Path to EPI nifti file (anterior-posterior) - - dwi_file_PA : str - Path to EPI nifti file (posterior-anterior) - - bvec_file : str - Path to bvec file - - bval_file : str - Path to bval file - - subjects_dir : str - Path to subject's freesurfer directory - - working_dir : str - Path to workflow working directory - - out_dir : str - Path to output directory - - eddy_niter : int, default=5 - Fixed number of eddy iterations. See - https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/eddy/UsersGuide#A--niter - - slice_outlier_threshold: int or float - Number of allowed outlier slices per volume. If this is exceeded the - volume is dropped from analysis. If `slice_outlier_threshold` is an - int, it is treated as number of allowed outlier slices. If - `slice_outlier_threshold` is a float between 0 and 1 (exclusive), it is - treated the fraction of allowed outlier slices. - - Notes - ----- - This assumes that there are scans with phase-encode directions AP/PA for - topup. - - See Also - -------- - dmriprep.run.get_dmriprep_pe_workflow - """ - wf = get_dmriprep_pe_workflow() - wf.base_dir = op.join(op.abspath(working_dir), subject_id) - - inputspec = wf.get_node('inputspec') - inputspec.inputs.subject_id = subject_id - inputspec.inputs.dwi_file = dwi_file - inputspec.inputs.dwi_file_ap = dwi_file_AP - inputspec.inputs.dwi_file_pa = dwi_file_PA - inputspec.inputs.bvec_file = bvec_file - inputspec.inputs.bval_file = bval_file - inputspec.inputs.subjects_dir = subjects_dir - inputspec.inputs.out_dir = op.abspath(out_dir) - inputspec.inputs.eddy_niter = eddy_niter - inputspec.inputs.slice_outlier_threshold = slice_outlier_threshold - - # write the graph (this is saved to the working dir) - wf.write_graph() - wf.config['execution']['remove_unnecessary_outputs'] = False - wf.config['execution']['keep_inputs'] = True - wf.run() - - -def get_dmriprep_pe_workflow(): - """Return the dmriprep (phase encoded) nipype workflow - - Parameters - ---------- - - - Returns - ------- - wf : nipype.pipeline.engine.Workflow - Nipype dmriprep workflow - - Notes - ----- - This assumes that there are scans with phase-encode directions AP/PA for - topup. - """ - import nipype.interfaces.freesurfer as fs - import nipype.interfaces.fsl as fsl - import nipype.interfaces.io as nio - import nipype.interfaces.utility as niu - import nipype.pipeline.engine as pe - from nipype.interfaces.dipy import DTI - from nipype.workflows.dmri.fsl.artifacts import all_fsl_pipeline - - inputspec = pe.Node(niu.IdentityInterface(fields=[ - 'subject_id', - 'dwi_file', - 'dwi_file_ap', - 'dwi_file_pa', - 'bvec_file', - 'bval_file', - 'subjects_dir', - 'out_dir', - 'eddy_niter', - 'slice_outlier_threshold' - ]), name="inputspec") - - # AK: watch out, other datasets might be encoded LR - epi_ap = {'echospacing': 66.5e-3, 'enc_dir': 'y-'} - epi_pa = {'echospacing': 66.5e-3, 'enc_dir': 'y'} - prep = all_fsl_pipeline(epi_params=epi_ap, altepi_params=epi_pa) - - # initialize an overall workflow - wf = pe.Workflow(name="dmriprep") - - wf.connect(inputspec, 'dwi_file', prep, 'inputnode.in_file') - wf.connect(inputspec, 'bvec_file', prep, 'inputnode.in_bvec') - wf.connect(inputspec, 'bval_file', prep, 'inputnode.in_bval') - wf.connect(inputspec, 'eddy_niter', prep, 'fsl_eddy.niter') - - eddy = prep.get_node('fsl_eddy') - eddy.inputs.repol = True - eddy.inputs.cnr_maps = True - eddy.inputs.residuals = True - import multiprocessing - eddy.inputs.num_threads = multiprocessing.cpu_count() - import numba.cuda - try: - if numba.cuda.gpus: - eddy.inputs.use_cuda = True - except: - eddy.inputs.use_cuda = False - - topup = prep.get_node('peb_correction.topup') - topup.inputs.checksize = True - - applytopup = prep.get_node('peb_correction.unwarp') - applytopup.inputs.checksize = True - - eddy.inputs.checksize = True - - eddy_quad = pe.Node(fsl.EddyQuad(verbose=True, checksize=True), name="eddy_quad") - get_path = lambda x: x.split('.nii.gz')[0].split('_fix')[0] - get_qc_path = lambda x: x.split('.nii.gz')[0] + '.qc' - wf.connect(prep, ('fsl_eddy.out_corrected', get_path), eddy_quad, 'base_name') - wf.connect(inputspec, 'bval_file', eddy_quad, 'bval_file') - wf.connect(prep, 'Rotate_Bvec.out_file', eddy_quad, 'bvec_file') - wf.connect(prep, 'peb_correction.topup.out_field', eddy_quad, 'field') - wf.connect(prep, 'gen_index.out_file', eddy_quad, 'idx_file') - wf.connect(prep, 'peb_correction.topup.out_enc_file', eddy_quad, 'param_file') - wf.connect(prep, ('fsl_eddy.out_corrected', get_qc_path), eddy_quad, 'output_dir') - - # need a mask file for eddy_quad. lets get it from the B0. - def get_b0_mask_fn(b0_file): - import nibabel as nib - from nipype.utils.filemanip import fname_presuffix - from dipy.segment.mask import median_otsu - import os - - mask_file = fname_presuffix(b0_file, suffix="_mask", newpath=os.path.abspath('.')) - img = nib.load(b0_file) - data, aff = img.get_data(), img.affine - _, mask = median_otsu(data, 2, 1) - nib.Nifti1Image(mask.astype(float), aff).to_filename(mask_file) - return mask_file - - - def id_outliers_fn(outlier_report, threshold, dwi_file): - """Get list of scans that exceed threshold for number of outliers - - Parameters - ---------- - outlier_report: string - Path to the fsl_eddy outlier report - - threshold: int or float - If threshold is an int, it is treated as number of allowed outlier - slices. If threshold is a float between 0 and 1 (exclusive), it is - treated the fraction of allowed outlier slices before we drop the - whole volume. - - dwi_file: string - Path to nii dwi file to determine total number of slices - - Returns - ------- - drop_scans: numpy.ndarray - List of scan indices to drop - """ - import nibabel as nib - import numpy as np - import os.path as op - import parse - - with open(op.abspath(outlier_report), 'r') as fp: - lines = fp.readlines() - - p = parse.compile( - "Slice {slice:d} in scan {scan:d} is an outlier with " - "mean {mean_sd:f} standard deviations off, and mean " - "squared {mean_sq_sd:f} standard deviations off." - ) - - outliers = [p.parse(l).named for l in lines] - scans = {d['scan'] for d in outliers} - - def num_outliers(scan, outliers): - return len([d for d in outliers if d['scan'] == scan]) - - if 0 < threshold < 1: - img = nib.load(dwi_file) - try: - threshold *= img.header.get_n_slices() - except nib.spatialimages.HeaderDataError: - print('WARNING. We are not sure which dimension has the ' - 'slices in this image. So we are using the 3rd dim.', img.shape) - threshold *= img.shape[2] - - drop_scans = np.array([ - s for s in scans - if num_outliers(s, outliers) > threshold - ]) - - outpath = op.abspath("dropped_scans.txt") - np.savetxt(outpath, drop_scans, fmt="%d") - - return drop_scans, outpath - - id_outliers_node = pe.Node(niu.Function( - input_names=["outlier_report", "threshold", "dwi_file"], - output_names=["drop_scans", "outpath"], - function=id_outliers_fn), - name="id_outliers_node" - ) - - wf.connect(inputspec, 'dwi_file', id_outliers_node, 'dwi_file') - wf.connect(inputspec, 'slice_outlier_threshold', id_outliers_node, 'threshold') - - wf.connect(prep, "fsl_eddy.out_outlier_report", - id_outliers_node, "outlier_report") - - list_merge = pe.Node(niu.Merge(numinputs=2), name="list_merge") - wf.connect(inputspec, 'dwi_file_ap', list_merge, 'in1') - wf.connect(inputspec, 'dwi_file_pa', list_merge, 'in2') - - merge = pe.Node(fsl.Merge(dimension='t'), name="mergeAPPA") - wf.connect(merge, 'merged_file', prep, 'inputnode.alt_file') - wf.connect(list_merge, 'out', merge, 'in_files') - - fslroi = pe.Node(fsl.ExtractROI(t_min=0, t_size=1), name="fslroi") - wf.connect(prep, "outputnode.out_file", fslroi, "in_file") - - b0mask_node = pe.Node(niu.Function(input_names=['b0_file'], - output_names=['mask_file'], - function=get_b0_mask_fn), - name="getB0Mask") - wf.connect(fslroi, 'roi_file', b0mask_node, 'b0_file') - wf.connect(b0mask_node, 'mask_file', eddy_quad, 'mask_file') - - bbreg = pe.Node(fs.BBRegister(contrast_type="t2", init="coreg", - out_fsl_file=True, - # subjects_dir=subjects_dir, - epi_mask=True), - name="bbreg") - bbreg.inputs.subject_id = 'freesurfer' # bids_sub_name - wf.connect(fslroi, "roi_file", bbreg, "source_file") - wf.connect(inputspec, 'subjects_dir', bbreg, 'subjects_dir') - - def drop_outliers_fn(in_file, in_bval, in_bvec, drop_scans): - """Drop outlier volumes from dwi file - - Parameters - ---------- - in_file: string - Path to nii dwi file to drop outlier volumes from - - in_bval: string - Path to bval file to drop outlier volumes from - - in_bvec: string - Path to bvec file to drop outlier volumes from - - drop_scans: numpy.ndarray - List of scan indices to drop - - Returns - ------- - out_file: string - Path to "thinned" output dwi file - - out_bval: string - Path to "thinned" output bval file - - out_bvec: string - Path to "thinned" output bvec file - """ - import nibabel as nib - import numpy as np - import os.path as op - from nipype.utils.filemanip import fname_presuffix - - img = nib.load(op.abspath(in_file)) - img_data = img.get_data() - img_data_thinned = np.delete(img_data, - drop_scans, - axis=3) - if isinstance(img, nib.nifti1.Nifti1Image): - img_thinned = nib.Nifti1Image(img_data_thinned.astype(np.float64), - img.affine, - header=img.header) - elif isinstance(img, nib.nifti2.Nifti2Image): - img_thinned = nib.Nifti2Image(img_data_thinned.astype(np.float64), - img.affine, - header=img.header) - else: - raise TypeError("in_file does not contain Nifti image datatype.") - - out_file = fname_presuffix(in_file, suffix="_thinned", newpath=op.abspath('.')) - nib.save(img_thinned, op.abspath(out_file)) - - bval = np.loadtxt(in_bval) - bval_thinned = np.delete(bval, drop_scans, axis=0) - out_bval = fname_presuffix(in_bval, suffix="_thinned", newpath=op.abspath('.')) - np.savetxt(out_bval, bval_thinned) - - bvec = np.loadtxt(in_bvec) - bvec_thinned = np.delete(bvec, drop_scans, axis=1) - out_bvec = fname_presuffix(in_bvec, suffix="_thinned", newpath=op.abspath('.')) - np.savetxt(out_bvec, bvec_thinned) - - return out_file, out_bval, out_bvec - - drop_outliers_node = pe.Node(niu.Function( - input_names=["in_file", "in_bval", "in_bvec", "drop_scans"], - output_names=["out_file", "out_bval", "out_bvec"], - function=drop_outliers_fn), - name="drop_outliers_node" - ) - - # Align the output of drop_outliers_node & also the eddy corrected version to the anatomical space - # without resampling. and then for aparc+aseg & the mask, resample to the larger voxel size of the B0 image from - # fslroi. Also we need to apply the transformation to both bvecs (dropped & eddied) and I think we can just load - # the affine from bbreg (sio.loadmat) and np.dot(coord, aff) for each coord in bvec - - def get_orig(subjects_dir, sub='freesurfer'): - import os.path as op - return op.join(subjects_dir, sub, "mri", "orig.mgz") - - def get_aparc_aseg(subjects_dir, sub='freesurfer'): - import os.path as op - return op.join(subjects_dir, sub, "mri", "aparc+aseg.mgz") - - # transform the dropped volume version to anat space w/ out resampling - voltransform = pe.Node(fs.ApplyVolTransform(no_resample=True), - iterfield=['source_file', 'reg_file'], - name='transform') - - wf.connect(inputspec, 'subjects_dir', voltransform, 'subjects_dir') - wf.connect(inputspec, ('subjects_dir', get_aparc_aseg), voltransform, 'target_file') - wf.connect(prep, "outputnode.out_file", voltransform, "source_file") - wf.connect(bbreg, "out_reg_file", voltransform, "reg_file") - - def apply_transform_to_bvecs_fn(bvec_file, reg_mat_file): - import numpy as np - import nipype.utils.filemanip as fm - import os - - aff = np.loadtxt(reg_mat_file) - bvecs = np.loadtxt(bvec_file) - bvec_trans = [] - for bvec in bvecs.T: - coord = np.hstack((bvec, [1])) - coord_trans = np.dot(coord, aff)[:-1] - bvec_trans.append(coord_trans) - out_bvec = fm.fname_presuffix(bvec_file, suffix="anat_space", newpath=os.path.abspath('.')) - np.savetxt(out_bvec, np.asarray(bvec_trans).T) - return out_bvec - - apply_transform_to_bvecs_node = pe.Node(niu.Function(input_names=['bvec_file', 'reg_mat_file'], - output_names=['out_bvec'], - function=apply_transform_to_bvecs_fn), - name="applyTransformToBvecs") - wf.connect(bbreg, 'out_fsl_file', apply_transform_to_bvecs_node, 'reg_mat_file') - wf.connect(prep, 'outputnode.out_bvec', apply_transform_to_bvecs_node, 'bvec_file') - - # ok cool, now lets do the thresholding. - - wf.connect(id_outliers_node, "drop_scans", drop_outliers_node, "drop_scans") - wf.connect(voltransform, "transformed_file", drop_outliers_node, "in_file") - wf.connect(inputspec, 'bval_file', drop_outliers_node, 'in_bval') - wf.connect(apply_transform_to_bvecs_node, "out_bvec", drop_outliers_node, "in_bvec") - - # lets compute the tensor on both the dropped volume scan - # and also the original, eddy corrected one. - get_tensor = pe.Node(DTI(), name="dipy_tensor") - wf.connect(drop_outliers_node, "out_file", get_tensor, "in_file") - wf.connect(drop_outliers_node, "out_bval", get_tensor, "in_bval") - wf.connect(drop_outliers_node, "out_bvec", get_tensor, "in_bvec") - - get_tensor_eddy = get_tensor.clone('dipy_tensor_eddy') - wf.connect(voltransform, 'transformed_file', get_tensor_eddy, "in_file") - wf.connect(apply_transform_to_bvecs_node, 'out_bvec', get_tensor_eddy, "in_bvec") - wf.connect(inputspec, 'bval_file', get_tensor_eddy, 'in_bval') - - # AK: What is this, some vestigal node from a previous workflow? - # I'm not sure why the tensor gets scaled. but i guess lets scale it for - # both the dropped & eddy corrected versions. - scale_tensor = pe.Node(name='scale_tensor', interface=fsl.BinaryMaths()) - scale_tensor.inputs.operation = 'mul' - scale_tensor.inputs.operand_value = 1000 - wf.connect(get_tensor, 'out_file', scale_tensor, 'in_file') - - scale_tensor_eddy = scale_tensor.clone('scale_tensor_eddy') - wf.connect(get_tensor_eddy, 'out_file', scale_tensor_eddy, 'in_file') - - # OK now that anatomical stuff (segmentation & mask) - # We'll need: - # 1. the B0 image in anat space (fslroi the 'transformed file' of voltransform - # 2. the aparc aseg resampled-like the B0 image (mri_convert) - # 3. the resample aparc_aseg binarized to be the mask image. - - def binarize_aparc(aparc_aseg): - import nibabel as nib - from nipype.utils.filemanip import fname_presuffix - import os.path as op - - img = nib.load(aparc_aseg) - data, aff = img.get_data(), img.affine - outfile = fname_presuffix( - aparc_aseg, suffix="bin.nii.gz", - newpath=op.abspath("."), use_ext=False - ) - nib.Nifti1Image((data > 0).astype(float), aff).to_filename(outfile) - return outfile - - create_mask = pe.Node(niu.Function(input_names=["aparc_aseg"], - output_names=["outfile"], - function=binarize_aparc), - name="bin_aparc") - - get_b0_anat = fslroi.clone('get_b0_anat') - wf.connect(voltransform, 'transformed_file', get_b0_anat, 'in_file') - - # reslice the anat-space aparc+aseg to the DWI resolution - reslice_to_dwi = pe.Node(fs.MRIConvert(resample_type="nearest"), - name="reslice_to_dwi") - wf.connect(get_b0_anat, 'roi_file', reslice_to_dwi, 'reslice_like') - wf.connect(inputspec, ('subjects_dir', get_aparc_aseg), reslice_to_dwi, 'in_file') - - # also reslice the orig i suppose - reslice_orig_to_dwi = reslice_to_dwi.clone('resliceT1wToDwi') - wf.connect(inputspec, ('subjects_dir', get_orig), reslice_orig_to_dwi, 'in_file') - # reslice_orig_to_dwi.inputs.in_file = get_orig(subjects_dir, 'freesurfer') - reslice_orig_to_dwi.inputs.out_type = 'niigz' - wf.connect(get_b0_anat, 'roi_file', reslice_orig_to_dwi, 'reslice_like') - - # we assume the freesurfer is the output of BIDS - # so the freesurfer output is in /path/to/derivatives/sub-whatever/freesurfer - # which means the subject_dir is /path/to/derivatives/sub-whatever - # reslice_to_dwi.inputs.in_file = get_aparc_aseg(subjects_dir, 'freesurfer') - - # now we have a nice aparc+aseg still in anat space but resliced like the dwi file - # lets create a mask file from it. - - wf.connect(reslice_to_dwi, 'out_file', create_mask, 'aparc_aseg') - - # save all the things - datasink = pe.Node(nio.DataSink(), name="sinker") - wf.connect(inputspec, 'out_dir', datasink, 'base_directory') - wf.connect(inputspec, 'subject_id', datasink, 'container') - - wf.connect(drop_outliers_node, "out_file", datasink, "dmriprep.dwi.@thinned") - wf.connect(drop_outliers_node, "out_bval", datasink, "dmriprep.dwi.@bval_thinned") - wf.connect(drop_outliers_node, "out_bvec", datasink, "dmriprep.dwi.@bvec_thinned") - - # eddy corrected files - wf.connect(prep, "outputnode.out_file", datasink, "dmriprep.dwi_eddy.@corrected") - wf.connect(prep, "outputnode.out_bvec", datasink, "dmriprep.dwi_eddy.@rotated") - wf.connect(inputspec, 'bval_file', datasink, 'dmriprep.dwi_eddy.@bval') - - # all the eddy stuff except the corrected files - wf.connect(prep, "fsl_eddy.out_movement_rms", - datasink, "dmriprep.qc.@eddyparamsrms") - wf.connect(prep, "fsl_eddy.out_outlier_report", - datasink, "dmriprep.qc.@eddyparamsreport") - wf.connect(prep, "fsl_eddy.out_restricted_movement_rms", - datasink, "dmriprep.qc.@eddyparamsrestrictrms") - wf.connect(prep, "fsl_eddy.out_shell_alignment_parameters", - datasink, "dmriprep.qc.@eddyparamsshellalign") - wf.connect(prep, "fsl_eddy.out_parameter", - datasink, "dmriprep.qc.@eddyparams") - wf.connect(prep, "fsl_eddy.out_cnr_maps", - datasink, "dmriprep.qc.@eddycndr") - wf.connect(prep, "fsl_eddy.out_residuals", - datasink, "dmriprep.qc.@eddyresid") - - # the file that told us which volumes to drop - wf.connect(id_outliers_node, "outpath", datasink, "dmriprep.qc.@droppedscans") - - # the tensors of the dropped volumes dwi - wf.connect(get_tensor, "out_file", datasink, "dmriprep.dti.@tensor") - wf.connect(get_tensor, "fa_file", datasink, "dmriprep.dti.@fa") - wf.connect(get_tensor, "md_file", datasink, "dmriprep.dti.@md") - wf.connect(get_tensor, "ad_file", datasink, "dmriprep.dti.@ad") - wf.connect(get_tensor, "rd_file", datasink, "dmriprep.dti.@rd") - wf.connect(get_tensor, "color_fa_file", datasink, "dmriprep.dti.@colorfa") - wf.connect(scale_tensor, "out_file", datasink, "dmriprep.dti.@scaled_tensor") - - # the tensors of the eddied volumes dwi - wf.connect(get_tensor_eddy, "out_file", datasink, "dmriprep.dti_eddy.@tensor") - wf.connect(get_tensor_eddy, "fa_file", datasink, "dmriprep.dti_eddy.@fa") - wf.connect(get_tensor_eddy, "md_file", datasink, "dmriprep.dti_eddy.@md") - wf.connect(get_tensor_eddy, "ad_file", datasink, "dmriprep.dti_eddy.@ad") - wf.connect(get_tensor_eddy, "rd_file", datasink, "dmriprep.dti_eddy.@rd") - wf.connect(get_tensor_eddy, "color_fa_file", datasink, "dmriprep.dti_eddy.@colorfa") - wf.connect(scale_tensor_eddy, "out_file", datasink, "dmriprep.dti_eddy.@scaled_tensor") - - # all the eddy_quad stuff - wf.connect(eddy_quad, 'qc_json', datasink, "dmriprep.qc.@eddyquad_json") - wf.connect(eddy_quad, 'qc_pdf', datasink, "dmriprep.qc.@eddyquad_pdf") - wf.connect(eddy_quad, 'avg_b_png', datasink, "dmriprep.qc.@eddyquad_bpng") - wf.connect(eddy_quad, 'avg_b0_pe_png', - datasink, "dmriprep.qc.@eddyquad_b0png") - wf.connect(eddy_quad, 'cnr_png', datasink, "dmriprep.qc.@eddyquad_cnr") - wf.connect(eddy_quad, 'vdm_png', datasink, "dmriprep.qc.@eddyquad_vdm") - wf.connect(eddy_quad, 'residuals', datasink, 'dmriprep.qc.@eddyquad_resid') - - # anatomical registration stuff - wf.connect(bbreg, "min_cost_file", datasink, "dmriprep.reg.@mincost") - wf.connect(bbreg, "out_fsl_file", datasink, "dmriprep.reg.@fslfile") - wf.connect(bbreg, "out_reg_file", datasink, "dmriprep.reg.@reg") - - # anatomical files resliced - wf.connect(reslice_to_dwi, 'out_file', datasink, 'dmriprep.anat.@segmentation') - wf.connect(create_mask, 'outfile', datasink, 'dmriprep.anat.@mask') - wf.connect(reslice_orig_to_dwi, 'out_file', datasink, 'dmriprep.anat.@T1w') - - def report_fn(dwi_corrected_file, eddy_rms, eddy_report, - color_fa_file, anat_mask_file, outlier_indices, - eddy_qc_file): - from dmriprep.qc import create_report_json - - report = create_report_json(dwi_corrected_file, eddy_rms, eddy_report, - color_fa_file, anat_mask_file, outlier_indices, - eddy_qc_file) - return report - - report_node = pe.Node(niu.Function( - input_names=['dwi_corrected_file', 'eddy_rms', - 'eddy_report', 'color_fa_file', - 'anat_mask_file', 'outlier_indices', 'eddy_qc_file'], - output_names=['report'], - function=report_fn - ), name="reportJSON") - - # for the report, lets show the eddy corrected (full volume) image - wf.connect(voltransform, "transformed_file", report_node, 'dwi_corrected_file') - wf.connect(eddy_quad, 'qc_json', report_node, 'eddy_qc_file') - - # add the rms movement output from eddy - wf.connect(prep, "fsl_eddy.out_movement_rms", report_node, 'eddy_rms') - wf.connect(prep, "fsl_eddy.out_outlier_report", report_node, 'eddy_report') - wf.connect(id_outliers_node, 'drop_scans', report_node, 'outlier_indices') - - # the mask file to check our registration, and the colorFA file go in the report - wf.connect(create_mask, "outfile", report_node, 'anat_mask_file') - wf.connect(get_tensor, "color_fa_file", report_node, 'color_fa_file') - - # save that report! - wf.connect(report_node, 'report', datasink, 'dmriprep.report.@report') - - # this part is done last, to get the filenames *just right* - # its super annoying. - def name_files_nicely(dwi_file, subject_id): - import os.path as op - - dwi_fname = op.split(dwi_file)[1].split(".nii.gz")[0] - substitutions = [ - ("vol0000_flirt_merged.nii.gz", dwi_fname + '.nii.gz'), - ("stats.vol0000_flirt_merged.txt", dwi_fname + ".art.json"), - ("motion_parameters.par", dwi_fname + ".motion.txt"), - ("_rotated.bvec", ".bvec"), - ("art.vol0000_flirt_merged_outliers.txt", dwi_fname + ".outliers.txt"), - ("vol0000_flirt_merged", dwi_fname), - ("_roi_bbreg_freesurfer", "_register"), - ("dwi/eddy_corrected", "dwi/%s" % dwi_fname), - ("dti/eddy_corrected", "dti/%s" % dwi_fname.replace("_dwi", "")), - ("reg/eddy_corrected", "reg/%s" % dwi_fname.replace("_dwi", "")), - ("aparc+aseg_outbin", dwi_fname.replace("_dwi", "_mask")), - ("aparc+aseg_out", dwi_fname.replace("_dwi", "_aparc+aseg")), - ("art.eddy_corrected_outliers", dwi_fname.replace("dwi", "outliers")), - ("color_fa", "colorfa"), - ("orig_out", dwi_fname.replace("_dwi", "_T1w")), - ("stats.eddy_corrected", dwi_fname.replace("dwi", "artStats")), - ("eddy_corrected.eddy_parameters", dwi_fname + ".eddy_parameters"), - ("qc/eddy_corrected", "qc/" + dwi_fname), - ("derivatives/dmriprep", "derivatives/{}/dmriprep".format(subject_id)), - ("_rotatedanat_space_thinned", ""), - ("_thinned", ""), - ("eddy_corrected", dwi_fname), - ("_warped", ""), - ("_maths", "_scaled"), - ("dropped_scans", dwi_fname.replace("_dwi", "_dwi_dropped_scans")), - ("report.json", dwi_fname.replace("_dwi", "_dwi_report.json")) - ] - return substitutions - - node_name_files_nicely = pe.Node(niu.Function(input_names=['dwi_file', 'subject_id'], - output_names=['substitutions'], - function=name_files_nicely), - name="name_files_nicely") - wf.connect(inputspec, 'dwi_file', node_name_files_nicely, 'dwi_file') - wf.connect(inputspec, 'subject_id', node_name_files_nicely, 'subject_id') - wf.connect(node_name_files_nicely, 'substitutions', datasink, 'substitutions') - - return wf From d753b3b9160dfdfacd81a7f7528d5f2f611b4215 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Mon, 17 Jun 2019 17:01:40 -0400 Subject: [PATCH 015/156] [ENH] Salim's fieldmap workflow --- dmriprep/workflows/fieldmap/__init__.py | 48 +++++++++++++++++++++ dmriprep/workflows/fieldmap/fmap.py | 55 +++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 dmriprep/workflows/fieldmap/__init__.py create mode 100644 dmriprep/workflows/fieldmap/fmap.py diff --git a/dmriprep/workflows/fieldmap/__init__.py b/dmriprep/workflows/fieldmap/__init__.py new file mode 100644 index 00000000..a225866a --- /dev/null +++ b/dmriprep/workflows/fieldmap/__init__.py @@ -0,0 +1,48 @@ +#!/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: +""" +.. _sdc_estimation : +Fieldmap estimation and unwarping workflows +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. automodule:: fmriprep.workflows.fieldmap.base + :members: + :undoc-members: + :show-inheritance: +.. automodule:: fmriprep.workflows.fieldmap.fmap + :members: + :undoc-members: + :show-inheritance: +.. automodule:: fmriprep.workflows.fieldmap.phdiff + :members: + :undoc-members: + :show-inheritance: +.. automodule:: fmriprep.workflows.fieldmap.pepolar + :members: + :undoc-members: + :show-inheritance: +.. automodule:: fmriprep.workflows.fieldmap.syn + :members: + :undoc-members: + :show-inheritance: +.. automodule:: fmriprep.workflows.fieldmap.unwarp + :members: + :undoc-members: + :show-inheritance: +""" + +from .base import init_fmap_wf + +# from .base import init_sdc_wf +# from .unwarp import init_sdc_unwarp_wf, init_fmap_unwarp_report_wf +# from .pepolar import init_pepolar_unwarp_wf +# from .syn import init_syn_sdc_wf +# +# __all__ = [ +# "init_sdc_wf", +# "init_sdc_unwarp_wf", +# "init_fmap_unwarp_report_wf", +# "init_pepolar_unwarp_wf", +# "init_syn_sdc_wf", +# ] diff --git a/dmriprep/workflows/fieldmap/fmap.py b/dmriprep/workflows/fieldmap/fmap.py new file mode 100644 index 00000000..cd4995d8 --- /dev/null +++ b/dmriprep/workflows/fieldmap/fmap.py @@ -0,0 +1,55 @@ +# -*- 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: +""" +.. _sdc_direct_b0 : +Direct B0 mapping sequences +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +When the fieldmap is directly measured with a prescribed sequence (such as +:abbr:`SE (spiral echo)`), we only need to calculate the corresponding B-Spline +coefficients to adapt the fieldmap to the TOPUP tool. +This procedure is described with more detail `here `__. +This corresponds to the section 8.9.3 --fieldmap image (and one magnitude image)-- +of the BIDS specification. +""" +# from sdcflows.workflows.fmap import init_fmap_wf +# +# __all__ = ["init_fmap_wf"] + + +def init_fmap_wf(): + from nipype.pipeline import engine as pe + from nipype.interfaces import fsl, utility as niu + from nipype import logging + + fmap_wf = pe.Workflow(name="fmap_prep_wf") + + inputnode = pe.Node( + niu.IdentityInterface(fields=["fieldmap", "magnitude", "b0_stripped"]), + name="inputnode", + ) + + outputnode = pe.Node(niu.IdentityInterface(fields=["out_fmap"]), name="outputnode") + + rad_to_hz = pe.Node( + fsl.BinaryMaths(operation="div", operand_value=6.28), name="radToHz" + ) + + mag_flirt = pe.Node(fsl.FLIRT(), name="magFlirt") + + fmap_flirt = pe.Node(fsl.FLIRT(apply_xfm=True), name="fmapFlirt") + + fmap_wf.connect( + [ + (inputnode, rad_to_hz, [("fieldmap", "in_file")]), + (inputnode, mag_flirt, [("magnitude", "in_file")]), + (inputnode, mag_flirt, [("b0_stripped", "reference")]), + (rad_to_hz, fmap_flirt, [("out_file", "in_file")]), + (inputnode, fmap_flirt, [("b0_stripped", "reference")]), + (mag_flirt, fmap_flirt, [("out_matrix_file", "in_matrix_file")]), + (fmap_flirt, outputnode, [("out_file", "out_fmap")]), + ] + ) + + return fmap_wf From d4b0aeaa6b0614a550e5e53be46c74508a4879eb Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Mon, 17 Jun 2019 17:02:25 -0400 Subject: [PATCH 016/156] add fmap to main workflow --- dmriprep/workflows/dwi/base.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/dmriprep/workflows/dwi/base.py b/dmriprep/workflows/dwi/base.py index 7d3e37b2..1e579372 100755 --- a/dmriprep/workflows/dwi/base.py +++ b/dmriprep/workflows/dwi/base.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +from .fieldmap import init_fmap_wf def init_dwi_preproc_wf(dwi_file, layout): @@ -23,6 +24,15 @@ def init_dwi_preproc_wf(dwi_file, layout): fmap_key = fmap["suffix"] fmap["metadata"] = layout.get_metadata(fmap[fmap_key]) + if not fmaps: + raise Exception( + "No fieldmap images found for participant {}. " + "All workflows require fieldmap images".format(subject_id) + ) + + if fmaps[0]["suffix"] == "fieldmap": + fmap_wf = init_fmap_wf() + dwi_wf = pe.Workflow(name="dwi_preproc_wf") inputnode = pe.Node( @@ -241,6 +251,17 @@ def get_b0_mask_fn(b0_file): (ecc, outputnode, [("out_corrected", "out_file")]), (b0mask_node, outputnode, [("mask_file", "out_mask")]), (ecc, outputnode, [("out_rotated_bvecs", "out_bvec")]), + ( + inputnode, + fmap_wf, + [ + ("fieldmap", "inputnode.fieldmap"), + ("magnitude", "inputnode.magnitude"), + ], + ), + (bet_dwi0, fmap_wf, [("out_file", "inputnode.b0_stripped")]), + (fmap_wf, ecc, [(("outputnode.out_fmap", get_path), "field")]), + (fmap_wf, eddy_quad, [(("outputnode.out_fmap", get_path), "field")]), ] ) From d2c41367d0eeeeb4838eb159c51e5f677d6ef23d Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Mon, 17 Jun 2019 17:09:37 -0400 Subject: [PATCH 017/156] [FIX] remove run module from cli --- dmriprep/cli.py | 1 - 1 file changed, 1 deletion(-) diff --git a/dmriprep/cli.py b/dmriprep/cli.py index 2681c922..6542817b 100644 --- a/dmriprep/cli.py +++ b/dmriprep/cli.py @@ -8,7 +8,6 @@ import click -from . import run from . import utils from .data import get_dataset from .workflows.base import init_dmriprep_wf From 964e7a7cd64ce073a71ca78e5e4f68fa300f77d1 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Mon, 17 Jun 2019 17:32:37 -0400 Subject: [PATCH 018/156] [REF] move datasink to dwi workflow and remove run module --- dmriprep/__init__.py | 1 - dmriprep/workflows/base.py | 3 +-- dmriprep/workflows/datasink/__init__.py | 3 --- dmriprep/workflows/dwi/__init__.py | 2 +- dmriprep/workflows/{datasink/base.py => dwi/datasink.py} | 0 tests/test_dmriprep.py | 3 +-- 6 files changed, 3 insertions(+), 9 deletions(-) delete mode 100644 dmriprep/workflows/datasink/__init__.py rename dmriprep/workflows/{datasink/base.py => dwi/datasink.py} (100%) diff --git a/dmriprep/__init__.py b/dmriprep/__init__.py index e0d2b3df..d69154a1 100644 --- a/dmriprep/__init__.py +++ b/dmriprep/__init__.py @@ -18,7 +18,6 @@ from . import data from . import qc -from . import run module_logger = logging.getLogger(__name__) diff --git a/dmriprep/workflows/base.py b/dmriprep/workflows/base.py index b6f84550..4ee7508f 100644 --- a/dmriprep/workflows/base.py +++ b/dmriprep/workflows/base.py @@ -4,8 +4,7 @@ from copy import deepcopy from nipype.pipeline import engine as pe -from .dwi import init_dwi_preproc_wf -from .datasink import init_output_wf +from .dwi import init_dwi_preproc_wf, init_output_wf def init_dmriprep_wf(layout, subject_list, work_dir, output_dir): diff --git a/dmriprep/workflows/datasink/__init__.py b/dmriprep/workflows/datasink/__init__.py deleted file mode 100644 index 6fdf35eb..00000000 --- a/dmriprep/workflows/datasink/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env python - -from .base import init_output_wf diff --git a/dmriprep/workflows/dwi/__init__.py b/dmriprep/workflows/dwi/__init__.py index 4133e319..6fb392f3 100644 --- a/dmriprep/workflows/dwi/__init__.py +++ b/dmriprep/workflows/dwi/__init__.py @@ -1,3 +1,3 @@ #!/usr/bin/env python -from .base import init_dwi_preproc_wf +from .base import init_dwi_preproc_wf, init_output_wf diff --git a/dmriprep/workflows/datasink/base.py b/dmriprep/workflows/dwi/datasink.py similarity index 100% rename from dmriprep/workflows/datasink/base.py rename to dmriprep/workflows/dwi/datasink.py diff --git a/tests/test_dmriprep.py b/tests/test_dmriprep.py index a1553c69..1786035f 100644 --- a/tests/test_dmriprep.py +++ b/tests/test_dmriprep.py @@ -7,7 +7,6 @@ from click.testing import CliRunner -from dmriprep import dmriprep from dmriprep import cli @@ -32,7 +31,7 @@ def test_command_line_interface(): runner = CliRunner() result = runner.invoke(cli.main) assert result.exit_code == 0 - assert 'dmriprep.cli.main' in result.output + assert 'cli.main' in result.output help_result = runner.invoke(cli.main, ['--help']) assert help_result.exit_code == 0 assert '--help Show this message and exit.' in help_result.output From 1ea9e36c8eb66dac435cb6d938255e0d0680a819 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Mon, 17 Jun 2019 17:41:56 -0400 Subject: [PATCH 019/156] [FIX] fix bad imports --- dmriprep/workflows/dwi/__init__.py | 3 +- dmriprep/workflows/dwi/base.py | 2 +- dmriprep/workflows/dwi/report.py | 448 ++++++++++++++++++++++++ dmriprep/workflows/fieldmap/__init__.py | 2 +- 4 files changed, 452 insertions(+), 3 deletions(-) create mode 100644 dmriprep/workflows/dwi/report.py diff --git a/dmriprep/workflows/dwi/__init__.py b/dmriprep/workflows/dwi/__init__.py index 6fb392f3..c8dbb344 100644 --- a/dmriprep/workflows/dwi/__init__.py +++ b/dmriprep/workflows/dwi/__init__.py @@ -1,3 +1,4 @@ #!/usr/bin/env python -from .base import init_dwi_preproc_wf, init_output_wf +from .base import init_dwi_preproc_wf +from .datasink import init_output_wf diff --git a/dmriprep/workflows/dwi/base.py b/dmriprep/workflows/dwi/base.py index 1e579372..b9481fb0 100755 --- a/dmriprep/workflows/dwi/base.py +++ b/dmriprep/workflows/dwi/base.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -from .fieldmap import init_fmap_wf +from ..fieldmap import init_fmap_wf def init_dwi_preproc_wf(dwi_file, layout): diff --git a/dmriprep/workflows/dwi/report.py b/dmriprep/workflows/dwi/report.py new file mode 100644 index 00000000..29579134 --- /dev/null +++ b/dmriprep/workflows/dwi/report.py @@ -0,0 +1,448 @@ +#!/usr/bin/env python + + +def init_dwi_report_wf(): + + import nipype.interfaces.freesurfer as fs + import nipype.interfaces.fsl as fsl + import nipype.interfaces.io as nio + import nipype.interfaces.dipy as dipy + import nipype.interfaces.utility as niu + import nipype.pipeline.engine as pe + + inputspec = pe.Node(niu.IdentityInterface(fields=[ + 'subject_id', + 'dwi_file', + 'dwi_file_ap', + 'dwi_file_pa', + 'bvec_file', + 'bval_file', + 'subjects_dir', + 'out_dir', + 'eddy_niter', + 'slice_outlier_threshold' + ]), name="inputspec") + + def id_outliers_fn(outlier_report, threshold, dwi_file): + """Get list of scans that exceed threshold for number of outliers + Parameters + ---------- + outlier_report: string + Path to the fsl_eddy outlier report + threshold: int or float + If threshold is an int, it is treated as number of allowed outlier + slices. If threshold is a float between 0 and 1 (exclusive), it is + treated the fraction of allowed outlier slices before we drop the + whole volume. + dwi_file: string + Path to nii dwi file to determine total number of slices + Returns + ------- + drop_scans: numpy.ndarray + List of scan indices to drop + """ + import nibabel as nib + import numpy as np + import os.path as op + import parse + + with open(op.abspath(outlier_report), 'r') as fp: + lines = fp.readlines() + + p = parse.compile( + "Slice {slice:d} in scan {scan:d} is an outlier with " + "mean {mean_sd:f} standard deviations off, and mean " + "squared {mean_sq_sd:f} standard deviations off." + ) + + outliers = [p.parse(l).named for l in lines] + scans = {d['scan'] for d in outliers} + + def num_outliers(scan, outliers): + return len([d for d in outliers if d['scan'] == scan]) + + if 0 < threshold < 1: + img = nib.load(dwi_file) + try: + threshold *= img.header.get_n_slices() + except nib.spatialimages.HeaderDataError: + print('WARNING. We are not sure which dimension has the ' + 'slices in this image. So we are using the 3rd dim.', img.shape) + threshold *= img.shape[2] + + drop_scans = np.array([ + s for s in scans + if num_outliers(s, outliers) > threshold + ]) + + outpath = op.abspath("dropped_scans.txt") + np.savetxt(outpath, drop_scans, fmt="%d") + + return drop_scans, outpath + + id_outliers_node = pe.Node(niu.Function( + input_names=["outlier_report", "threshold", "dwi_file"], + output_names=["drop_scans", "outpath"], + function=id_outliers_fn), + name="id_outliers_node" + ) + + wf.connect(inputspec, 'dwi_file', id_outliers_node, 'dwi_file') + wf.connect(inputspec, 'slice_outlier_threshold', id_outliers_node, 'threshold') + + wf.connect(prep, "fsl_eddy.out_outlier_report", + id_outliers_node, "outlier_report") + + fslroi = pe.Node(fsl.ExtractROI(t_min=0, t_size=1), name="fslroi") + wf.connect(prep, "outputnode.out_file", fslroi, "in_file") + + bbreg = pe.Node(fs.BBRegister(contrast_type="t2", init="coreg", + out_fsl_file=True, + # subjects_dir=subjects_dir, + epi_mask=True), + name="bbreg") + bbreg.inputs.subject_id = 'freesurfer' # bids_sub_name + wf.connect(fslroi, "roi_file", bbreg, "source_file") + wf.connect(inputspec, 'subjects_dir', bbreg, 'subjects_dir') + + def drop_outliers_fn(in_file, in_bval, in_bvec, drop_scans): + """Drop outlier volumes from dwi file + Parameters + ---------- + in_file: string + Path to nii dwi file to drop outlier volumes from + in_bval: string + Path to bval file to drop outlier volumes from + in_bvec: string + Path to bvec file to drop outlier volumes from + drop_scans: numpy.ndarray + List of scan indices to drop + Returns + ------- + out_file: string + Path to "thinned" output dwi file + out_bval: string + Path to "thinned" output bval file + out_bvec: string + Path to "thinned" output bvec file + """ + import nibabel as nib + import numpy as np + import os.path as op + from nipype.utils.filemanip import fname_presuffix + + img = nib.load(op.abspath(in_file)) + img_data = img.get_data() + img_data_thinned = np.delete(img_data, + drop_scans, + axis=3) + if isinstance(img, nib.nifti1.Nifti1Image): + img_thinned = nib.Nifti1Image(img_data_thinned.astype(np.float64), + img.affine, + header=img.header) + elif isinstance(img, nib.nifti2.Nifti2Image): + img_thinned = nib.Nifti2Image(img_data_thinned.astype(np.float64), + img.affine, + header=img.header) + else: + raise TypeError("in_file does not contain Nifti image datatype.") + + out_file = fname_presuffix(in_file, suffix="_thinned", newpath=op.abspath('.')) + nib.save(img_thinned, op.abspath(out_file)) + + bval = np.loadtxt(in_bval) + bval_thinned = np.delete(bval, drop_scans, axis=0) + out_bval = fname_presuffix(in_bval, suffix="_thinned", newpath=op.abspath('.')) + np.savetxt(out_bval, bval_thinned) + + bvec = np.loadtxt(in_bvec) + bvec_thinned = np.delete(bvec, drop_scans, axis=1) + out_bvec = fname_presuffix(in_bvec, suffix="_thinned", newpath=op.abspath('.')) + np.savetxt(out_bvec, bvec_thinned) + + return out_file, out_bval, out_bvec + + drop_outliers_node = pe.Node(niu.Function( + input_names=["in_file", "in_bval", "in_bvec", "drop_scans"], + output_names=["out_file", "out_bval", "out_bvec"], + function=drop_outliers_fn), + name="drop_outliers_node" + ) + + # Align the output of drop_outliers_node & also the eddy corrected version to the anatomical space + # without resampling. and then for aparc+aseg & the mask, resample to the larger voxel size of the B0 image from + # fslroi. Also we need to apply the transformation to both bvecs (dropped & eddied) and I think we can just load + # the affine from bbreg (sio.loadmat) and np.dot(coord, aff) for each coord in bvec + + def get_orig(subjects_dir, sub='freesurfer'): + import os.path as op + return op.join(subjects_dir, sub, "mri", "orig.mgz") + + def get_aparc_aseg(subjects_dir, sub='freesurfer'): + import os.path as op + return op.join(subjects_dir, sub, "mri", "aparc+aseg.mgz") + + # transform the dropped volume version to anat space w/ out resampling + voltransform = pe.Node(fs.ApplyVolTransform(no_resample=True), + iterfield=['source_file', 'reg_file'], + name='transform') + + wf.connect(inputspec, 'subjects_dir', voltransform, 'subjects_dir') + wf.connect(inputspec, ('subjects_dir', get_aparc_aseg), voltransform, 'target_file') + wf.connect(prep, "outputnode.out_file", voltransform, "source_file") + wf.connect(bbreg, "out_reg_file", voltransform, "reg_file") + + def apply_transform_to_bvecs_fn(bvec_file, reg_mat_file): + import numpy as np + import nipype.utils.filemanip as fm + import os + + aff = np.loadtxt(reg_mat_file) + bvecs = np.loadtxt(bvec_file) + bvec_trans = [] + for bvec in bvecs.T: + coord = np.hstack((bvec, [1])) + coord_trans = np.dot(coord, aff)[:-1] + bvec_trans.append(coord_trans) + out_bvec = fm.fname_presuffix(bvec_file, suffix="anat_space", newpath=os.path.abspath('.')) + np.savetxt(out_bvec, np.asarray(bvec_trans).T) + return out_bvec + + apply_transform_to_bvecs_node = pe.Node(niu.Function(input_names=['bvec_file', 'reg_mat_file'], + output_names=['out_bvec'], + function=apply_transform_to_bvecs_fn), + name="applyTransformToBvecs") + wf.connect(bbreg, 'out_fsl_file', apply_transform_to_bvecs_node, 'reg_mat_file') + wf.connect(prep, 'outputnode.out_bvec', apply_transform_to_bvecs_node, 'bvec_file') + + # ok cool, now lets do the thresholding. + + wf.connect(id_outliers_node, "drop_scans", drop_outliers_node, "drop_scans") + wf.connect(voltransform, "transformed_file", drop_outliers_node, "in_file") + wf.connect(inputspec, 'bval_file', drop_outliers_node, 'in_bval') + wf.connect(apply_transform_to_bvecs_node, "out_bvec", drop_outliers_node, "in_bvec") + + # lets compute the tensor on both the dropped volume scan + # and also the original, eddy corrected one. + get_tensor = pe.Node(dipy.DTI(), name="dipy_tensor") + wf.connect(drop_outliers_node, "out_file", get_tensor, "in_file") + wf.connect(drop_outliers_node, "out_bval", get_tensor, "in_bval") + wf.connect(drop_outliers_node, "out_bvec", get_tensor, "in_bvec") + + get_tensor_eddy = get_tensor.clone('dipy_tensor_eddy') + wf.connect(voltransform, 'transformed_file', get_tensor_eddy, "in_file") + wf.connect(apply_transform_to_bvecs_node, 'out_bvec', get_tensor_eddy, "in_bvec") + wf.connect(inputspec, 'bval_file', get_tensor_eddy, 'in_bval') + + # AK: What is this, some vestigal node from a previous workflow? + # I'm not sure why the tensor gets scaled. but i guess lets scale it for + # both the dropped & eddy corrected versions. + scale_tensor = pe.Node(name='scale_tensor', interface=fsl.BinaryMaths()) + scale_tensor.inputs.operation = 'mul' + scale_tensor.inputs.operand_value = 1000 + wf.connect(get_tensor, 'out_file', scale_tensor, 'in_file') + + scale_tensor_eddy = scale_tensor.clone('scale_tensor_eddy') + wf.connect(get_tensor_eddy, 'out_file', scale_tensor_eddy, 'in_file') + + # OK now that anatomical stuff (segmentation & mask) + # We'll need: + # 1. the B0 image in anat space (fslroi the 'transformed file' of voltransform + # 2. the aparc aseg resampled-like the B0 image (mri_convert) + # 3. the resample aparc_aseg binarized to be the mask image. + + def binarize_aparc(aparc_aseg): + import nibabel as nib + from nipype.utils.filemanip import fname_presuffix + import os.path as op + + img = nib.load(aparc_aseg) + data, aff = img.get_data(), img.affine + outfile = fname_presuffix( + aparc_aseg, suffix="bin.nii.gz", + newpath=op.abspath("."), use_ext=False + ) + nib.Nifti1Image((data > 0).astype(float), aff).to_filename(outfile) + return outfile + + create_mask = pe.Node(niu.Function(input_names=["aparc_aseg"], + output_names=["outfile"], + function=binarize_aparc), + name="bin_aparc") + + get_b0_anat = fslroi.clone('get_b0_anat') + wf.connect(voltransform, 'transformed_file', get_b0_anat, 'in_file') + + # reslice the anat-space aparc+aseg to the DWI resolution + reslice_to_dwi = pe.Node(fs.MRIConvert(resample_type="nearest"), + name="reslice_to_dwi") + wf.connect(get_b0_anat, 'roi_file', reslice_to_dwi, 'reslice_like') + wf.connect(inputspec, ('subjects_dir', get_aparc_aseg), reslice_to_dwi, 'in_file') + + # also reslice the orig i suppose + reslice_orig_to_dwi = reslice_to_dwi.clone('resliceT1wToDwi') + wf.connect(inputspec, ('subjects_dir', get_orig), reslice_orig_to_dwi, 'in_file') + # reslice_orig_to_dwi.inputs.in_file = get_orig(subjects_dir, 'freesurfer') + reslice_orig_to_dwi.inputs.out_type = 'niigz' + wf.connect(get_b0_anat, 'roi_file', reslice_orig_to_dwi, 'reslice_like') + + # we assume the freesurfer is the output of BIDS + # so the freesurfer output is in /path/to/derivatives/sub-whatever/freesurfer + # which means the subject_dir is /path/to/derivatives/sub-whatever + # reslice_to_dwi.inputs.in_file = get_aparc_aseg(subjects_dir, 'freesurfer') + + # now we have a nice aparc+aseg still in anat space but resliced like the dwi file + # lets create a mask file from it. + + wf.connect(reslice_to_dwi, 'out_file', create_mask, 'aparc_aseg') + + # save all the things + datasink = pe.Node(nio.DataSink(), name="sinker") + wf.connect(inputspec, 'out_dir', datasink, 'base_directory') + wf.connect(inputspec, 'subject_id', datasink, 'container') + + wf.connect(drop_outliers_node, "out_file", datasink, "dmriprep.dwi.@thinned") + wf.connect(drop_outliers_node, "out_bval", datasink, "dmriprep.dwi.@bval_thinned") + wf.connect(drop_outliers_node, "out_bvec", datasink, "dmriprep.dwi.@bvec_thinned") + + # eddy corrected files + wf.connect(prep, "outputnode.out_file", datasink, "dmriprep.dwi_eddy.@corrected") + wf.connect(prep, "outputnode.out_bvec", datasink, "dmriprep.dwi_eddy.@rotated") + wf.connect(inputspec, 'bval_file', datasink, 'dmriprep.dwi_eddy.@bval') + + # all the eddy stuff except the corrected files + wf.connect(prep, "fsl_eddy.out_movement_rms", + datasink, "dmriprep.qc.@eddyparamsrms") + wf.connect(prep, "fsl_eddy.out_outlier_report", + datasink, "dmriprep.qc.@eddyparamsreport") + wf.connect(prep, "fsl_eddy.out_restricted_movement_rms", + datasink, "dmriprep.qc.@eddyparamsrestrictrms") + wf.connect(prep, "fsl_eddy.out_shell_alignment_parameters", + datasink, "dmriprep.qc.@eddyparamsshellalign") + wf.connect(prep, "fsl_eddy.out_parameter", + datasink, "dmriprep.qc.@eddyparams") + wf.connect(prep, "fsl_eddy.out_cnr_maps", + datasink, "dmriprep.qc.@eddycndr") + wf.connect(prep, "fsl_eddy.out_residuals", + datasink, "dmriprep.qc.@eddyresid") + + # the file that told us which volumes to drop + wf.connect(id_outliers_node, "outpath", datasink, "dmriprep.qc.@droppedscans") + + # the tensors of the dropped volumes dwi + wf.connect(get_tensor, "out_file", datasink, "dmriprep.dti.@tensor") + wf.connect(get_tensor, "fa_file", datasink, "dmriprep.dti.@fa") + wf.connect(get_tensor, "md_file", datasink, "dmriprep.dti.@md") + wf.connect(get_tensor, "ad_file", datasink, "dmriprep.dti.@ad") + wf.connect(get_tensor, "rd_file", datasink, "dmriprep.dti.@rd") + wf.connect(get_tensor, "color_fa_file", datasink, "dmriprep.dti.@colorfa") + wf.connect(scale_tensor, "out_file", datasink, "dmriprep.dti.@scaled_tensor") + + # the tensors of the eddied volumes dwi + wf.connect(get_tensor_eddy, "out_file", datasink, "dmriprep.dti_eddy.@tensor") + wf.connect(get_tensor_eddy, "fa_file", datasink, "dmriprep.dti_eddy.@fa") + wf.connect(get_tensor_eddy, "md_file", datasink, "dmriprep.dti_eddy.@md") + wf.connect(get_tensor_eddy, "ad_file", datasink, "dmriprep.dti_eddy.@ad") + wf.connect(get_tensor_eddy, "rd_file", datasink, "dmriprep.dti_eddy.@rd") + wf.connect(get_tensor_eddy, "color_fa_file", datasink, "dmriprep.dti_eddy.@colorfa") + wf.connect(scale_tensor_eddy, "out_file", datasink, "dmriprep.dti_eddy.@scaled_tensor") + + # all the eddy_quad stuff + wf.connect(eddy_quad, 'qc_json', datasink, "dmriprep.qc.@eddyquad_json") + wf.connect(eddy_quad, 'qc_pdf', datasink, "dmriprep.qc.@eddyquad_pdf") + wf.connect(eddy_quad, 'avg_b_png', datasink, "dmriprep.qc.@eddyquad_bpng") + wf.connect(eddy_quad, 'avg_b0_pe_png', + datasink, "dmriprep.qc.@eddyquad_b0png") + wf.connect(eddy_quad, 'cnr_png', datasink, "dmriprep.qc.@eddyquad_cnr") + wf.connect(eddy_quad, 'vdm_png', datasink, "dmriprep.qc.@eddyquad_vdm") + wf.connect(eddy_quad, 'residuals', datasink, 'dmriprep.qc.@eddyquad_resid') + + # anatomical registration stuff + wf.connect(bbreg, "min_cost_file", datasink, "dmriprep.reg.@mincost") + wf.connect(bbreg, "out_fsl_file", datasink, "dmriprep.reg.@fslfile") + wf.connect(bbreg, "out_reg_file", datasink, "dmriprep.reg.@reg") + + # anatomical files resliced + wf.connect(reslice_to_dwi, 'out_file', datasink, 'dmriprep.anat.@segmentation') + wf.connect(create_mask, 'outfile', datasink, 'dmriprep.anat.@mask') + wf.connect(reslice_orig_to_dwi, 'out_file', datasink, 'dmriprep.anat.@T1w') + + def report_fn(dwi_corrected_file, eddy_rms, eddy_report, + color_fa_file, anat_mask_file, outlier_indices, + eddy_qc_file): + from dmriprep.qc import create_report_json + + report = create_report_json(dwi_corrected_file, eddy_rms, eddy_report, + color_fa_file, anat_mask_file, outlier_indices, + eddy_qc_file) + return report + + report_node = pe.Node(niu.Function( + input_names=['dwi_corrected_file', 'eddy_rms', + 'eddy_report', 'color_fa_file', + 'anat_mask_file', 'outlier_indices', 'eddy_qc_file'], + output_names=['report'], + function=report_fn + ), name="reportJSON") + + # for the report, lets show the eddy corrected (full volume) image + wf.connect(voltransform, "transformed_file", report_node, 'dwi_corrected_file') + wf.connect(eddy_quad, 'qc_json', report_node, 'eddy_qc_file') + + # add the rms movement output from eddy + wf.connect(prep, "fsl_eddy.out_movement_rms", report_node, 'eddy_rms') + wf.connect(prep, "fsl_eddy.out_outlier_report", report_node, 'eddy_report') + wf.connect(id_outliers_node, 'drop_scans', report_node, 'outlier_indices') + + # the mask file to check our registration, and the colorFA file go in the report + wf.connect(create_mask, "outfile", report_node, 'anat_mask_file') + wf.connect(get_tensor, "color_fa_file", report_node, 'color_fa_file') + + # save that report! + wf.connect(report_node, 'report', datasink, 'dmriprep.report.@report') + + # this part is done last, to get the filenames *just right* + # its super annoying. + def name_files_nicely(dwi_file, subject_id): + import os.path as op + + dwi_fname = op.split(dwi_file)[1].split(".nii.gz")[0] + substitutions = [ + ("vol0000_flirt_merged.nii.gz", dwi_fname + '.nii.gz'), + ("stats.vol0000_flirt_merged.txt", dwi_fname + ".art.json"), + ("motion_parameters.par", dwi_fname + ".motion.txt"), + ("_rotated.bvec", ".bvec"), + ("art.vol0000_flirt_merged_outliers.txt", dwi_fname + ".outliers.txt"), + ("vol0000_flirt_merged", dwi_fname), + ("_roi_bbreg_freesurfer", "_register"), + ("dwi/eddy_corrected", "dwi/%s" % dwi_fname), + ("dti/eddy_corrected", "dti/%s" % dwi_fname.replace("_dwi", "")), + ("reg/eddy_corrected", "reg/%s" % dwi_fname.replace("_dwi", "")), + ("aparc+aseg_outbin", dwi_fname.replace("_dwi", "_mask")), + ("aparc+aseg_out", dwi_fname.replace("_dwi", "_aparc+aseg")), + ("art.eddy_corrected_outliers", dwi_fname.replace("dwi", "outliers")), + ("color_fa", "colorfa"), + ("orig_out", dwi_fname.replace("_dwi", "_T1w")), + ("stats.eddy_corrected", dwi_fname.replace("dwi", "artStats")), + ("eddy_corrected.eddy_parameters", dwi_fname + ".eddy_parameters"), + ("qc/eddy_corrected", "qc/" + dwi_fname), + ("derivatives/dmriprep", "derivatives/{}/dmriprep".format(subject_id)), + ("_rotatedanat_space_thinned", ""), + ("_thinned", ""), + ("eddy_corrected", dwi_fname), + ("_warped", ""), + ("_maths", "_scaled"), + ("dropped_scans", dwi_fname.replace("_dwi", "_dwi_dropped_scans")), + ("report.json", dwi_fname.replace("_dwi", "_dwi_report.json")) + ] + return substitutions + + node_name_files_nicely = pe.Node(niu.Function(input_names=['dwi_file', 'subject_id'], + output_names=['substitutions'], + function=name_files_nicely), + name="name_files_nicely") + wf.connect(inputspec, 'dwi_file', node_name_files_nicely, 'dwi_file') + wf.connect(inputspec, 'subject_id', node_name_files_nicely, 'subject_id') + wf.connect(node_name_files_nicely, 'substitutions', datasink, 'substitutions') + + return wf diff --git a/dmriprep/workflows/fieldmap/__init__.py b/dmriprep/workflows/fieldmap/__init__.py index a225866a..8c1b7432 100644 --- a/dmriprep/workflows/fieldmap/__init__.py +++ b/dmriprep/workflows/fieldmap/__init__.py @@ -32,7 +32,7 @@ :show-inheritance: """ -from .base import init_fmap_wf +from .fmap import init_fmap_wf # from .base import init_sdc_wf # from .unwarp import init_sdc_unwarp_wf, init_fmap_unwarp_report_wf From a4822498b07df18dfb76d04ef0a8f74ded703683 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Mon, 17 Jun 2019 17:54:14 -0400 Subject: [PATCH 020/156] Add subject_id to dwi base workflow and style changes --- dmriprep/workflows/base.py | 12 +- dmriprep/workflows/dwi/base.py | 2 +- dmriprep/workflows/dwi/datasink.py | 79 ++- dmriprep/workflows/dwi/report.py | 890 ++++++++++++------------ dmriprep/workflows/fieldmap/__init__.py | 45 -- dmriprep/workflows/fieldmap/fmap.py | 19 +- 6 files changed, 508 insertions(+), 539 deletions(-) diff --git a/dmriprep/workflows/base.py b/dmriprep/workflows/base.py index 4ee7508f..02bb7d9b 100644 --- a/dmriprep/workflows/base.py +++ b/dmriprep/workflows/base.py @@ -54,14 +54,14 @@ def init_single_subject_wf(layout, subject_id, name, work_dir, output_dir): for dwi_file in dwi_files: entities = layout.parse_file_entities(dwi_file) session_id = entities["session"] - dwi_preproc_wf = init_dwi_preproc_wf(dwi_file=dwi_file, layout=layout) + dwi_preproc_wf = init_dwi_preproc_wf( + subject_id=subject_id, dwi_file=dwi_file, layout=layout + ) datasink_wf = init_output_wf( - subject=subject_id, session=session_id, output_folder=output_dir + subject_id=subject_id, session_id=session_id, output_folder=output_dir ) dwi_preproc_wf.base_dir = os.path.join(os.path.abspath(work_dir), subject_id) - entities = layout.parse_file_entities(dwi_file) - session_id = entities["session"] inputspec = dwi_preproc_wf.get_node("inputnode") inputspec.inputs.subject_id = subject_id @@ -72,8 +72,8 @@ def init_single_subject_wf(layout, subject_id, name, work_dir, output_dir): inputspec.inputs.out_dir = os.path.abspath(output_dir) ds_inputspec = datasink_wf.get_node("inputnode") - ds_inputspec.inputs.subject = subject_id - ds_inputspec.inputs.session = session_id + ds_inputspec.inputs.subject_id = subject_id + ds_inputspec.inputs.session_id = session_id ds_inputspec.inputs.output_folder = output_dir wf_name = "sub_" + subject_id + "_ses_" + session_id + "_preproc_wf" diff --git a/dmriprep/workflows/dwi/base.py b/dmriprep/workflows/dwi/base.py index b9481fb0..ff92af50 100755 --- a/dmriprep/workflows/dwi/base.py +++ b/dmriprep/workflows/dwi/base.py @@ -2,7 +2,7 @@ from ..fieldmap import init_fmap_wf -def init_dwi_preproc_wf(dwi_file, layout): +def init_dwi_preproc_wf(subject_id, dwi_file, layout): from nipype.pipeline import engine as pe from nipype.interfaces import ( freesurfer as fs, diff --git a/dmriprep/workflows/dwi/datasink.py b/dmriprep/workflows/dwi/datasink.py index 530b6ba6..58500aae 100644 --- a/dmriprep/workflows/dwi/datasink.py +++ b/dmriprep/workflows/dwi/datasink.py @@ -1,40 +1,71 @@ #!/usr/bin/env python -def init_output_wf(subject, session, output_folder): + +def init_output_wf(subject_id, session_id, output_folder): from nipype.pipeline import engine as pe - from nipype.interfaces import (freesurfer as fs, fsl, mrtrix3, io as nio, \ - utility as niu) + from nipype.interfaces import ( + freesurfer as fs, + fsl, + mrtrix3, + io as nio, + utility as niu, + ) from nipype import logging op_wf = pe.Workflow(name="output_wf") - inputnode = pe.Node(niu.IdentityInterface(fields=[ - 'subject', - 'session', - 'out_file', - 'out_mask', - 'out_bvec', - 'output_folder']), - name="inputnode") + inputnode = pe.Node( + niu.IdentityInterface( + fields=[ + "subject_id", + "session_id", + "out_file", + "out_mask", + "out_bvec", + "output_folder", + ] + ), + name="inputnode", + ) - def build_path(output_folder, subject, session): + def build_path(output_folder, subject_id, session_id): import os.path as op - return op.join(output_folder, "sub-" + subject, "ses-" + session, "dwi") - concat = pe.Node(niu.Function(input_names=["output_folder", "subject", "session"], output_names=["built_folder"], function=build_path), name="build_path") + return op.join(output_folder, "sub-" + subject_id, "ses-" + session_id, "dwi") + + concat = pe.Node( + niu.Function( + input_names=["output_folder", "subject_id", "session_id"], + output_names=["built_folder"], + function=build_path, + ), + name="build_path", + ) datasink = pe.Node(nio.DataSink(), name="datasink") op_wf.connect( - [(inputnode, concat, [('subject', 'subject'), - ('session', 'session'), - ('output_folder', 'output_folder')]), - (concat, datasink, [('built_folder', 'base_directory')]), - (inputnode, datasink, [('out_file', '@result.@dwi'), - ('out_bvec', '@result.@bvec'), - ('out_mask', '@result.@mask')]) - ] - ) - + [ + ( + inputnode, + concat, + [ + ("subject_id", "subject_id"), + ("session_id", "session_id"), + ("output_folder", "output_folder"), + ], + ), + (concat, datasink, [("built_folder", "base_directory")]), + ( + inputnode, + datasink, + [ + ("out_file", "@result.@dwi"), + ("out_bvec", "@result.@bvec"), + ("out_mask", "@result.@mask"), + ], + ), + ] + ) return op_wf diff --git a/dmriprep/workflows/dwi/report.py b/dmriprep/workflows/dwi/report.py index 29579134..127123c3 100644 --- a/dmriprep/workflows/dwi/report.py +++ b/dmriprep/workflows/dwi/report.py @@ -1,448 +1,448 @@ #!/usr/bin/env python -def init_dwi_report_wf(): - - import nipype.interfaces.freesurfer as fs - import nipype.interfaces.fsl as fsl - import nipype.interfaces.io as nio - import nipype.interfaces.dipy as dipy - import nipype.interfaces.utility as niu - import nipype.pipeline.engine as pe - - inputspec = pe.Node(niu.IdentityInterface(fields=[ - 'subject_id', - 'dwi_file', - 'dwi_file_ap', - 'dwi_file_pa', - 'bvec_file', - 'bval_file', - 'subjects_dir', - 'out_dir', - 'eddy_niter', - 'slice_outlier_threshold' - ]), name="inputspec") - - def id_outliers_fn(outlier_report, threshold, dwi_file): - """Get list of scans that exceed threshold for number of outliers - Parameters - ---------- - outlier_report: string - Path to the fsl_eddy outlier report - threshold: int or float - If threshold is an int, it is treated as number of allowed outlier - slices. If threshold is a float between 0 and 1 (exclusive), it is - treated the fraction of allowed outlier slices before we drop the - whole volume. - dwi_file: string - Path to nii dwi file to determine total number of slices - Returns - ------- - drop_scans: numpy.ndarray - List of scan indices to drop - """ - import nibabel as nib - import numpy as np - import os.path as op - import parse - - with open(op.abspath(outlier_report), 'r') as fp: - lines = fp.readlines() - - p = parse.compile( - "Slice {slice:d} in scan {scan:d} is an outlier with " - "mean {mean_sd:f} standard deviations off, and mean " - "squared {mean_sq_sd:f} standard deviations off." - ) - - outliers = [p.parse(l).named for l in lines] - scans = {d['scan'] for d in outliers} - - def num_outliers(scan, outliers): - return len([d for d in outliers if d['scan'] == scan]) - - if 0 < threshold < 1: - img = nib.load(dwi_file) - try: - threshold *= img.header.get_n_slices() - except nib.spatialimages.HeaderDataError: - print('WARNING. We are not sure which dimension has the ' - 'slices in this image. So we are using the 3rd dim.', img.shape) - threshold *= img.shape[2] - - drop_scans = np.array([ - s for s in scans - if num_outliers(s, outliers) > threshold - ]) - - outpath = op.abspath("dropped_scans.txt") - np.savetxt(outpath, drop_scans, fmt="%d") - - return drop_scans, outpath - - id_outliers_node = pe.Node(niu.Function( - input_names=["outlier_report", "threshold", "dwi_file"], - output_names=["drop_scans", "outpath"], - function=id_outliers_fn), - name="id_outliers_node" - ) - - wf.connect(inputspec, 'dwi_file', id_outliers_node, 'dwi_file') - wf.connect(inputspec, 'slice_outlier_threshold', id_outliers_node, 'threshold') - - wf.connect(prep, "fsl_eddy.out_outlier_report", - id_outliers_node, "outlier_report") - - fslroi = pe.Node(fsl.ExtractROI(t_min=0, t_size=1), name="fslroi") - wf.connect(prep, "outputnode.out_file", fslroi, "in_file") - - bbreg = pe.Node(fs.BBRegister(contrast_type="t2", init="coreg", - out_fsl_file=True, - # subjects_dir=subjects_dir, - epi_mask=True), - name="bbreg") - bbreg.inputs.subject_id = 'freesurfer' # bids_sub_name - wf.connect(fslroi, "roi_file", bbreg, "source_file") - wf.connect(inputspec, 'subjects_dir', bbreg, 'subjects_dir') - - def drop_outliers_fn(in_file, in_bval, in_bvec, drop_scans): - """Drop outlier volumes from dwi file - Parameters - ---------- - in_file: string - Path to nii dwi file to drop outlier volumes from - in_bval: string - Path to bval file to drop outlier volumes from - in_bvec: string - Path to bvec file to drop outlier volumes from - drop_scans: numpy.ndarray - List of scan indices to drop - Returns - ------- - out_file: string - Path to "thinned" output dwi file - out_bval: string - Path to "thinned" output bval file - out_bvec: string - Path to "thinned" output bvec file - """ - import nibabel as nib - import numpy as np - import os.path as op - from nipype.utils.filemanip import fname_presuffix - - img = nib.load(op.abspath(in_file)) - img_data = img.get_data() - img_data_thinned = np.delete(img_data, - drop_scans, - axis=3) - if isinstance(img, nib.nifti1.Nifti1Image): - img_thinned = nib.Nifti1Image(img_data_thinned.astype(np.float64), - img.affine, - header=img.header) - elif isinstance(img, nib.nifti2.Nifti2Image): - img_thinned = nib.Nifti2Image(img_data_thinned.astype(np.float64), - img.affine, - header=img.header) - else: - raise TypeError("in_file does not contain Nifti image datatype.") - - out_file = fname_presuffix(in_file, suffix="_thinned", newpath=op.abspath('.')) - nib.save(img_thinned, op.abspath(out_file)) - - bval = np.loadtxt(in_bval) - bval_thinned = np.delete(bval, drop_scans, axis=0) - out_bval = fname_presuffix(in_bval, suffix="_thinned", newpath=op.abspath('.')) - np.savetxt(out_bval, bval_thinned) - - bvec = np.loadtxt(in_bvec) - bvec_thinned = np.delete(bvec, drop_scans, axis=1) - out_bvec = fname_presuffix(in_bvec, suffix="_thinned", newpath=op.abspath('.')) - np.savetxt(out_bvec, bvec_thinned) - - return out_file, out_bval, out_bvec - - drop_outliers_node = pe.Node(niu.Function( - input_names=["in_file", "in_bval", "in_bvec", "drop_scans"], - output_names=["out_file", "out_bval", "out_bvec"], - function=drop_outliers_fn), - name="drop_outliers_node" - ) - - # Align the output of drop_outliers_node & also the eddy corrected version to the anatomical space - # without resampling. and then for aparc+aseg & the mask, resample to the larger voxel size of the B0 image from - # fslroi. Also we need to apply the transformation to both bvecs (dropped & eddied) and I think we can just load - # the affine from bbreg (sio.loadmat) and np.dot(coord, aff) for each coord in bvec - - def get_orig(subjects_dir, sub='freesurfer'): - import os.path as op - return op.join(subjects_dir, sub, "mri", "orig.mgz") - - def get_aparc_aseg(subjects_dir, sub='freesurfer'): - import os.path as op - return op.join(subjects_dir, sub, "mri", "aparc+aseg.mgz") - - # transform the dropped volume version to anat space w/ out resampling - voltransform = pe.Node(fs.ApplyVolTransform(no_resample=True), - iterfield=['source_file', 'reg_file'], - name='transform') - - wf.connect(inputspec, 'subjects_dir', voltransform, 'subjects_dir') - wf.connect(inputspec, ('subjects_dir', get_aparc_aseg), voltransform, 'target_file') - wf.connect(prep, "outputnode.out_file", voltransform, "source_file") - wf.connect(bbreg, "out_reg_file", voltransform, "reg_file") - - def apply_transform_to_bvecs_fn(bvec_file, reg_mat_file): - import numpy as np - import nipype.utils.filemanip as fm - import os - - aff = np.loadtxt(reg_mat_file) - bvecs = np.loadtxt(bvec_file) - bvec_trans = [] - for bvec in bvecs.T: - coord = np.hstack((bvec, [1])) - coord_trans = np.dot(coord, aff)[:-1] - bvec_trans.append(coord_trans) - out_bvec = fm.fname_presuffix(bvec_file, suffix="anat_space", newpath=os.path.abspath('.')) - np.savetxt(out_bvec, np.asarray(bvec_trans).T) - return out_bvec - - apply_transform_to_bvecs_node = pe.Node(niu.Function(input_names=['bvec_file', 'reg_mat_file'], - output_names=['out_bvec'], - function=apply_transform_to_bvecs_fn), - name="applyTransformToBvecs") - wf.connect(bbreg, 'out_fsl_file', apply_transform_to_bvecs_node, 'reg_mat_file') - wf.connect(prep, 'outputnode.out_bvec', apply_transform_to_bvecs_node, 'bvec_file') - - # ok cool, now lets do the thresholding. - - wf.connect(id_outliers_node, "drop_scans", drop_outliers_node, "drop_scans") - wf.connect(voltransform, "transformed_file", drop_outliers_node, "in_file") - wf.connect(inputspec, 'bval_file', drop_outliers_node, 'in_bval') - wf.connect(apply_transform_to_bvecs_node, "out_bvec", drop_outliers_node, "in_bvec") - - # lets compute the tensor on both the dropped volume scan - # and also the original, eddy corrected one. - get_tensor = pe.Node(dipy.DTI(), name="dipy_tensor") - wf.connect(drop_outliers_node, "out_file", get_tensor, "in_file") - wf.connect(drop_outliers_node, "out_bval", get_tensor, "in_bval") - wf.connect(drop_outliers_node, "out_bvec", get_tensor, "in_bvec") - - get_tensor_eddy = get_tensor.clone('dipy_tensor_eddy') - wf.connect(voltransform, 'transformed_file', get_tensor_eddy, "in_file") - wf.connect(apply_transform_to_bvecs_node, 'out_bvec', get_tensor_eddy, "in_bvec") - wf.connect(inputspec, 'bval_file', get_tensor_eddy, 'in_bval') - - # AK: What is this, some vestigal node from a previous workflow? - # I'm not sure why the tensor gets scaled. but i guess lets scale it for - # both the dropped & eddy corrected versions. - scale_tensor = pe.Node(name='scale_tensor', interface=fsl.BinaryMaths()) - scale_tensor.inputs.operation = 'mul' - scale_tensor.inputs.operand_value = 1000 - wf.connect(get_tensor, 'out_file', scale_tensor, 'in_file') - - scale_tensor_eddy = scale_tensor.clone('scale_tensor_eddy') - wf.connect(get_tensor_eddy, 'out_file', scale_tensor_eddy, 'in_file') - - # OK now that anatomical stuff (segmentation & mask) - # We'll need: - # 1. the B0 image in anat space (fslroi the 'transformed file' of voltransform - # 2. the aparc aseg resampled-like the B0 image (mri_convert) - # 3. the resample aparc_aseg binarized to be the mask image. - - def binarize_aparc(aparc_aseg): - import nibabel as nib - from nipype.utils.filemanip import fname_presuffix - import os.path as op - - img = nib.load(aparc_aseg) - data, aff = img.get_data(), img.affine - outfile = fname_presuffix( - aparc_aseg, suffix="bin.nii.gz", - newpath=op.abspath("."), use_ext=False - ) - nib.Nifti1Image((data > 0).astype(float), aff).to_filename(outfile) - return outfile - - create_mask = pe.Node(niu.Function(input_names=["aparc_aseg"], - output_names=["outfile"], - function=binarize_aparc), - name="bin_aparc") - - get_b0_anat = fslroi.clone('get_b0_anat') - wf.connect(voltransform, 'transformed_file', get_b0_anat, 'in_file') - - # reslice the anat-space aparc+aseg to the DWI resolution - reslice_to_dwi = pe.Node(fs.MRIConvert(resample_type="nearest"), - name="reslice_to_dwi") - wf.connect(get_b0_anat, 'roi_file', reslice_to_dwi, 'reslice_like') - wf.connect(inputspec, ('subjects_dir', get_aparc_aseg), reslice_to_dwi, 'in_file') - - # also reslice the orig i suppose - reslice_orig_to_dwi = reslice_to_dwi.clone('resliceT1wToDwi') - wf.connect(inputspec, ('subjects_dir', get_orig), reslice_orig_to_dwi, 'in_file') - # reslice_orig_to_dwi.inputs.in_file = get_orig(subjects_dir, 'freesurfer') - reslice_orig_to_dwi.inputs.out_type = 'niigz' - wf.connect(get_b0_anat, 'roi_file', reslice_orig_to_dwi, 'reslice_like') - - # we assume the freesurfer is the output of BIDS - # so the freesurfer output is in /path/to/derivatives/sub-whatever/freesurfer - # which means the subject_dir is /path/to/derivatives/sub-whatever - # reslice_to_dwi.inputs.in_file = get_aparc_aseg(subjects_dir, 'freesurfer') - - # now we have a nice aparc+aseg still in anat space but resliced like the dwi file - # lets create a mask file from it. - - wf.connect(reslice_to_dwi, 'out_file', create_mask, 'aparc_aseg') - - # save all the things - datasink = pe.Node(nio.DataSink(), name="sinker") - wf.connect(inputspec, 'out_dir', datasink, 'base_directory') - wf.connect(inputspec, 'subject_id', datasink, 'container') - - wf.connect(drop_outliers_node, "out_file", datasink, "dmriprep.dwi.@thinned") - wf.connect(drop_outliers_node, "out_bval", datasink, "dmriprep.dwi.@bval_thinned") - wf.connect(drop_outliers_node, "out_bvec", datasink, "dmriprep.dwi.@bvec_thinned") - - # eddy corrected files - wf.connect(prep, "outputnode.out_file", datasink, "dmriprep.dwi_eddy.@corrected") - wf.connect(prep, "outputnode.out_bvec", datasink, "dmriprep.dwi_eddy.@rotated") - wf.connect(inputspec, 'bval_file', datasink, 'dmriprep.dwi_eddy.@bval') - - # all the eddy stuff except the corrected files - wf.connect(prep, "fsl_eddy.out_movement_rms", - datasink, "dmriprep.qc.@eddyparamsrms") - wf.connect(prep, "fsl_eddy.out_outlier_report", - datasink, "dmriprep.qc.@eddyparamsreport") - wf.connect(prep, "fsl_eddy.out_restricted_movement_rms", - datasink, "dmriprep.qc.@eddyparamsrestrictrms") - wf.connect(prep, "fsl_eddy.out_shell_alignment_parameters", - datasink, "dmriprep.qc.@eddyparamsshellalign") - wf.connect(prep, "fsl_eddy.out_parameter", - datasink, "dmriprep.qc.@eddyparams") - wf.connect(prep, "fsl_eddy.out_cnr_maps", - datasink, "dmriprep.qc.@eddycndr") - wf.connect(prep, "fsl_eddy.out_residuals", - datasink, "dmriprep.qc.@eddyresid") - - # the file that told us which volumes to drop - wf.connect(id_outliers_node, "outpath", datasink, "dmriprep.qc.@droppedscans") - - # the tensors of the dropped volumes dwi - wf.connect(get_tensor, "out_file", datasink, "dmriprep.dti.@tensor") - wf.connect(get_tensor, "fa_file", datasink, "dmriprep.dti.@fa") - wf.connect(get_tensor, "md_file", datasink, "dmriprep.dti.@md") - wf.connect(get_tensor, "ad_file", datasink, "dmriprep.dti.@ad") - wf.connect(get_tensor, "rd_file", datasink, "dmriprep.dti.@rd") - wf.connect(get_tensor, "color_fa_file", datasink, "dmriprep.dti.@colorfa") - wf.connect(scale_tensor, "out_file", datasink, "dmriprep.dti.@scaled_tensor") - - # the tensors of the eddied volumes dwi - wf.connect(get_tensor_eddy, "out_file", datasink, "dmriprep.dti_eddy.@tensor") - wf.connect(get_tensor_eddy, "fa_file", datasink, "dmriprep.dti_eddy.@fa") - wf.connect(get_tensor_eddy, "md_file", datasink, "dmriprep.dti_eddy.@md") - wf.connect(get_tensor_eddy, "ad_file", datasink, "dmriprep.dti_eddy.@ad") - wf.connect(get_tensor_eddy, "rd_file", datasink, "dmriprep.dti_eddy.@rd") - wf.connect(get_tensor_eddy, "color_fa_file", datasink, "dmriprep.dti_eddy.@colorfa") - wf.connect(scale_tensor_eddy, "out_file", datasink, "dmriprep.dti_eddy.@scaled_tensor") - - # all the eddy_quad stuff - wf.connect(eddy_quad, 'qc_json', datasink, "dmriprep.qc.@eddyquad_json") - wf.connect(eddy_quad, 'qc_pdf', datasink, "dmriprep.qc.@eddyquad_pdf") - wf.connect(eddy_quad, 'avg_b_png', datasink, "dmriprep.qc.@eddyquad_bpng") - wf.connect(eddy_quad, 'avg_b0_pe_png', - datasink, "dmriprep.qc.@eddyquad_b0png") - wf.connect(eddy_quad, 'cnr_png', datasink, "dmriprep.qc.@eddyquad_cnr") - wf.connect(eddy_quad, 'vdm_png', datasink, "dmriprep.qc.@eddyquad_vdm") - wf.connect(eddy_quad, 'residuals', datasink, 'dmriprep.qc.@eddyquad_resid') - - # anatomical registration stuff - wf.connect(bbreg, "min_cost_file", datasink, "dmriprep.reg.@mincost") - wf.connect(bbreg, "out_fsl_file", datasink, "dmriprep.reg.@fslfile") - wf.connect(bbreg, "out_reg_file", datasink, "dmriprep.reg.@reg") - - # anatomical files resliced - wf.connect(reslice_to_dwi, 'out_file', datasink, 'dmriprep.anat.@segmentation') - wf.connect(create_mask, 'outfile', datasink, 'dmriprep.anat.@mask') - wf.connect(reslice_orig_to_dwi, 'out_file', datasink, 'dmriprep.anat.@T1w') - - def report_fn(dwi_corrected_file, eddy_rms, eddy_report, - color_fa_file, anat_mask_file, outlier_indices, - eddy_qc_file): - from dmriprep.qc import create_report_json - - report = create_report_json(dwi_corrected_file, eddy_rms, eddy_report, - color_fa_file, anat_mask_file, outlier_indices, - eddy_qc_file) - return report - - report_node = pe.Node(niu.Function( - input_names=['dwi_corrected_file', 'eddy_rms', - 'eddy_report', 'color_fa_file', - 'anat_mask_file', 'outlier_indices', 'eddy_qc_file'], - output_names=['report'], - function=report_fn - ), name="reportJSON") - - # for the report, lets show the eddy corrected (full volume) image - wf.connect(voltransform, "transformed_file", report_node, 'dwi_corrected_file') - wf.connect(eddy_quad, 'qc_json', report_node, 'eddy_qc_file') - - # add the rms movement output from eddy - wf.connect(prep, "fsl_eddy.out_movement_rms", report_node, 'eddy_rms') - wf.connect(prep, "fsl_eddy.out_outlier_report", report_node, 'eddy_report') - wf.connect(id_outliers_node, 'drop_scans', report_node, 'outlier_indices') - - # the mask file to check our registration, and the colorFA file go in the report - wf.connect(create_mask, "outfile", report_node, 'anat_mask_file') - wf.connect(get_tensor, "color_fa_file", report_node, 'color_fa_file') - - # save that report! - wf.connect(report_node, 'report', datasink, 'dmriprep.report.@report') - - # this part is done last, to get the filenames *just right* - # its super annoying. - def name_files_nicely(dwi_file, subject_id): - import os.path as op - - dwi_fname = op.split(dwi_file)[1].split(".nii.gz")[0] - substitutions = [ - ("vol0000_flirt_merged.nii.gz", dwi_fname + '.nii.gz'), - ("stats.vol0000_flirt_merged.txt", dwi_fname + ".art.json"), - ("motion_parameters.par", dwi_fname + ".motion.txt"), - ("_rotated.bvec", ".bvec"), - ("art.vol0000_flirt_merged_outliers.txt", dwi_fname + ".outliers.txt"), - ("vol0000_flirt_merged", dwi_fname), - ("_roi_bbreg_freesurfer", "_register"), - ("dwi/eddy_corrected", "dwi/%s" % dwi_fname), - ("dti/eddy_corrected", "dti/%s" % dwi_fname.replace("_dwi", "")), - ("reg/eddy_corrected", "reg/%s" % dwi_fname.replace("_dwi", "")), - ("aparc+aseg_outbin", dwi_fname.replace("_dwi", "_mask")), - ("aparc+aseg_out", dwi_fname.replace("_dwi", "_aparc+aseg")), - ("art.eddy_corrected_outliers", dwi_fname.replace("dwi", "outliers")), - ("color_fa", "colorfa"), - ("orig_out", dwi_fname.replace("_dwi", "_T1w")), - ("stats.eddy_corrected", dwi_fname.replace("dwi", "artStats")), - ("eddy_corrected.eddy_parameters", dwi_fname + ".eddy_parameters"), - ("qc/eddy_corrected", "qc/" + dwi_fname), - ("derivatives/dmriprep", "derivatives/{}/dmriprep".format(subject_id)), - ("_rotatedanat_space_thinned", ""), - ("_thinned", ""), - ("eddy_corrected", dwi_fname), - ("_warped", ""), - ("_maths", "_scaled"), - ("dropped_scans", dwi_fname.replace("_dwi", "_dwi_dropped_scans")), - ("report.json", dwi_fname.replace("_dwi", "_dwi_report.json")) - ] - return substitutions - - node_name_files_nicely = pe.Node(niu.Function(input_names=['dwi_file', 'subject_id'], - output_names=['substitutions'], - function=name_files_nicely), - name="name_files_nicely") - wf.connect(inputspec, 'dwi_file', node_name_files_nicely, 'dwi_file') - wf.connect(inputspec, 'subject_id', node_name_files_nicely, 'subject_id') - wf.connect(node_name_files_nicely, 'substitutions', datasink, 'substitutions') - - return wf +# def init_dwi_report_wf(): +# +# import nipype.interfaces.freesurfer as fs +# import nipype.interfaces.fsl as fsl +# import nipype.interfaces.io as nio +# import nipype.interfaces.dipy as dipy +# import nipype.interfaces.utility as niu +# import nipype.pipeline.engine as pe +# +# inputspec = pe.Node(niu.IdentityInterface(fields=[ +# 'subject_id', +# 'dwi_file', +# 'dwi_file_ap', +# 'dwi_file_pa', +# 'bvec_file', +# 'bval_file', +# 'subjects_dir', +# 'out_dir', +# 'eddy_niter', +# 'slice_outlier_threshold' +# ]), name="inputspec") +# +# def id_outliers_fn(outlier_report, threshold, dwi_file): +# """Get list of scans that exceed threshold for number of outliers +# Parameters +# ---------- +# outlier_report: string +# Path to the fsl_eddy outlier report +# threshold: int or float +# If threshold is an int, it is treated as number of allowed outlier +# slices. If threshold is a float between 0 and 1 (exclusive), it is +# treated the fraction of allowed outlier slices before we drop the +# whole volume. +# dwi_file: string +# Path to nii dwi file to determine total number of slices +# Returns +# ------- +# drop_scans: numpy.ndarray +# List of scan indices to drop +# """ +# import nibabel as nib +# import numpy as np +# import os.path as op +# import parse +# +# with open(op.abspath(outlier_report), 'r') as fp: +# lines = fp.readlines() +# +# p = parse.compile( +# "Slice {slice:d} in scan {scan:d} is an outlier with " +# "mean {mean_sd:f} standard deviations off, and mean " +# "squared {mean_sq_sd:f} standard deviations off." +# ) +# +# outliers = [p.parse(l).named for l in lines] +# scans = {d['scan'] for d in outliers} +# +# def num_outliers(scan, outliers): +# return len([d for d in outliers if d['scan'] == scan]) +# +# if 0 < threshold < 1: +# img = nib.load(dwi_file) +# try: +# threshold *= img.header.get_n_slices() +# except nib.spatialimages.HeaderDataError: +# print('WARNING. We are not sure which dimension has the ' +# 'slices in this image. So we are using the 3rd dim.', img.shape) +# threshold *= img.shape[2] +# +# drop_scans = np.array([ +# s for s in scans +# if num_outliers(s, outliers) > threshold +# ]) +# +# outpath = op.abspath("dropped_scans.txt") +# np.savetxt(outpath, drop_scans, fmt="%d") +# +# return drop_scans, outpath +# +# id_outliers_node = pe.Node(niu.Function( +# input_names=["outlier_report", "threshold", "dwi_file"], +# output_names=["drop_scans", "outpath"], +# function=id_outliers_fn), +# name="id_outliers_node" +# ) +# +# wf.connect(inputspec, 'dwi_file', id_outliers_node, 'dwi_file') +# wf.connect(inputspec, 'slice_outlier_threshold', id_outliers_node, 'threshold') +# +# wf.connect(prep, "fsl_eddy.out_outlier_report", +# id_outliers_node, "outlier_report") +# +# fslroi = pe.Node(fsl.ExtractROI(t_min=0, t_size=1), name="fslroi") +# wf.connect(prep, "outputnode.out_file", fslroi, "in_file") +# +# bbreg = pe.Node(fs.BBRegister(contrast_type="t2", init="coreg", +# out_fsl_file=True, +# # subjects_dir=subjects_dir, +# epi_mask=True), +# name="bbreg") +# bbreg.inputs.subject_id = 'freesurfer' # bids_sub_name +# wf.connect(fslroi, "roi_file", bbreg, "source_file") +# wf.connect(inputspec, 'subjects_dir', bbreg, 'subjects_dir') +# +# def drop_outliers_fn(in_file, in_bval, in_bvec, drop_scans): +# """Drop outlier volumes from dwi file +# Parameters +# ---------- +# in_file: string +# Path to nii dwi file to drop outlier volumes from +# in_bval: string +# Path to bval file to drop outlier volumes from +# in_bvec: string +# Path to bvec file to drop outlier volumes from +# drop_scans: numpy.ndarray +# List of scan indices to drop +# Returns +# ------- +# out_file: string +# Path to "thinned" output dwi file +# out_bval: string +# Path to "thinned" output bval file +# out_bvec: string +# Path to "thinned" output bvec file +# """ +# import nibabel as nib +# import numpy as np +# import os.path as op +# from nipype.utils.filemanip import fname_presuffix +# +# img = nib.load(op.abspath(in_file)) +# img_data = img.get_data() +# img_data_thinned = np.delete(img_data, +# drop_scans, +# axis=3) +# if isinstance(img, nib.nifti1.Nifti1Image): +# img_thinned = nib.Nifti1Image(img_data_thinned.astype(np.float64), +# img.affine, +# header=img.header) +# elif isinstance(img, nib.nifti2.Nifti2Image): +# img_thinned = nib.Nifti2Image(img_data_thinned.astype(np.float64), +# img.affine, +# header=img.header) +# else: +# raise TypeError("in_file does not contain Nifti image datatype.") +# +# out_file = fname_presuffix(in_file, suffix="_thinned", newpath=op.abspath('.')) +# nib.save(img_thinned, op.abspath(out_file)) +# +# bval = np.loadtxt(in_bval) +# bval_thinned = np.delete(bval, drop_scans, axis=0) +# out_bval = fname_presuffix(in_bval, suffix="_thinned", newpath=op.abspath('.')) +# np.savetxt(out_bval, bval_thinned) +# +# bvec = np.loadtxt(in_bvec) +# bvec_thinned = np.delete(bvec, drop_scans, axis=1) +# out_bvec = fname_presuffix(in_bvec, suffix="_thinned", newpath=op.abspath('.')) +# np.savetxt(out_bvec, bvec_thinned) +# +# return out_file, out_bval, out_bvec +# +# drop_outliers_node = pe.Node(niu.Function( +# input_names=["in_file", "in_bval", "in_bvec", "drop_scans"], +# output_names=["out_file", "out_bval", "out_bvec"], +# function=drop_outliers_fn), +# name="drop_outliers_node" +# ) +# +# # Align the output of drop_outliers_node & also the eddy corrected version to the anatomical space +# # without resampling. and then for aparc+aseg & the mask, resample to the larger voxel size of the B0 image from +# # fslroi. Also we need to apply the transformation to both bvecs (dropped & eddied) and I think we can just load +# # the affine from bbreg (sio.loadmat) and np.dot(coord, aff) for each coord in bvec +# +# def get_orig(subjects_dir, sub='freesurfer'): +# import os.path as op +# return op.join(subjects_dir, sub, "mri", "orig.mgz") +# +# def get_aparc_aseg(subjects_dir, sub='freesurfer'): +# import os.path as op +# return op.join(subjects_dir, sub, "mri", "aparc+aseg.mgz") +# +# # transform the dropped volume version to anat space w/ out resampling +# voltransform = pe.Node(fs.ApplyVolTransform(no_resample=True), +# iterfield=['source_file', 'reg_file'], +# name='transform') +# +# wf.connect(inputspec, 'subjects_dir', voltransform, 'subjects_dir') +# wf.connect(inputspec, ('subjects_dir', get_aparc_aseg), voltransform, 'target_file') +# wf.connect(prep, "outputnode.out_file", voltransform, "source_file") +# wf.connect(bbreg, "out_reg_file", voltransform, "reg_file") +# +# def apply_transform_to_bvecs_fn(bvec_file, reg_mat_file): +# import numpy as np +# import nipype.utils.filemanip as fm +# import os +# +# aff = np.loadtxt(reg_mat_file) +# bvecs = np.loadtxt(bvec_file) +# bvec_trans = [] +# for bvec in bvecs.T: +# coord = np.hstack((bvec, [1])) +# coord_trans = np.dot(coord, aff)[:-1] +# bvec_trans.append(coord_trans) +# out_bvec = fm.fname_presuffix(bvec_file, suffix="anat_space", newpath=os.path.abspath('.')) +# np.savetxt(out_bvec, np.asarray(bvec_trans).T) +# return out_bvec +# +# apply_transform_to_bvecs_node = pe.Node(niu.Function(input_names=['bvec_file', 'reg_mat_file'], +# output_names=['out_bvec'], +# function=apply_transform_to_bvecs_fn), +# name="applyTransformToBvecs") +# wf.connect(bbreg, 'out_fsl_file', apply_transform_to_bvecs_node, 'reg_mat_file') +# wf.connect(prep, 'outputnode.out_bvec', apply_transform_to_bvecs_node, 'bvec_file') +# +# # ok cool, now lets do the thresholding. +# +# wf.connect(id_outliers_node, "drop_scans", drop_outliers_node, "drop_scans") +# wf.connect(voltransform, "transformed_file", drop_outliers_node, "in_file") +# wf.connect(inputspec, 'bval_file', drop_outliers_node, 'in_bval') +# wf.connect(apply_transform_to_bvecs_node, "out_bvec", drop_outliers_node, "in_bvec") +# +# # lets compute the tensor on both the dropped volume scan +# # and also the original, eddy corrected one. +# get_tensor = pe.Node(dipy.DTI(), name="dipy_tensor") +# wf.connect(drop_outliers_node, "out_file", get_tensor, "in_file") +# wf.connect(drop_outliers_node, "out_bval", get_tensor, "in_bval") +# wf.connect(drop_outliers_node, "out_bvec", get_tensor, "in_bvec") +# +# get_tensor_eddy = get_tensor.clone('dipy_tensor_eddy') +# wf.connect(voltransform, 'transformed_file', get_tensor_eddy, "in_file") +# wf.connect(apply_transform_to_bvecs_node, 'out_bvec', get_tensor_eddy, "in_bvec") +# wf.connect(inputspec, 'bval_file', get_tensor_eddy, 'in_bval') +# +# # AK: What is this, some vestigal node from a previous workflow? +# # I'm not sure why the tensor gets scaled. but i guess lets scale it for +# # both the dropped & eddy corrected versions. +# scale_tensor = pe.Node(name='scale_tensor', interface=fsl.BinaryMaths()) +# scale_tensor.inputs.operation = 'mul' +# scale_tensor.inputs.operand_value = 1000 +# wf.connect(get_tensor, 'out_file', scale_tensor, 'in_file') +# +# scale_tensor_eddy = scale_tensor.clone('scale_tensor_eddy') +# wf.connect(get_tensor_eddy, 'out_file', scale_tensor_eddy, 'in_file') +# +# # OK now that anatomical stuff (segmentation & mask) +# # We'll need: +# # 1. the B0 image in anat space (fslroi the 'transformed file' of voltransform +# # 2. the aparc aseg resampled-like the B0 image (mri_convert) +# # 3. the resample aparc_aseg binarized to be the mask image. +# +# def binarize_aparc(aparc_aseg): +# import nibabel as nib +# from nipype.utils.filemanip import fname_presuffix +# import os.path as op +# +# img = nib.load(aparc_aseg) +# data, aff = img.get_data(), img.affine +# outfile = fname_presuffix( +# aparc_aseg, suffix="bin.nii.gz", +# newpath=op.abspath("."), use_ext=False +# ) +# nib.Nifti1Image((data > 0).astype(float), aff).to_filename(outfile) +# return outfile +# +# create_mask = pe.Node(niu.Function(input_names=["aparc_aseg"], +# output_names=["outfile"], +# function=binarize_aparc), +# name="bin_aparc") +# +# get_b0_anat = fslroi.clone('get_b0_anat') +# wf.connect(voltransform, 'transformed_file', get_b0_anat, 'in_file') +# +# # reslice the anat-space aparc+aseg to the DWI resolution +# reslice_to_dwi = pe.Node(fs.MRIConvert(resample_type="nearest"), +# name="reslice_to_dwi") +# wf.connect(get_b0_anat, 'roi_file', reslice_to_dwi, 'reslice_like') +# wf.connect(inputspec, ('subjects_dir', get_aparc_aseg), reslice_to_dwi, 'in_file') +# +# # also reslice the orig i suppose +# reslice_orig_to_dwi = reslice_to_dwi.clone('resliceT1wToDwi') +# wf.connect(inputspec, ('subjects_dir', get_orig), reslice_orig_to_dwi, 'in_file') +# # reslice_orig_to_dwi.inputs.in_file = get_orig(subjects_dir, 'freesurfer') +# reslice_orig_to_dwi.inputs.out_type = 'niigz' +# wf.connect(get_b0_anat, 'roi_file', reslice_orig_to_dwi, 'reslice_like') +# +# # we assume the freesurfer is the output of BIDS +# # so the freesurfer output is in /path/to/derivatives/sub-whatever/freesurfer +# # which means the subject_dir is /path/to/derivatives/sub-whatever +# # reslice_to_dwi.inputs.in_file = get_aparc_aseg(subjects_dir, 'freesurfer') +# +# # now we have a nice aparc+aseg still in anat space but resliced like the dwi file +# # lets create a mask file from it. +# +# wf.connect(reslice_to_dwi, 'out_file', create_mask, 'aparc_aseg') +# +# # save all the things +# datasink = pe.Node(nio.DataSink(), name="sinker") +# wf.connect(inputspec, 'out_dir', datasink, 'base_directory') +# wf.connect(inputspec, 'subject_id', datasink, 'container') +# +# wf.connect(drop_outliers_node, "out_file", datasink, "dmriprep.dwi.@thinned") +# wf.connect(drop_outliers_node, "out_bval", datasink, "dmriprep.dwi.@bval_thinned") +# wf.connect(drop_outliers_node, "out_bvec", datasink, "dmriprep.dwi.@bvec_thinned") +# +# # eddy corrected files +# wf.connect(prep, "outputnode.out_file", datasink, "dmriprep.dwi_eddy.@corrected") +# wf.connect(prep, "outputnode.out_bvec", datasink, "dmriprep.dwi_eddy.@rotated") +# wf.connect(inputspec, 'bval_file', datasink, 'dmriprep.dwi_eddy.@bval') +# +# # all the eddy stuff except the corrected files +# wf.connect(prep, "fsl_eddy.out_movement_rms", +# datasink, "dmriprep.qc.@eddyparamsrms") +# wf.connect(prep, "fsl_eddy.out_outlier_report", +# datasink, "dmriprep.qc.@eddyparamsreport") +# wf.connect(prep, "fsl_eddy.out_restricted_movement_rms", +# datasink, "dmriprep.qc.@eddyparamsrestrictrms") +# wf.connect(prep, "fsl_eddy.out_shell_alignment_parameters", +# datasink, "dmriprep.qc.@eddyparamsshellalign") +# wf.connect(prep, "fsl_eddy.out_parameter", +# datasink, "dmriprep.qc.@eddyparams") +# wf.connect(prep, "fsl_eddy.out_cnr_maps", +# datasink, "dmriprep.qc.@eddycndr") +# wf.connect(prep, "fsl_eddy.out_residuals", +# datasink, "dmriprep.qc.@eddyresid") +# +# # the file that told us which volumes to drop +# wf.connect(id_outliers_node, "outpath", datasink, "dmriprep.qc.@droppedscans") +# +# # the tensors of the dropped volumes dwi +# wf.connect(get_tensor, "out_file", datasink, "dmriprep.dti.@tensor") +# wf.connect(get_tensor, "fa_file", datasink, "dmriprep.dti.@fa") +# wf.connect(get_tensor, "md_file", datasink, "dmriprep.dti.@md") +# wf.connect(get_tensor, "ad_file", datasink, "dmriprep.dti.@ad") +# wf.connect(get_tensor, "rd_file", datasink, "dmriprep.dti.@rd") +# wf.connect(get_tensor, "color_fa_file", datasink, "dmriprep.dti.@colorfa") +# wf.connect(scale_tensor, "out_file", datasink, "dmriprep.dti.@scaled_tensor") +# +# # the tensors of the eddied volumes dwi +# wf.connect(get_tensor_eddy, "out_file", datasink, "dmriprep.dti_eddy.@tensor") +# wf.connect(get_tensor_eddy, "fa_file", datasink, "dmriprep.dti_eddy.@fa") +# wf.connect(get_tensor_eddy, "md_file", datasink, "dmriprep.dti_eddy.@md") +# wf.connect(get_tensor_eddy, "ad_file", datasink, "dmriprep.dti_eddy.@ad") +# wf.connect(get_tensor_eddy, "rd_file", datasink, "dmriprep.dti_eddy.@rd") +# wf.connect(get_tensor_eddy, "color_fa_file", datasink, "dmriprep.dti_eddy.@colorfa") +# wf.connect(scale_tensor_eddy, "out_file", datasink, "dmriprep.dti_eddy.@scaled_tensor") +# +# # all the eddy_quad stuff +# wf.connect(eddy_quad, 'qc_json', datasink, "dmriprep.qc.@eddyquad_json") +# wf.connect(eddy_quad, 'qc_pdf', datasink, "dmriprep.qc.@eddyquad_pdf") +# wf.connect(eddy_quad, 'avg_b_png', datasink, "dmriprep.qc.@eddyquad_bpng") +# wf.connect(eddy_quad, 'avg_b0_pe_png', +# datasink, "dmriprep.qc.@eddyquad_b0png") +# wf.connect(eddy_quad, 'cnr_png', datasink, "dmriprep.qc.@eddyquad_cnr") +# wf.connect(eddy_quad, 'vdm_png', datasink, "dmriprep.qc.@eddyquad_vdm") +# wf.connect(eddy_quad, 'residuals', datasink, 'dmriprep.qc.@eddyquad_resid') +# +# # anatomical registration stuff +# wf.connect(bbreg, "min_cost_file", datasink, "dmriprep.reg.@mincost") +# wf.connect(bbreg, "out_fsl_file", datasink, "dmriprep.reg.@fslfile") +# wf.connect(bbreg, "out_reg_file", datasink, "dmriprep.reg.@reg") +# +# # anatomical files resliced +# wf.connect(reslice_to_dwi, 'out_file', datasink, 'dmriprep.anat.@segmentation') +# wf.connect(create_mask, 'outfile', datasink, 'dmriprep.anat.@mask') +# wf.connect(reslice_orig_to_dwi, 'out_file', datasink, 'dmriprep.anat.@T1w') +# +# def report_fn(dwi_corrected_file, eddy_rms, eddy_report, +# color_fa_file, anat_mask_file, outlier_indices, +# eddy_qc_file): +# from dmriprep.qc import create_report_json +# +# report = create_report_json(dwi_corrected_file, eddy_rms, eddy_report, +# color_fa_file, anat_mask_file, outlier_indices, +# eddy_qc_file) +# return report +# +# report_node = pe.Node(niu.Function( +# input_names=['dwi_corrected_file', 'eddy_rms', +# 'eddy_report', 'color_fa_file', +# 'anat_mask_file', 'outlier_indices', 'eddy_qc_file'], +# output_names=['report'], +# function=report_fn +# ), name="reportJSON") +# +# # for the report, lets show the eddy corrected (full volume) image +# wf.connect(voltransform, "transformed_file", report_node, 'dwi_corrected_file') +# wf.connect(eddy_quad, 'qc_json', report_node, 'eddy_qc_file') +# +# # add the rms movement output from eddy +# wf.connect(prep, "fsl_eddy.out_movement_rms", report_node, 'eddy_rms') +# wf.connect(prep, "fsl_eddy.out_outlier_report", report_node, 'eddy_report') +# wf.connect(id_outliers_node, 'drop_scans', report_node, 'outlier_indices') +# +# # the mask file to check our registration, and the colorFA file go in the report +# wf.connect(create_mask, "outfile", report_node, 'anat_mask_file') +# wf.connect(get_tensor, "color_fa_file", report_node, 'color_fa_file') +# +# # save that report! +# wf.connect(report_node, 'report', datasink, 'dmriprep.report.@report') +# +# # this part is done last, to get the filenames *just right* +# # its super annoying. +# def name_files_nicely(dwi_file, subject_id): +# import os.path as op +# +# dwi_fname = op.split(dwi_file)[1].split(".nii.gz")[0] +# substitutions = [ +# ("vol0000_flirt_merged.nii.gz", dwi_fname + '.nii.gz'), +# ("stats.vol0000_flirt_merged.txt", dwi_fname + ".art.json"), +# ("motion_parameters.par", dwi_fname + ".motion.txt"), +# ("_rotated.bvec", ".bvec"), +# ("art.vol0000_flirt_merged_outliers.txt", dwi_fname + ".outliers.txt"), +# ("vol0000_flirt_merged", dwi_fname), +# ("_roi_bbreg_freesurfer", "_register"), +# ("dwi/eddy_corrected", "dwi/%s" % dwi_fname), +# ("dti/eddy_corrected", "dti/%s" % dwi_fname.replace("_dwi", "")), +# ("reg/eddy_corrected", "reg/%s" % dwi_fname.replace("_dwi", "")), +# ("aparc+aseg_outbin", dwi_fname.replace("_dwi", "_mask")), +# ("aparc+aseg_out", dwi_fname.replace("_dwi", "_aparc+aseg")), +# ("art.eddy_corrected_outliers", dwi_fname.replace("dwi", "outliers")), +# ("color_fa", "colorfa"), +# ("orig_out", dwi_fname.replace("_dwi", "_T1w")), +# ("stats.eddy_corrected", dwi_fname.replace("dwi", "artStats")), +# ("eddy_corrected.eddy_parameters", dwi_fname + ".eddy_parameters"), +# ("qc/eddy_corrected", "qc/" + dwi_fname), +# ("derivatives/dmriprep", "derivatives/{}/dmriprep".format(subject_id)), +# ("_rotatedanat_space_thinned", ""), +# ("_thinned", ""), +# ("eddy_corrected", dwi_fname), +# ("_warped", ""), +# ("_maths", "_scaled"), +# ("dropped_scans", dwi_fname.replace("_dwi", "_dwi_dropped_scans")), +# ("report.json", dwi_fname.replace("_dwi", "_dwi_report.json")) +# ] +# return substitutions +# +# node_name_files_nicely = pe.Node(niu.Function(input_names=['dwi_file', 'subject_id'], +# output_names=['substitutions'], +# function=name_files_nicely), +# name="name_files_nicely") +# wf.connect(inputspec, 'dwi_file', node_name_files_nicely, 'dwi_file') +# wf.connect(inputspec, 'subject_id', node_name_files_nicely, 'subject_id') +# wf.connect(node_name_files_nicely, 'substitutions', datasink, 'substitutions') +# +# return wf diff --git a/dmriprep/workflows/fieldmap/__init__.py b/dmriprep/workflows/fieldmap/__init__.py index 8c1b7432..b2738743 100644 --- a/dmriprep/workflows/fieldmap/__init__.py +++ b/dmriprep/workflows/fieldmap/__init__.py @@ -1,48 +1,3 @@ #!/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: -""" -.. _sdc_estimation : -Fieldmap estimation and unwarping workflows -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. automodule:: fmriprep.workflows.fieldmap.base - :members: - :undoc-members: - :show-inheritance: -.. automodule:: fmriprep.workflows.fieldmap.fmap - :members: - :undoc-members: - :show-inheritance: -.. automodule:: fmriprep.workflows.fieldmap.phdiff - :members: - :undoc-members: - :show-inheritance: -.. automodule:: fmriprep.workflows.fieldmap.pepolar - :members: - :undoc-members: - :show-inheritance: -.. automodule:: fmriprep.workflows.fieldmap.syn - :members: - :undoc-members: - :show-inheritance: -.. automodule:: fmriprep.workflows.fieldmap.unwarp - :members: - :undoc-members: - :show-inheritance: -""" from .fmap import init_fmap_wf - -# from .base import init_sdc_wf -# from .unwarp import init_sdc_unwarp_wf, init_fmap_unwarp_report_wf -# from .pepolar import init_pepolar_unwarp_wf -# from .syn import init_syn_sdc_wf -# -# __all__ = [ -# "init_sdc_wf", -# "init_sdc_unwarp_wf", -# "init_fmap_unwarp_report_wf", -# "init_pepolar_unwarp_wf", -# "init_syn_sdc_wf", -# ] diff --git a/dmriprep/workflows/fieldmap/fmap.py b/dmriprep/workflows/fieldmap/fmap.py index cd4995d8..6eccc565 100644 --- a/dmriprep/workflows/fieldmap/fmap.py +++ b/dmriprep/workflows/fieldmap/fmap.py @@ -1,21 +1,4 @@ -# -*- 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: -""" -.. _sdc_direct_b0 : -Direct B0 mapping sequences -~~~~~~~~~~~~~~~~~~~~~~~~~~~ -When the fieldmap is directly measured with a prescribed sequence (such as -:abbr:`SE (spiral echo)`), we only need to calculate the corresponding B-Spline -coefficients to adapt the fieldmap to the TOPUP tool. -This procedure is described with more detail `here `__. -This corresponds to the section 8.9.3 --fieldmap image (and one magnitude image)-- -of the BIDS specification. -""" -# from sdcflows.workflows.fmap import init_fmap_wf -# -# __all__ = ["init_fmap_wf"] +#!/usr/bin/env python def init_fmap_wf(): From c991ac1aa5bc33ee9ca4acb1b199d624e46e98a6 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Mon, 17 Jun 2019 19:15:44 -0400 Subject: [PATCH 021/156] [FIX] add spaces to help and update fmap wf import --- dmriprep/cli.py | 10 +++++----- dmriprep/workflows/dwi/base.py | 4 +++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/dmriprep/cli.py b/dmriprep/cli.py index 6542817b..2f5dab9f 100644 --- a/dmriprep/cli.py +++ b/dmriprep/cli.py @@ -21,11 +21,11 @@ @click.command() @click.option( "--participant-label", - help="The label(s) of the participant(s) that should be" - "analyzed. The label corresponds to" - "sub- from the BIDS spec (so it does" - "not include 'sub-'). If this parameter is not provided" - "all subjects will be analyzed. Multiple participants" + help="The label(s) of the participant(s) that should be " + "analyzed. The label corresponds to " + "sub- from the BIDS spec (so it does " + "not include 'sub-'). If this parameter is not provided " + "all subjects will be analyzed. Multiple participants " "can be specified with a space separated list.", default=None, ) diff --git a/dmriprep/workflows/dwi/base.py b/dmriprep/workflows/dwi/base.py index ff92af50..c8c9998e 100755 --- a/dmriprep/workflows/dwi/base.py +++ b/dmriprep/workflows/dwi/base.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -from ..fieldmap import init_fmap_wf +from ..fieldmap.fmap import init_fmap_wf def init_dwi_preproc_wf(subject_id, dwi_file, layout): @@ -43,6 +43,8 @@ def init_dwi_preproc_wf(subject_id, dwi_file, layout): "metadata", "bvec_file", "bval_file", + "fieldmap", + "magnitude" "out_dir", "eddy_niter", "slice_outlier_threshold", From 9dfb717001a87d88f564b1464f2dcc3ba071e018 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Tue, 18 Jun 2019 10:04:09 -0400 Subject: [PATCH 022/156] [REF] refactor sdc workflow to allow multiple fmap types in future --- dmriprep/workflows/base.py | 5 +-- dmriprep/workflows/dwi/base.py | 42 ++++++++----------------- dmriprep/workflows/fieldmap/__init__.py | 1 + dmriprep/workflows/fieldmap/base.py | 33 +++++++++++++++++++ dmriprep/workflows/fieldmap/fmap.py | 12 +++---- 5 files changed, 54 insertions(+), 39 deletions(-) create mode 100644 dmriprep/workflows/fieldmap/base.py diff --git a/dmriprep/workflows/base.py b/dmriprep/workflows/base.py index 02bb7d9b..644a8d07 100644 --- a/dmriprep/workflows/base.py +++ b/dmriprep/workflows/base.py @@ -54,8 +54,9 @@ def init_single_subject_wf(layout, subject_id, name, work_dir, output_dir): for dwi_file in dwi_files: entities = layout.parse_file_entities(dwi_file) session_id = entities["session"] + metadata = layout.get_metadata(dwi_file) dwi_preproc_wf = init_dwi_preproc_wf( - subject_id=subject_id, dwi_file=dwi_file, layout=layout + subject_id=subject_id, dwi_file=dwi_file, metadata=metadata, layout=layout ) datasink_wf = init_output_wf( subject_id=subject_id, session_id=session_id, output_folder=output_dir @@ -66,7 +67,7 @@ def init_single_subject_wf(layout, subject_id, name, work_dir, output_dir): inputspec = dwi_preproc_wf.get_node("inputnode") inputspec.inputs.subject_id = subject_id inputspec.inputs.dwi_file = dwi_file - inputspec.inputs.metadata = layout.get_metadata(dwi_file) + inputspec.inputs.metadata = metadata inputspec.inputs.bvec_file = layout.get_bvec(dwi_file) inputspec.inputs.bval_file = layout.get_bval(dwi_file) inputspec.inputs.out_dir = os.path.abspath(output_dir) diff --git a/dmriprep/workflows/dwi/base.py b/dmriprep/workflows/dwi/base.py index c8c9998e..89f9243f 100755 --- a/dmriprep/workflows/dwi/base.py +++ b/dmriprep/workflows/dwi/base.py @@ -1,8 +1,7 @@ #!/usr/bin/env python -from ..fieldmap.fmap import init_fmap_wf -def init_dwi_preproc_wf(subject_id, dwi_file, layout): +def init_dwi_preproc_wf(subject_id, dwi_file, metadata, layout): from nipype.pipeline import engine as pe from nipype.interfaces import ( freesurfer as fs, @@ -12,26 +11,21 @@ def init_dwi_preproc_wf(subject_id, dwi_file, layout): io as nio, utility as niu, ) - from nipype import logging + + from ..fieldmap.base import init_sdc_prep_wf fmaps = [] fmaps = layout.get_fieldmap(dwi_file, return_list=True) for fmap in fmaps: - if fmap["suffix"] == "phase": - fmap_key = "phase1" - else: - fmap_key = fmap["suffix"] - fmap["metadata"] = layout.get_metadata(fmap[fmap_key]) - - if not fmaps: - raise Exception( - "No fieldmap images found for participant {}. " - "All workflows require fieldmap images".format(subject_id) - ) + # if fmap["suffix"] == "phase": + # fmap_key = "phase1" + # else: + # fmap_key = fmap["suffix"] + # fmap["metadata"] = layout.get_metadata(fmap[fmap_key]) + fmap["metadata"] = layout.get_metadata(fmap["suffix"]) - if fmaps[0]["suffix"] == "fieldmap": - fmap_wf = init_fmap_wf() + sdc_wf = init_sdc_prep_wf(fmaps, metadata) dwi_wf = pe.Workflow(name="dwi_preproc_wf") @@ -43,8 +37,6 @@ def init_dwi_preproc_wf(subject_id, dwi_file, layout): "metadata", "bvec_file", "bval_file", - "fieldmap", - "magnitude" "out_dir", "eddy_niter", "slice_outlier_threshold", @@ -253,17 +245,9 @@ def get_b0_mask_fn(b0_file): (ecc, outputnode, [("out_corrected", "out_file")]), (b0mask_node, outputnode, [("mask_file", "out_mask")]), (ecc, outputnode, [("out_rotated_bvecs", "out_bvec")]), - ( - inputnode, - fmap_wf, - [ - ("fieldmap", "inputnode.fieldmap"), - ("magnitude", "inputnode.magnitude"), - ], - ), - (bet_dwi0, fmap_wf, [("out_file", "inputnode.b0_stripped")]), - (fmap_wf, ecc, [(("outputnode.out_fmap", get_path), "field")]), - (fmap_wf, eddy_quad, [(("outputnode.out_fmap", get_path), "field")]), + (bet_dwi0, sdc_wf, [("out_file", "inputnode.b0_stripped")]), + (sdc_wf, ecc, [(("outputnode.out_fmap", get_path), "field")]), + (sdc_wf, eddy_quad, [(("outputnode.out_fmap", get_path), "field")]), ] ) diff --git a/dmriprep/workflows/fieldmap/__init__.py b/dmriprep/workflows/fieldmap/__init__.py index b2738743..0af22be8 100644 --- a/dmriprep/workflows/fieldmap/__init__.py +++ b/dmriprep/workflows/fieldmap/__init__.py @@ -1,3 +1,4 @@ #!/usr/bin/env python +from .base import init_sdc_prep_wf from .fmap import init_fmap_wf diff --git a/dmriprep/workflows/fieldmap/base.py b/dmriprep/workflows/fieldmap/base.py new file mode 100644 index 00000000..3ec9e2a0 --- /dev/null +++ b/dmriprep/workflows/fieldmap/base.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python + +FMAP_PRIORITY = {"epi": 0, "fieldmap": 1, "phasediff": 2, "phase": 3, "syn": 4} + + +def init_sdc_prep_wf(fmaps, dwi_meta): + from nipype.pipeline import engine as pe + from nipype.interfaces import utility as niu + + sdc_prep_wf = pe.Workflow(name="sdc_prep_wf") + + inputnode = pe.Node(niu.IdentityInterface(fields=["b0_stripped"]), name="inputnode") + + outputnode = pe.Node(niu.IdentityInterface(fields=["out_fmap"]), name="outputnode") + + fmaps.sort(key=lambda fmap: FMAP_PRIORITY[fmap["suffix"]]) + fmap = fmaps[0] + + if fmap["suffix"] == "fieldmap": + from .fmap import init_fmap_wf + + fmap_wf = init_fmap_wf() + fmap_wf.inputs.inputnode.fieldmap = fmap["fieldmap"] + fmap_wf.inputs.inputnode.magnitude = fmap["magnitude"] + + sdc_prep_wf.connect( + [ + (inputnode, fmap_wf, [("b0_stripped", "inputnode.b0_stripped")]), + (fmap_wf, outputnode, [("outputnode.out_fmap", "out_fmap")]), + ] + ) + + return sdc_prep_wf diff --git a/dmriprep/workflows/fieldmap/fmap.py b/dmriprep/workflows/fieldmap/fmap.py index 6eccc565..64716f07 100644 --- a/dmriprep/workflows/fieldmap/fmap.py +++ b/dmriprep/workflows/fieldmap/fmap.py @@ -4,14 +4,10 @@ def init_fmap_wf(): from nipype.pipeline import engine as pe from nipype.interfaces import fsl, utility as niu - from nipype import logging - fmap_wf = pe.Workflow(name="fmap_prep_wf") + wf = pe.Workflow(name="fmap_prep_wf") - inputnode = pe.Node( - niu.IdentityInterface(fields=["fieldmap", "magnitude", "b0_stripped"]), - name="inputnode", - ) + inputnode = pe.Node(niu.IdentityInterface(fields=["fieldmap", "magnitude", "b0_stripped"]), name="inputnode") outputnode = pe.Node(niu.IdentityInterface(fields=["out_fmap"]), name="outputnode") @@ -23,7 +19,7 @@ def init_fmap_wf(): fmap_flirt = pe.Node(fsl.FLIRT(apply_xfm=True), name="fmapFlirt") - fmap_wf.connect( + wf.connect( [ (inputnode, rad_to_hz, [("fieldmap", "in_file")]), (inputnode, mag_flirt, [("magnitude", "in_file")]), @@ -35,4 +31,4 @@ def init_fmap_wf(): ] ) - return fmap_wf + return wf From 0d6402c285468a8a95e0af1e3d28735e04e8ec21 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Tue, 18 Jun 2019 10:41:17 -0400 Subject: [PATCH 023/156] [STY] name denoise and unring output files nicely --- dmriprep/workflows/dwi/base.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/dmriprep/workflows/dwi/base.py b/dmriprep/workflows/dwi/base.py index 89f9243f..81d8a757 100755 --- a/dmriprep/workflows/dwi/base.py +++ b/dmriprep/workflows/dwi/base.py @@ -1,5 +1,7 @@ #!/usr/bin/env python +import os + def init_dwi_preproc_wf(subject_id, dwi_file, metadata, layout): from nipype.pipeline import engine as pe @@ -11,6 +13,7 @@ def init_dwi_preproc_wf(subject_id, dwi_file, metadata, layout): io as nio, utility as niu, ) + from nipype.utils.filemanip import fname_presuffix from ..fieldmap.base import init_sdc_prep_wf @@ -50,14 +53,18 @@ def init_dwi_preproc_wf(subject_id, dwi_file, metadata, layout): name="outputnode", ) - # name noise and out_file using fname_presuffix - denoise = pe.Node( - mrtrix3.DWIDenoise(noise="noise.nii.gz", out_file="denoised.nii.gz"), - name="denoise", + denoise = pe.Node(mrtrix3.DWIDenoise(), name="denoise") + denoise.inputs.noise = fname_presuffix( + dwi_file, suffix="_noise", newpath=os.path.abspath("."), use_ext=True + ) + denoise.inputs.out_file = fname_presuffix( + dwi_file, suffix="_denoised", newpath=os.path.abspath("."), use_ext=True ) - # name unring using fname_presuffix - unring = pe.Node(mrtrix3.MRDeGibbs(out_file="unringed.nii.gz"), name="unring") + unring = pe.Node(mrtrix3.MRDeGibbs(), name="unring") + unring.inputs.out_file = fname_presuffix( + dwi_file, suffix="_unringed", newpath=os.path.abspath("."), use_ext=True + ) def gen_index(in_file): import os.path as op @@ -247,7 +254,7 @@ def get_b0_mask_fn(b0_file): (ecc, outputnode, [("out_rotated_bvecs", "out_bvec")]), (bet_dwi0, sdc_wf, [("out_file", "inputnode.b0_stripped")]), (sdc_wf, ecc, [(("outputnode.out_fmap", get_path), "field")]), - (sdc_wf, eddy_quad, [(("outputnode.out_fmap", get_path), "field")]), + (sdc_wf, eddy_quad, [("outputnode.out_fmap", "field")]), ] ) From f30abe16d4e0f0ea90fcb31ca80a58c125b6e177 Mon Sep 17 00:00:00 2001 From: Salim Mansour Date: Mon, 24 Jun 2019 15:43:15 -0400 Subject: [PATCH 024/156] Fix for eddy_quad input --- dmriprep/workflows/dwi/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dmriprep/workflows/dwi/base.py b/dmriprep/workflows/dwi/base.py index 89f9243f..ac5dc38a 100755 --- a/dmriprep/workflows/dwi/base.py +++ b/dmriprep/workflows/dwi/base.py @@ -247,7 +247,7 @@ def get_b0_mask_fn(b0_file): (ecc, outputnode, [("out_rotated_bvecs", "out_bvec")]), (bet_dwi0, sdc_wf, [("out_file", "inputnode.b0_stripped")]), (sdc_wf, ecc, [(("outputnode.out_fmap", get_path), "field")]), - (sdc_wf, eddy_quad, [(("outputnode.out_fmap", get_path), "field")]), + (sdc_wf, eddy_quad, [("outputnode.out_fmap", "field")]), ] ) From c70b8747f5dca6aef832a8cca3dc0fbfd4384fa8 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Mon, 24 Jun 2019 15:56:18 -0400 Subject: [PATCH 025/156] update denoise and unring filenames --- dmriprep/workflows/dwi/base.py | 32 ++- dmriprep/workflows/dwi/report.py | 448 ------------------------------- 2 files changed, 23 insertions(+), 457 deletions(-) delete mode 100644 dmriprep/workflows/dwi/report.py diff --git a/dmriprep/workflows/dwi/base.py b/dmriprep/workflows/dwi/base.py index 81d8a757..2c87bcaf 100755 --- a/dmriprep/workflows/dwi/base.py +++ b/dmriprep/workflows/dwi/base.py @@ -20,6 +20,12 @@ def init_dwi_preproc_wf(subject_id, dwi_file, metadata, layout): fmaps = [] fmaps = layout.get_fieldmap(dwi_file, return_list=True) + if not fmaps: + raise Exception( + "No fieldmaps found for participant {}. " + "All workflows require fieldmaps".format(subject_id) + ) + for fmap in fmaps: # if fmap["suffix"] == "phase": # fmap_key = "phase1" @@ -53,17 +59,25 @@ def init_dwi_preproc_wf(subject_id, dwi_file, metadata, layout): name="outputnode", ) - denoise = pe.Node(mrtrix3.DWIDenoise(), name="denoise") - denoise.inputs.noise = fname_presuffix( - dwi_file, suffix="_noise", newpath=os.path.abspath("."), use_ext=True - ) - denoise.inputs.out_file = fname_presuffix( - dwi_file, suffix="_denoised", newpath=os.path.abspath("."), use_ext=True + denoise = pe.Node( + mrtrix3.DWIDenoise( + noise=fname_presuffix( + dwi_file, suffix="_noise", newpath=os.path.abspath("."), use_ext=True + ), + out_file=fname_presuffix( + dwi_file, suffix="_denoised", newpath=os.path.abspath("."), use_ext=True + ), + ), + name="denoise", ) - unring = pe.Node(mrtrix3.MRDeGibbs(), name="unring") - unring.inputs.out_file = fname_presuffix( - dwi_file, suffix="_unringed", newpath=os.path.abspath("."), use_ext=True + unring = pe.Node( + mrtrix3.MRDeGibbs( + out_file=fname_presuffix( + dwi_file, suffix="_unringed", newpath=os.path.abspath("."), use_ext=True + ) + ), + name="unring", ) def gen_index(in_file): diff --git a/dmriprep/workflows/dwi/report.py b/dmriprep/workflows/dwi/report.py deleted file mode 100644 index 127123c3..00000000 --- a/dmriprep/workflows/dwi/report.py +++ /dev/null @@ -1,448 +0,0 @@ -#!/usr/bin/env python - - -# def init_dwi_report_wf(): -# -# import nipype.interfaces.freesurfer as fs -# import nipype.interfaces.fsl as fsl -# import nipype.interfaces.io as nio -# import nipype.interfaces.dipy as dipy -# import nipype.interfaces.utility as niu -# import nipype.pipeline.engine as pe -# -# inputspec = pe.Node(niu.IdentityInterface(fields=[ -# 'subject_id', -# 'dwi_file', -# 'dwi_file_ap', -# 'dwi_file_pa', -# 'bvec_file', -# 'bval_file', -# 'subjects_dir', -# 'out_dir', -# 'eddy_niter', -# 'slice_outlier_threshold' -# ]), name="inputspec") -# -# def id_outliers_fn(outlier_report, threshold, dwi_file): -# """Get list of scans that exceed threshold for number of outliers -# Parameters -# ---------- -# outlier_report: string -# Path to the fsl_eddy outlier report -# threshold: int or float -# If threshold is an int, it is treated as number of allowed outlier -# slices. If threshold is a float between 0 and 1 (exclusive), it is -# treated the fraction of allowed outlier slices before we drop the -# whole volume. -# dwi_file: string -# Path to nii dwi file to determine total number of slices -# Returns -# ------- -# drop_scans: numpy.ndarray -# List of scan indices to drop -# """ -# import nibabel as nib -# import numpy as np -# import os.path as op -# import parse -# -# with open(op.abspath(outlier_report), 'r') as fp: -# lines = fp.readlines() -# -# p = parse.compile( -# "Slice {slice:d} in scan {scan:d} is an outlier with " -# "mean {mean_sd:f} standard deviations off, and mean " -# "squared {mean_sq_sd:f} standard deviations off." -# ) -# -# outliers = [p.parse(l).named for l in lines] -# scans = {d['scan'] for d in outliers} -# -# def num_outliers(scan, outliers): -# return len([d for d in outliers if d['scan'] == scan]) -# -# if 0 < threshold < 1: -# img = nib.load(dwi_file) -# try: -# threshold *= img.header.get_n_slices() -# except nib.spatialimages.HeaderDataError: -# print('WARNING. We are not sure which dimension has the ' -# 'slices in this image. So we are using the 3rd dim.', img.shape) -# threshold *= img.shape[2] -# -# drop_scans = np.array([ -# s for s in scans -# if num_outliers(s, outliers) > threshold -# ]) -# -# outpath = op.abspath("dropped_scans.txt") -# np.savetxt(outpath, drop_scans, fmt="%d") -# -# return drop_scans, outpath -# -# id_outliers_node = pe.Node(niu.Function( -# input_names=["outlier_report", "threshold", "dwi_file"], -# output_names=["drop_scans", "outpath"], -# function=id_outliers_fn), -# name="id_outliers_node" -# ) -# -# wf.connect(inputspec, 'dwi_file', id_outliers_node, 'dwi_file') -# wf.connect(inputspec, 'slice_outlier_threshold', id_outliers_node, 'threshold') -# -# wf.connect(prep, "fsl_eddy.out_outlier_report", -# id_outliers_node, "outlier_report") -# -# fslroi = pe.Node(fsl.ExtractROI(t_min=0, t_size=1), name="fslroi") -# wf.connect(prep, "outputnode.out_file", fslroi, "in_file") -# -# bbreg = pe.Node(fs.BBRegister(contrast_type="t2", init="coreg", -# out_fsl_file=True, -# # subjects_dir=subjects_dir, -# epi_mask=True), -# name="bbreg") -# bbreg.inputs.subject_id = 'freesurfer' # bids_sub_name -# wf.connect(fslroi, "roi_file", bbreg, "source_file") -# wf.connect(inputspec, 'subjects_dir', bbreg, 'subjects_dir') -# -# def drop_outliers_fn(in_file, in_bval, in_bvec, drop_scans): -# """Drop outlier volumes from dwi file -# Parameters -# ---------- -# in_file: string -# Path to nii dwi file to drop outlier volumes from -# in_bval: string -# Path to bval file to drop outlier volumes from -# in_bvec: string -# Path to bvec file to drop outlier volumes from -# drop_scans: numpy.ndarray -# List of scan indices to drop -# Returns -# ------- -# out_file: string -# Path to "thinned" output dwi file -# out_bval: string -# Path to "thinned" output bval file -# out_bvec: string -# Path to "thinned" output bvec file -# """ -# import nibabel as nib -# import numpy as np -# import os.path as op -# from nipype.utils.filemanip import fname_presuffix -# -# img = nib.load(op.abspath(in_file)) -# img_data = img.get_data() -# img_data_thinned = np.delete(img_data, -# drop_scans, -# axis=3) -# if isinstance(img, nib.nifti1.Nifti1Image): -# img_thinned = nib.Nifti1Image(img_data_thinned.astype(np.float64), -# img.affine, -# header=img.header) -# elif isinstance(img, nib.nifti2.Nifti2Image): -# img_thinned = nib.Nifti2Image(img_data_thinned.astype(np.float64), -# img.affine, -# header=img.header) -# else: -# raise TypeError("in_file does not contain Nifti image datatype.") -# -# out_file = fname_presuffix(in_file, suffix="_thinned", newpath=op.abspath('.')) -# nib.save(img_thinned, op.abspath(out_file)) -# -# bval = np.loadtxt(in_bval) -# bval_thinned = np.delete(bval, drop_scans, axis=0) -# out_bval = fname_presuffix(in_bval, suffix="_thinned", newpath=op.abspath('.')) -# np.savetxt(out_bval, bval_thinned) -# -# bvec = np.loadtxt(in_bvec) -# bvec_thinned = np.delete(bvec, drop_scans, axis=1) -# out_bvec = fname_presuffix(in_bvec, suffix="_thinned", newpath=op.abspath('.')) -# np.savetxt(out_bvec, bvec_thinned) -# -# return out_file, out_bval, out_bvec -# -# drop_outliers_node = pe.Node(niu.Function( -# input_names=["in_file", "in_bval", "in_bvec", "drop_scans"], -# output_names=["out_file", "out_bval", "out_bvec"], -# function=drop_outliers_fn), -# name="drop_outliers_node" -# ) -# -# # Align the output of drop_outliers_node & also the eddy corrected version to the anatomical space -# # without resampling. and then for aparc+aseg & the mask, resample to the larger voxel size of the B0 image from -# # fslroi. Also we need to apply the transformation to both bvecs (dropped & eddied) and I think we can just load -# # the affine from bbreg (sio.loadmat) and np.dot(coord, aff) for each coord in bvec -# -# def get_orig(subjects_dir, sub='freesurfer'): -# import os.path as op -# return op.join(subjects_dir, sub, "mri", "orig.mgz") -# -# def get_aparc_aseg(subjects_dir, sub='freesurfer'): -# import os.path as op -# return op.join(subjects_dir, sub, "mri", "aparc+aseg.mgz") -# -# # transform the dropped volume version to anat space w/ out resampling -# voltransform = pe.Node(fs.ApplyVolTransform(no_resample=True), -# iterfield=['source_file', 'reg_file'], -# name='transform') -# -# wf.connect(inputspec, 'subjects_dir', voltransform, 'subjects_dir') -# wf.connect(inputspec, ('subjects_dir', get_aparc_aseg), voltransform, 'target_file') -# wf.connect(prep, "outputnode.out_file", voltransform, "source_file") -# wf.connect(bbreg, "out_reg_file", voltransform, "reg_file") -# -# def apply_transform_to_bvecs_fn(bvec_file, reg_mat_file): -# import numpy as np -# import nipype.utils.filemanip as fm -# import os -# -# aff = np.loadtxt(reg_mat_file) -# bvecs = np.loadtxt(bvec_file) -# bvec_trans = [] -# for bvec in bvecs.T: -# coord = np.hstack((bvec, [1])) -# coord_trans = np.dot(coord, aff)[:-1] -# bvec_trans.append(coord_trans) -# out_bvec = fm.fname_presuffix(bvec_file, suffix="anat_space", newpath=os.path.abspath('.')) -# np.savetxt(out_bvec, np.asarray(bvec_trans).T) -# return out_bvec -# -# apply_transform_to_bvecs_node = pe.Node(niu.Function(input_names=['bvec_file', 'reg_mat_file'], -# output_names=['out_bvec'], -# function=apply_transform_to_bvecs_fn), -# name="applyTransformToBvecs") -# wf.connect(bbreg, 'out_fsl_file', apply_transform_to_bvecs_node, 'reg_mat_file') -# wf.connect(prep, 'outputnode.out_bvec', apply_transform_to_bvecs_node, 'bvec_file') -# -# # ok cool, now lets do the thresholding. -# -# wf.connect(id_outliers_node, "drop_scans", drop_outliers_node, "drop_scans") -# wf.connect(voltransform, "transformed_file", drop_outliers_node, "in_file") -# wf.connect(inputspec, 'bval_file', drop_outliers_node, 'in_bval') -# wf.connect(apply_transform_to_bvecs_node, "out_bvec", drop_outliers_node, "in_bvec") -# -# # lets compute the tensor on both the dropped volume scan -# # and also the original, eddy corrected one. -# get_tensor = pe.Node(dipy.DTI(), name="dipy_tensor") -# wf.connect(drop_outliers_node, "out_file", get_tensor, "in_file") -# wf.connect(drop_outliers_node, "out_bval", get_tensor, "in_bval") -# wf.connect(drop_outliers_node, "out_bvec", get_tensor, "in_bvec") -# -# get_tensor_eddy = get_tensor.clone('dipy_tensor_eddy') -# wf.connect(voltransform, 'transformed_file', get_tensor_eddy, "in_file") -# wf.connect(apply_transform_to_bvecs_node, 'out_bvec', get_tensor_eddy, "in_bvec") -# wf.connect(inputspec, 'bval_file', get_tensor_eddy, 'in_bval') -# -# # AK: What is this, some vestigal node from a previous workflow? -# # I'm not sure why the tensor gets scaled. but i guess lets scale it for -# # both the dropped & eddy corrected versions. -# scale_tensor = pe.Node(name='scale_tensor', interface=fsl.BinaryMaths()) -# scale_tensor.inputs.operation = 'mul' -# scale_tensor.inputs.operand_value = 1000 -# wf.connect(get_tensor, 'out_file', scale_tensor, 'in_file') -# -# scale_tensor_eddy = scale_tensor.clone('scale_tensor_eddy') -# wf.connect(get_tensor_eddy, 'out_file', scale_tensor_eddy, 'in_file') -# -# # OK now that anatomical stuff (segmentation & mask) -# # We'll need: -# # 1. the B0 image in anat space (fslroi the 'transformed file' of voltransform -# # 2. the aparc aseg resampled-like the B0 image (mri_convert) -# # 3. the resample aparc_aseg binarized to be the mask image. -# -# def binarize_aparc(aparc_aseg): -# import nibabel as nib -# from nipype.utils.filemanip import fname_presuffix -# import os.path as op -# -# img = nib.load(aparc_aseg) -# data, aff = img.get_data(), img.affine -# outfile = fname_presuffix( -# aparc_aseg, suffix="bin.nii.gz", -# newpath=op.abspath("."), use_ext=False -# ) -# nib.Nifti1Image((data > 0).astype(float), aff).to_filename(outfile) -# return outfile -# -# create_mask = pe.Node(niu.Function(input_names=["aparc_aseg"], -# output_names=["outfile"], -# function=binarize_aparc), -# name="bin_aparc") -# -# get_b0_anat = fslroi.clone('get_b0_anat') -# wf.connect(voltransform, 'transformed_file', get_b0_anat, 'in_file') -# -# # reslice the anat-space aparc+aseg to the DWI resolution -# reslice_to_dwi = pe.Node(fs.MRIConvert(resample_type="nearest"), -# name="reslice_to_dwi") -# wf.connect(get_b0_anat, 'roi_file', reslice_to_dwi, 'reslice_like') -# wf.connect(inputspec, ('subjects_dir', get_aparc_aseg), reslice_to_dwi, 'in_file') -# -# # also reslice the orig i suppose -# reslice_orig_to_dwi = reslice_to_dwi.clone('resliceT1wToDwi') -# wf.connect(inputspec, ('subjects_dir', get_orig), reslice_orig_to_dwi, 'in_file') -# # reslice_orig_to_dwi.inputs.in_file = get_orig(subjects_dir, 'freesurfer') -# reslice_orig_to_dwi.inputs.out_type = 'niigz' -# wf.connect(get_b0_anat, 'roi_file', reslice_orig_to_dwi, 'reslice_like') -# -# # we assume the freesurfer is the output of BIDS -# # so the freesurfer output is in /path/to/derivatives/sub-whatever/freesurfer -# # which means the subject_dir is /path/to/derivatives/sub-whatever -# # reslice_to_dwi.inputs.in_file = get_aparc_aseg(subjects_dir, 'freesurfer') -# -# # now we have a nice aparc+aseg still in anat space but resliced like the dwi file -# # lets create a mask file from it. -# -# wf.connect(reslice_to_dwi, 'out_file', create_mask, 'aparc_aseg') -# -# # save all the things -# datasink = pe.Node(nio.DataSink(), name="sinker") -# wf.connect(inputspec, 'out_dir', datasink, 'base_directory') -# wf.connect(inputspec, 'subject_id', datasink, 'container') -# -# wf.connect(drop_outliers_node, "out_file", datasink, "dmriprep.dwi.@thinned") -# wf.connect(drop_outliers_node, "out_bval", datasink, "dmriprep.dwi.@bval_thinned") -# wf.connect(drop_outliers_node, "out_bvec", datasink, "dmriprep.dwi.@bvec_thinned") -# -# # eddy corrected files -# wf.connect(prep, "outputnode.out_file", datasink, "dmriprep.dwi_eddy.@corrected") -# wf.connect(prep, "outputnode.out_bvec", datasink, "dmriprep.dwi_eddy.@rotated") -# wf.connect(inputspec, 'bval_file', datasink, 'dmriprep.dwi_eddy.@bval') -# -# # all the eddy stuff except the corrected files -# wf.connect(prep, "fsl_eddy.out_movement_rms", -# datasink, "dmriprep.qc.@eddyparamsrms") -# wf.connect(prep, "fsl_eddy.out_outlier_report", -# datasink, "dmriprep.qc.@eddyparamsreport") -# wf.connect(prep, "fsl_eddy.out_restricted_movement_rms", -# datasink, "dmriprep.qc.@eddyparamsrestrictrms") -# wf.connect(prep, "fsl_eddy.out_shell_alignment_parameters", -# datasink, "dmriprep.qc.@eddyparamsshellalign") -# wf.connect(prep, "fsl_eddy.out_parameter", -# datasink, "dmriprep.qc.@eddyparams") -# wf.connect(prep, "fsl_eddy.out_cnr_maps", -# datasink, "dmriprep.qc.@eddycndr") -# wf.connect(prep, "fsl_eddy.out_residuals", -# datasink, "dmriprep.qc.@eddyresid") -# -# # the file that told us which volumes to drop -# wf.connect(id_outliers_node, "outpath", datasink, "dmriprep.qc.@droppedscans") -# -# # the tensors of the dropped volumes dwi -# wf.connect(get_tensor, "out_file", datasink, "dmriprep.dti.@tensor") -# wf.connect(get_tensor, "fa_file", datasink, "dmriprep.dti.@fa") -# wf.connect(get_tensor, "md_file", datasink, "dmriprep.dti.@md") -# wf.connect(get_tensor, "ad_file", datasink, "dmriprep.dti.@ad") -# wf.connect(get_tensor, "rd_file", datasink, "dmriprep.dti.@rd") -# wf.connect(get_tensor, "color_fa_file", datasink, "dmriprep.dti.@colorfa") -# wf.connect(scale_tensor, "out_file", datasink, "dmriprep.dti.@scaled_tensor") -# -# # the tensors of the eddied volumes dwi -# wf.connect(get_tensor_eddy, "out_file", datasink, "dmriprep.dti_eddy.@tensor") -# wf.connect(get_tensor_eddy, "fa_file", datasink, "dmriprep.dti_eddy.@fa") -# wf.connect(get_tensor_eddy, "md_file", datasink, "dmriprep.dti_eddy.@md") -# wf.connect(get_tensor_eddy, "ad_file", datasink, "dmriprep.dti_eddy.@ad") -# wf.connect(get_tensor_eddy, "rd_file", datasink, "dmriprep.dti_eddy.@rd") -# wf.connect(get_tensor_eddy, "color_fa_file", datasink, "dmriprep.dti_eddy.@colorfa") -# wf.connect(scale_tensor_eddy, "out_file", datasink, "dmriprep.dti_eddy.@scaled_tensor") -# -# # all the eddy_quad stuff -# wf.connect(eddy_quad, 'qc_json', datasink, "dmriprep.qc.@eddyquad_json") -# wf.connect(eddy_quad, 'qc_pdf', datasink, "dmriprep.qc.@eddyquad_pdf") -# wf.connect(eddy_quad, 'avg_b_png', datasink, "dmriprep.qc.@eddyquad_bpng") -# wf.connect(eddy_quad, 'avg_b0_pe_png', -# datasink, "dmriprep.qc.@eddyquad_b0png") -# wf.connect(eddy_quad, 'cnr_png', datasink, "dmriprep.qc.@eddyquad_cnr") -# wf.connect(eddy_quad, 'vdm_png', datasink, "dmriprep.qc.@eddyquad_vdm") -# wf.connect(eddy_quad, 'residuals', datasink, 'dmriprep.qc.@eddyquad_resid') -# -# # anatomical registration stuff -# wf.connect(bbreg, "min_cost_file", datasink, "dmriprep.reg.@mincost") -# wf.connect(bbreg, "out_fsl_file", datasink, "dmriprep.reg.@fslfile") -# wf.connect(bbreg, "out_reg_file", datasink, "dmriprep.reg.@reg") -# -# # anatomical files resliced -# wf.connect(reslice_to_dwi, 'out_file', datasink, 'dmriprep.anat.@segmentation') -# wf.connect(create_mask, 'outfile', datasink, 'dmriprep.anat.@mask') -# wf.connect(reslice_orig_to_dwi, 'out_file', datasink, 'dmriprep.anat.@T1w') -# -# def report_fn(dwi_corrected_file, eddy_rms, eddy_report, -# color_fa_file, anat_mask_file, outlier_indices, -# eddy_qc_file): -# from dmriprep.qc import create_report_json -# -# report = create_report_json(dwi_corrected_file, eddy_rms, eddy_report, -# color_fa_file, anat_mask_file, outlier_indices, -# eddy_qc_file) -# return report -# -# report_node = pe.Node(niu.Function( -# input_names=['dwi_corrected_file', 'eddy_rms', -# 'eddy_report', 'color_fa_file', -# 'anat_mask_file', 'outlier_indices', 'eddy_qc_file'], -# output_names=['report'], -# function=report_fn -# ), name="reportJSON") -# -# # for the report, lets show the eddy corrected (full volume) image -# wf.connect(voltransform, "transformed_file", report_node, 'dwi_corrected_file') -# wf.connect(eddy_quad, 'qc_json', report_node, 'eddy_qc_file') -# -# # add the rms movement output from eddy -# wf.connect(prep, "fsl_eddy.out_movement_rms", report_node, 'eddy_rms') -# wf.connect(prep, "fsl_eddy.out_outlier_report", report_node, 'eddy_report') -# wf.connect(id_outliers_node, 'drop_scans', report_node, 'outlier_indices') -# -# # the mask file to check our registration, and the colorFA file go in the report -# wf.connect(create_mask, "outfile", report_node, 'anat_mask_file') -# wf.connect(get_tensor, "color_fa_file", report_node, 'color_fa_file') -# -# # save that report! -# wf.connect(report_node, 'report', datasink, 'dmriprep.report.@report') -# -# # this part is done last, to get the filenames *just right* -# # its super annoying. -# def name_files_nicely(dwi_file, subject_id): -# import os.path as op -# -# dwi_fname = op.split(dwi_file)[1].split(".nii.gz")[0] -# substitutions = [ -# ("vol0000_flirt_merged.nii.gz", dwi_fname + '.nii.gz'), -# ("stats.vol0000_flirt_merged.txt", dwi_fname + ".art.json"), -# ("motion_parameters.par", dwi_fname + ".motion.txt"), -# ("_rotated.bvec", ".bvec"), -# ("art.vol0000_flirt_merged_outliers.txt", dwi_fname + ".outliers.txt"), -# ("vol0000_flirt_merged", dwi_fname), -# ("_roi_bbreg_freesurfer", "_register"), -# ("dwi/eddy_corrected", "dwi/%s" % dwi_fname), -# ("dti/eddy_corrected", "dti/%s" % dwi_fname.replace("_dwi", "")), -# ("reg/eddy_corrected", "reg/%s" % dwi_fname.replace("_dwi", "")), -# ("aparc+aseg_outbin", dwi_fname.replace("_dwi", "_mask")), -# ("aparc+aseg_out", dwi_fname.replace("_dwi", "_aparc+aseg")), -# ("art.eddy_corrected_outliers", dwi_fname.replace("dwi", "outliers")), -# ("color_fa", "colorfa"), -# ("orig_out", dwi_fname.replace("_dwi", "_T1w")), -# ("stats.eddy_corrected", dwi_fname.replace("dwi", "artStats")), -# ("eddy_corrected.eddy_parameters", dwi_fname + ".eddy_parameters"), -# ("qc/eddy_corrected", "qc/" + dwi_fname), -# ("derivatives/dmriprep", "derivatives/{}/dmriprep".format(subject_id)), -# ("_rotatedanat_space_thinned", ""), -# ("_thinned", ""), -# ("eddy_corrected", dwi_fname), -# ("_warped", ""), -# ("_maths", "_scaled"), -# ("dropped_scans", dwi_fname.replace("_dwi", "_dwi_dropped_scans")), -# ("report.json", dwi_fname.replace("_dwi", "_dwi_report.json")) -# ] -# return substitutions -# -# node_name_files_nicely = pe.Node(niu.Function(input_names=['dwi_file', 'subject_id'], -# output_names=['substitutions'], -# function=name_files_nicely), -# name="name_files_nicely") -# wf.connect(inputspec, 'dwi_file', node_name_files_nicely, 'dwi_file') -# wf.connect(inputspec, 'subject_id', node_name_files_nicely, 'subject_id') -# wf.connect(node_name_files_nicely, 'substitutions', datasink, 'substitutions') -# -# return wf From b75c5d62d7cfc1533b027df3d5ba3d5af348546e Mon Sep 17 00:00:00 2001 From: Salim Mansour Date: Mon, 24 Jun 2019 16:57:07 -0400 Subject: [PATCH 026/156] Added base files necessary for phdiff implementation --- dmriprep/interfaces/__init__.py | 3 + dmriprep/interfaces/fmap.py | 621 ++++++++++++++++++++++++++ dmriprep/workflows/base.py | 1 + dmriprep/workflows/dwi/base.py | 2 +- dmriprep/workflows/fieldmap/base.py | 74 ++- dmriprep/workflows/fieldmap/phdiff.py | 435 ++++++++++++++++++ dmriprep/workflows/fieldmap/unwarp.py | 331 ++++++++++++++ 7 files changed, 1464 insertions(+), 3 deletions(-) create mode 100644 dmriprep/interfaces/__init__.py create mode 100644 dmriprep/interfaces/fmap.py create mode 100644 dmriprep/workflows/fieldmap/phdiff.py create mode 100644 dmriprep/workflows/fieldmap/unwarp.py diff --git a/dmriprep/interfaces/__init__.py b/dmriprep/interfaces/__init__.py new file mode 100644 index 00000000..0319100e --- /dev/null +++ b/dmriprep/interfaces/__init__.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python + +from .fmap import FieldEnhance, FieldToRadS, FieldToHz, Phasediff2Fieldmap, Phases2Fieldmap diff --git a/dmriprep/interfaces/fmap.py b/dmriprep/interfaces/fmap.py new file mode 100644 index 00000000..71c96cc2 --- /dev/null +++ b/dmriprep/interfaces/fmap.py @@ -0,0 +1,621 @@ +#!/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: +""" +Interfaces to deal with the various types of fieldmap sources + + .. testsetup:: + + >>> tmpdir = getfixture('tmpdir') + >>> tmp = tmpdir.chdir() # changing to a temporary directory + >>> nb.Nifti1Image(np.zeros((90, 90, 60)), None, None).to_filename( + ... tmpdir.join('epi.nii.gz').strpath) + + +""" + +import numpy as np +import nibabel as nb +from nipype import logging +from nipype.utils.filemanip import fname_presuffix +from nipype.interfaces.base import ( + BaseInterfaceInputSpec, TraitedSpec, File, isdefined, traits, + SimpleInterface, InputMultiObject) + +LOGGER = logging.getLogger('nipype.interface') + + +class FieldEnhanceInputSpec(BaseInterfaceInputSpec): + in_file = File(exists=True, mandatory=True, desc='input fieldmap') + in_mask = File(exists=True, desc='brain mask') + in_magnitude = File(exists=True, desc='input magnitude') + unwrap = traits.Bool(False, usedefault=True, desc='run phase unwrap') + despike = traits.Bool(True, usedefault=True, desc='run despike filter') + bspline_smooth = traits.Bool(True, usedefault=True, desc='run 3D bspline smoother') + mask_erode = traits.Int(1, usedefault=True, desc='mask erosion iterations') + despike_threshold = traits.Float(0.2, usedefault=True, desc='mask erosion iterations') + num_threads = traits.Int(1, usedefault=True, nohash=True, desc='number of jobs') + + +class FieldEnhanceOutputSpec(TraitedSpec): + out_file = File(desc='the output fieldmap') + out_unwrapped = File(desc='unwrapped fieldmap') + + +class FieldEnhance(SimpleInterface): + """ + The FieldEnhance interface wraps a workflow to massage the input fieldmap + and return it masked, despiked, etc. + """ + input_spec = FieldEnhanceInputSpec + output_spec = FieldEnhanceOutputSpec + + def _run_interface(self, runtime): + from scipy import ndimage as sim + + fmap_nii = nb.load(self.inputs.in_file) + data = np.squeeze(fmap_nii.get_data().astype(np.float32)) + + # Despike / denoise (no-mask) + if self.inputs.despike: + data = _despike2d(data, self.inputs.despike_threshold) + + mask = None + if isdefined(self.inputs.in_mask): + masknii = nb.load(self.inputs.in_mask) + mask = masknii.get_data().astype(np.uint8) + + # Dilate mask + if self.inputs.mask_erode > 0: + struc = sim.iterate_structure(sim.generate_binary_structure(3, 2), 1) + mask = sim.binary_erosion( + mask, struc, + iterations=self.inputs.mask_erode + ).astype(np.uint8) # pylint: disable=no-member + + self._results['out_file'] = fname_presuffix( + self.inputs.in_file, suffix='_enh', newpath=runtime.cwd) + datanii = nb.Nifti1Image(data, fmap_nii.affine, fmap_nii.header) + + if self.inputs.unwrap: + data = _unwrap(data, self.inputs.in_magnitude, mask) + self._results['out_unwrapped'] = fname_presuffix( + self.inputs.in_file, suffix='_unwrap', newpath=runtime.cwd) + nb.Nifti1Image(data, fmap_nii.affine, fmap_nii.header).to_filename( + self._results['out_unwrapped']) + + if not self.inputs.bspline_smooth: + datanii.to_filename(self._results['out_file']) + return runtime + else: + from ..utils import bspline as fbsp + from statsmodels.robust.scale import mad + + # Fit BSplines (coarse) + bspobj = fbsp.BSplineFieldmap(datanii, weights=mask, + njobs=self.inputs.num_threads) + bspobj.fit() + smoothed1 = bspobj.get_smoothed() + + # Manipulate the difference map + diffmap = data - smoothed1.get_data() + sderror = mad(diffmap[mask > 0]) + LOGGER.info('SD of error after B-Spline fitting is %f', sderror) + errormask = np.zeros_like(diffmap) + errormask[np.abs(diffmap) > (10 * sderror)] = 1 + errormask *= mask + + nslices = 0 + try: + errorslice = np.squeeze(np.argwhere(errormask.sum(0).sum(0) > 0)) + nslices = errorslice[-1] - errorslice[0] + except IndexError: # mask is empty, do not refine + pass + + if nslices > 1: + diffmapmsk = mask[..., errorslice[0]:errorslice[-1]] + diffmapnii = nb.Nifti1Image( + diffmap[..., errorslice[0]:errorslice[-1]] * diffmapmsk, + datanii.affine, datanii.header) + + bspobj2 = fbsp.BSplineFieldmap(diffmapnii, knots_zooms=[24., 24., 4.], + njobs=self.inputs.num_threads) + bspobj2.fit() + smoothed2 = bspobj2.get_smoothed().get_data() + + final = smoothed1.get_data().copy() + final[..., errorslice[0]:errorslice[-1]] += smoothed2 + else: + final = smoothed1.get_data() + + nb.Nifti1Image(final, datanii.affine, datanii.header).to_filename( + self._results['out_file']) + + return runtime + + +class FieldToRadSInputSpec(BaseInterfaceInputSpec): + in_file = File(exists=True, mandatory=True, desc='input fieldmap') + fmap_range = traits.Float(desc='range of input field map') + + +class FieldToRadSOutputSpec(TraitedSpec): + out_file = File(desc='the output fieldmap') + fmap_range = traits.Float(desc='range of input field map') + + +class FieldToRadS(SimpleInterface): + """ + The FieldToRadS converts from arbitrary units to rad/s + """ + input_spec = FieldToRadSInputSpec + output_spec = FieldToRadSOutputSpec + + def _run_interface(self, runtime): + fmap_range = None + if isdefined(self.inputs.fmap_range): + fmap_range = self.inputs.fmap_range + self._results['out_file'], self._results['fmap_range'] = _torads( + self.inputs.in_file, fmap_range, newpath=runtime.cwd) + return runtime + + +class FieldToHzInputSpec(BaseInterfaceInputSpec): + in_file = File(exists=True, mandatory=True, desc='input fieldmap') + range_hz = traits.Float(mandatory=True, desc='range of input field map') + + +class FieldToHzOutputSpec(TraitedSpec): + out_file = File(desc='the output fieldmap') + + +class FieldToHz(SimpleInterface): + """ + The FieldToHz converts from arbitrary units to Hz + """ + input_spec = FieldToHzInputSpec + output_spec = FieldToHzOutputSpec + + def _run_interface(self, runtime): + self._results['out_file'] = _tohz( + self.inputs.in_file, self.inputs.range_hz, newpath=runtime.cwd) + return runtime + + +class Phasediff2FieldmapInputSpec(BaseInterfaceInputSpec): + in_file = File(exists=True, mandatory=True, desc='input fieldmap') + metadata = traits.Dict(mandatory=True, desc='BIDS metadata dictionary') + + +class Phasediff2FieldmapOutputSpec(TraitedSpec): + out_file = File(desc='the output fieldmap') + + +class Phasediff2Fieldmap(SimpleInterface): + """ + Convert a phase difference map into a fieldmap in Hz + """ + input_spec = Phasediff2FieldmapInputSpec + output_spec = Phasediff2FieldmapOutputSpec + + def _run_interface(self, runtime): + self._results['out_file'] = phdiff2fmap( + self.inputs.in_file, + _delta_te(self.inputs.metadata), + newpath=runtime.cwd) + return runtime + + +class Phases2FieldmapInputSpec(BaseInterfaceInputSpec): + phase_files = InputMultiObject( + File(exists=True), mandatory=True, desc='list of phase1, phase2 files') + metadatas = traits.List( + traits.Dict, mandatory=True, desc='list of phase1, phase2 metadata dicts') + + +class Phases2FieldmapOutputSpec(TraitedSpec): + out_file = File(desc='the output fieldmap') + phasediff_metadata = traits.Dict(desc='the phasediff metadata') + + +class Phases2Fieldmap(SimpleInterface): + """ + Convert a phase1, phase2 into a difference map + """ + input_spec = Phases2FieldmapInputSpec + output_spec = Phases2FieldmapOutputSpec + + def _run_interface(self, runtime): + # Get the echo times + fmap_file, merged_metadata = phases2fmap(self.inputs.phase_files, self.inputs.metadatas, + newpath=runtime.cwd) + self._results['phasediff_metadata'] = merged_metadata + self._results['out_file'] = fmap_file + return runtime + + +def _despike2d(data, thres, neigh=None): + """ + despiking as done in FSL fugue + """ + + if neigh is None: + neigh = [-1, 0, 1] + nslices = data.shape[-1] + + for k in range(nslices): + data2d = data[..., k] + + for i in range(data2d.shape[0]): + for j in range(data2d.shape[1]): + vals = [] + thisval = data2d[i, j] + for ii in neigh: + for jj in neigh: + try: + vals.append(data2d[i + ii, j + jj]) + except IndexError: + pass + vals = np.array(vals) + patch_range = vals.max() - vals.min() + patch_med = np.median(vals) + + if (patch_range > 1e-6 and + (abs(thisval - patch_med) / patch_range) > thres): + data[i, j, k] = patch_med + return data + + +def _unwrap(fmap_data, mag_file, mask=None): + from math import pi + from nipype.interfaces.fsl import PRELUDE + magnii = nb.load(mag_file) + + if mask is None: + mask = np.ones_like(fmap_data, dtype=np.uint8) + + fmapmax = max(abs(fmap_data[mask > 0].min()), fmap_data[mask > 0].max()) + fmap_data *= pi / fmapmax + + nb.Nifti1Image(fmap_data, magnii.affine).to_filename('fmap_rad.nii.gz') + nb.Nifti1Image(mask, magnii.affine).to_filename('fmap_mask.nii.gz') + nb.Nifti1Image(magnii.get_data(), magnii.affine).to_filename('fmap_mag.nii.gz') + + # Run prelude + res = PRELUDE(phase_file='fmap_rad.nii.gz', + magnitude_file='fmap_mag.nii.gz', + mask_file='fmap_mask.nii.gz').run() + + unwrapped = nb.load(res.outputs.unwrapped_phase_file).get_data() * (fmapmax / pi) + return unwrapped + + +def get_ees(in_meta, in_file=None): + """ + Calculate the *effective echo spacing* :math:`t_\\text{ees}` + for an input :abbr:`EPI (echo-planar imaging)` scan. + + + There are several procedures to calculate the effective + echo spacing. The basic one is that an ``EffectiveEchoSpacing`` + field is set in the JSON sidecar. The following examples + use an ``'epi.nii.gz'`` file-stub which has 90 pixels in the + j-axis encoding direction. + + >>> meta = {'EffectiveEchoSpacing': 0.00059, + ... 'PhaseEncodingDirection': 'j-'} + >>> get_ees(meta) + 0.00059 + + If the *total readout time* :math:`T_\\text{ro}` (``TotalReadoutTime`` + BIDS field) is provided, then the effective echo spacing can be + calculated reading the number of voxels :math:`N_\\text{PE}` along the + readout direction and the parallel acceleration + factor of the EPI + + .. math :: + + = T_\\text{ro} \\, (N_\\text{PE} / f_\\text{acc} - 1)^{-1} + + where :math:`N_y` is the number of pixels along the phase-encoding direction + :math:`y`, and :math:`f_\\text{acc}` is the parallel imaging acceleration factor + (:abbr:`GRAPPA (GeneRalized Autocalibrating Partial Parallel Acquisition)`, + :abbr:`ARC (Autocalibrating Reconstruction for Cartesian imaging)`, etc.). + + >>> meta = {'TotalReadoutTime': 0.02596, + ... 'PhaseEncodingDirection': 'j-', + ... 'ParallelReductionFactorInPlane': 2} + >>> get_ees(meta, in_file='epi.nii.gz') + 0.00059 + + Some vendors, like Philips, store different parameter names + (see http://dbic.dartmouth.edu/pipermail/mrusers/attachments/\ +20141112/eb1d20e6/attachment.pdf): + + >>> meta = {'WaterFatShift': 8.129, + ... 'MagneticFieldStrength': 3, + ... 'PhaseEncodingDirection': 'j-', + ... 'ParallelReductionFactorInPlane': 2} + >>> get_ees(meta, in_file='epi.nii.gz') + 0.00041602630141921826 + + """ + + import nibabel as nb + from fmriprep.interfaces.fmap import _get_pe_index + + # Use case 1: EES is defined + ees = in_meta.get('EffectiveEchoSpacing', None) + if ees is not None: + return ees + + # All other cases require the parallel acc and npe (N vox in PE dir) + acc = float(in_meta.get('ParallelReductionFactorInPlane', 1.0)) + npe = nb.load(in_file).shape[_get_pe_index(in_meta)] + etl = npe // acc + + # Use case 2: TRT is defined + trt = in_meta.get('TotalReadoutTime', None) + if trt is not None: + return trt / (etl - 1) + + # Use case 3 (philips scans) + wfs = in_meta.get('WaterFatShift', None) + if wfs is not None: + fstrength = in_meta['MagneticFieldStrength'] + wfd_ppm = 3.4 # water-fat diff in ppm + g_ratio_mhz_t = 42.57 # gyromagnetic ratio for proton (1H) in MHz/T + wfs_hz = fstrength * wfd_ppm * g_ratio_mhz_t + return wfs / (wfs_hz * etl) + + raise ValueError('Unknown effective echo-spacing specification') + + +def get_trt(in_meta, in_file=None): + """ + Calculate the *total readout time* for an input + :abbr:`EPI (echo-planar imaging)` scan. + + + There are several procedures to calculate the total + readout time. The basic one is that a ``TotalReadoutTime`` + field is set in the JSON sidecar. The following examples + use an ``'epi.nii.gz'`` file-stub which has 90 pixels in the + j-axis encoding direction. + + >>> meta = {'TotalReadoutTime': 0.02596} + >>> get_trt(meta) + 0.02596 + + If the *effective echo spacing* :math:`t_\\text{ees}` + (``EffectiveEchoSpacing`` BIDS field) is provided, then the + total readout time can be calculated reading the number + of voxels along the readout direction :math:`T_\\text{ro}` + and the parallel acceleration factor of the EPI :math:`f_\\text{acc}`. + + .. math :: + + T_\\text{ro} = t_\\text{ees} \\, (N_\\text{PE} / f_\\text{acc} - 1) + + >>> meta = {'EffectiveEchoSpacing': 0.00059, + ... 'PhaseEncodingDirection': 'j-', + ... 'ParallelReductionFactorInPlane': 2} + >>> get_trt(meta, in_file='epi.nii.gz') + 0.02596 + + Some vendors, like Philips, store different parameter names: + + >>> meta = {'WaterFatShift': 8.129, + ... 'MagneticFieldStrength': 3, + ... 'PhaseEncodingDirection': 'j-', + ... 'ParallelReductionFactorInPlane': 2} + >>> get_trt(meta, in_file='epi.nii.gz') + 0.018721183563864822 + + """ + + # Use case 1: TRT is defined + trt = in_meta.get('TotalReadoutTime', None) + if trt is not None: + return trt + + # All other cases require the parallel acc and npe (N vox in PE dir) + acc = float(in_meta.get('ParallelReductionFactorInPlane', 1.0)) + npe = nb.load(in_file).shape[_get_pe_index(in_meta)] + etl = npe // acc + + # Use case 2: TRT is defined + ees = in_meta.get('EffectiveEchoSpacing', None) + if ees is not None: + return ees * (etl - 1) + + # Use case 3 (philips scans) + wfs = in_meta.get('WaterFatShift', None) + if wfs is not None: + fstrength = in_meta['MagneticFieldStrength'] + wfd_ppm = 3.4 # water-fat diff in ppm + g_ratio_mhz_t = 42.57 # gyromagnetic ratio for proton (1H) in MHz/T + wfs_hz = fstrength * wfd_ppm * g_ratio_mhz_t + return wfs / wfs_hz + + raise ValueError('Unknown total-readout time specification') + + +def _get_pe_index(meta): + pe = meta['PhaseEncodingDirection'] + try: + return {'i': 0, 'j': 1, 'k': 2}[pe[0]] + except KeyError: + raise RuntimeError('"%s" is an invalid PE string' % pe) + + +def _torads(in_file, fmap_range=None, newpath=None): + """ + Convert a field map to rad/s units + + If fmap_range is None, the range of the fieldmap + will be automatically calculated. + + Use fmap_range=0.5 to convert from Hz to rad/s + """ + from math import pi + import nibabel as nb + from nipype.utils.filemanip import fname_presuffix + + out_file = fname_presuffix(in_file, suffix='_rad', newpath=newpath) + fmapnii = nb.load(in_file) + fmapdata = fmapnii.get_data() + + if fmap_range is None: + fmap_range = max(abs(fmapdata.min()), fmapdata.max()) + fmapdata = fmapdata * (pi / fmap_range) + out_img = nb.Nifti1Image(fmapdata, fmapnii.affine, fmapnii.header) + out_img.set_data_dtype('float32') + out_img.to_filename(out_file) + return out_file, fmap_range + + +def _tohz(in_file, range_hz, newpath=None): + """Convert a field map to Hz units""" + from math import pi + import nibabel as nb + from nipype.utils.filemanip import fname_presuffix + + out_file = fname_presuffix(in_file, suffix='_hz', newpath=newpath) + fmapnii = nb.load(in_file) + fmapdata = fmapnii.get_data() + fmapdata = fmapdata * (range_hz / pi) + out_img = nb.Nifti1Image(fmapdata, fmapnii.affine, fmapnii.header) + out_img.set_data_dtype('float32') + out_img.to_filename(out_file) + return out_file + + +def phdiff2fmap(in_file, delta_te, newpath=None): + r""" + Converts the input phase-difference map into a fieldmap in Hz, + using the eq. (1) of [Hutton2002]_: + + .. math:: + + \Delta B_0 (\text{T}^{-1}) = \frac{\Delta \Theta}{2\pi\gamma \Delta\text{TE}} + + + In this case, we do not take into account the gyromagnetic ratio of the + proton (:math:`\gamma`), since it will be applied inside TOPUP: + + .. math:: + + \Delta B_0 (\text{Hz}) = \frac{\Delta \Theta}{2\pi \Delta\text{TE}} + + """ + import math + import numpy as np + import nibabel as nb + from nipype.utils.filemanip import fname_presuffix + # GYROMAG_RATIO_H_PROTON_MHZ = 42.576 + + out_file = fname_presuffix(in_file, suffix='_fmap', newpath=newpath) + image = nb.load(in_file) + data = (image.get_data().astype(np.float32) / (2. * math.pi * delta_te)) + nii = nb.Nifti1Image(data, image.affine, image.header) + nii.set_data_dtype(np.float32) + nii.to_filename(out_file) + return out_file + + +def phases2fmap(phase_files, metadatas, newpath=None): + """Calculates a phasediff from two phase images. Assumes monopolar + readout. """ + import numpy as np + import nibabel as nb + from nipype.utils.filemanip import fname_presuffix + from copy import deepcopy + + phasediff_file = fname_presuffix(phase_files[0], suffix='_phasediff', newpath=newpath) + echo_times = [meta.get("EchoTime") for meta in metadatas] + if None in echo_times or echo_times[0] == echo_times[1]: + raise RuntimeError() + # Determine the order of subtraction + short_echo_index = echo_times.index(min(echo_times)) + long_echo_index = echo_times.index(max(echo_times)) + + short_phase_image = phase_files[short_echo_index] + long_phase_image = phase_files[long_echo_index] + + image0 = nb.load(short_phase_image) + phase0 = image0.get_fdata() + image1 = nb.load(long_phase_image) + phase1 = image1.get_fdata() + + def rescale_image(img): + if np.any(img < -128): + # This happens sometimes on 7T fieldmaps + LOGGER.info("Found negative values in phase image: rescaling") + imax = img.max() + imin = img.min() + scaled = 2 * ((img - imin) / (imax - imin) - 0.5) + return np.pi * scaled + mask = img > 0 + imax = img.max() + imin = img.min() + max_check = imax - 4096 + if np.abs(max_check) > 10 or np.abs(imin) > 10: + LOGGER.warning("Phase image may be scaled incorrectly: check results") + return mask * (img / 2048 * np.pi - np.pi) + + # Calculate fieldmaps + rad0 = rescale_image(phase0) + rad1 = rescale_image(phase1) + a = np.cos(rad0) + b = np.sin(rad0) + c = np.cos(rad1) + d = np.sin(rad1) + fmap = -np.arctan2(b * c - a * d, a * c + b * d) + + phasediff_nii = nb.Nifti1Image(fmap, image0.affine) + phasediff_nii.set_data_dtype(np.float32) + phasediff_nii.to_filename(phasediff_file) + + merged_metadata = deepcopy(metadatas[0]) + del merged_metadata['EchoTime'] + merged_metadata['EchoTime1'] = float(echo_times[short_echo_index]) + merged_metadata['EchoTime2'] = float(echo_times[long_echo_index]) + + return phasediff_file, merged_metadata + + +def _delta_te(in_values, te1=None, te2=None): + r"""Read :math:`\Delta_\text{TE}` from BIDS metadata dict""" + if isinstance(in_values, float): + te2 = in_values + te1 = 0. + + if isinstance(in_values, dict): + te1 = in_values.get('EchoTime1') + te2 = in_values.get('EchoTime2') + + if not all((te1, te2)): + te2 = in_values.get('EchoTimeDifference') + te1 = 0 + + if isinstance(in_values, list): + te2, te1 = in_values + if isinstance(te1, list): + te1 = te1[1] + if isinstance(te2, list): + te2 = te2[1] + + # For convienience if both are missing we should give one error about them + if te1 is None and te2 is None: + raise RuntimeError('EchoTime1 and EchoTime2 metadata fields not found. ' + 'Please consult the BIDS specification.') + if te1 is None: + raise RuntimeError( + 'EchoTime1 metadata field not found. Please consult the BIDS specification.') + if te2 is None: + raise RuntimeError( + 'EchoTime2 metadata field not found. Please consult the BIDS specification.') + + return abs(float(te2) - float(te1)) diff --git a/dmriprep/workflows/base.py b/dmriprep/workflows/base.py index 644a8d07..ddbb04fb 100644 --- a/dmriprep/workflows/base.py +++ b/dmriprep/workflows/base.py @@ -76,6 +76,7 @@ def init_single_subject_wf(layout, subject_id, name, work_dir, output_dir): ds_inputspec.inputs.subject_id = subject_id ds_inputspec.inputs.session_id = session_id ds_inputspec.inputs.output_folder = output_dir + ds_inputspec.inputs.metadata = metadata wf_name = "sub_" + subject_id + "_ses_" + session_id + "_preproc_wf" full_wf = pe.Workflow(name=wf_name) diff --git a/dmriprep/workflows/dwi/base.py b/dmriprep/workflows/dwi/base.py index 2c87bcaf..f89a650e 100755 --- a/dmriprep/workflows/dwi/base.py +++ b/dmriprep/workflows/dwi/base.py @@ -34,7 +34,7 @@ def init_dwi_preproc_wf(subject_id, dwi_file, metadata, layout): # fmap["metadata"] = layout.get_metadata(fmap[fmap_key]) fmap["metadata"] = layout.get_metadata(fmap["suffix"]) - sdc_wf = init_sdc_prep_wf(fmaps, metadata) + sdc_wf = init_sdc_prep_wf(fmaps, metadata, layout) dwi_wf = pe.Workflow(name="dwi_preproc_wf") diff --git a/dmriprep/workflows/fieldmap/base.py b/dmriprep/workflows/fieldmap/base.py index 3ec9e2a0..165bb7d5 100644 --- a/dmriprep/workflows/fieldmap/base.py +++ b/dmriprep/workflows/fieldmap/base.py @@ -3,7 +3,7 @@ FMAP_PRIORITY = {"epi": 0, "fieldmap": 1, "phasediff": 2, "phase": 3, "syn": 4} -def init_sdc_prep_wf(fmaps, dwi_meta): +def init_sdc_prep_wf(fmaps, dwi_meta, layout, omp_nthreads=1, fmap_bspline=False): from nipype.pipeline import engine as pe from nipype.interfaces import utility as niu @@ -11,7 +11,22 @@ def init_sdc_prep_wf(fmaps, dwi_meta): inputnode = pe.Node(niu.IdentityInterface(fields=["b0_stripped"]), name="inputnode") - outputnode = pe.Node(niu.IdentityInterface(fields=["out_fmap"]), name="outputnode") + #outputnode = pe.Node(niu.IdentityInterface(fields=["out_fmap",]), name="outputnode") + + outputnode = pe.Node( + niu.IdentityInterface( + fields=[ + "out_fmap", + "bold_ref", + "bold_mask", + "bold_ref_brain", + "out_warp", + "syn_bold_ref", + "method" + ] + ), + name="outputnode", + ) fmaps.sort(key=lambda fmap: FMAP_PRIORITY[fmap["suffix"]]) fmap = fmaps[0] @@ -30,4 +45,59 @@ def init_sdc_prep_wf(fmaps, dwi_meta): ] ) + if fmap['suffix'] in ('phasediff', 'phase'): + from .phdiff import init_phdiff_wf + fmap_estimator_wf = init_phdiff_wf(omp_nthreads=omp_nthreads, + phasetype=fmap['suffix'], + layout=layout) + if fmap['suffix'] == 'phasediff': + fmap_estimator_wf.inputs.inputnode.phasediff = fmap['phasediff'] + elif fmap['suffix'] == 'phase': + # Check that fieldmap is not bipolar + fmap_polarity = fmap['metadata'].get('DiffusionScheme', None) + if fmap_polarity == 'Bipolar': + LOGGER.warning("Bipolar fieldmaps are not supported. Ignoring") + sdc_prep_wf.__postdesc__ = "" + outputnode.inputs.method = 'None' + sdc_prep_wf.connect([ + (inputnode, outputnode, [('bold_ref', 'bold_ref'), + ('bold_mask', 'bold_mask'), + ('bold_ref_brain', 'bold_ref_brain')]), + ]) + return sdc_prep_wf + if fmap_polarity is None: + LOGGER.warning("Assuming phase images are Monopolar") + + fmap_estimator_wf.inputs.inputnode.phasediff = [fmap['phase1'], fmap['phase2']] + + fmap_estimator_wf.inputs.inputnode.magnitude = [ + fmap_ for key, fmap_ in sorted(fmap.items()) + if key.startswith("magnitude") + ] + + sdc_unwarp_wf = init_sdc_unwarp_wf( + omp_nthreads=omp_nthreads, + fmap_demean=fmap_demean, + debug=debug, + name='sdc_unwarp_wf') + sdc_unwarp_wf.inputs.inputnode.metadata = bold_meta + + sdc_prep_wf.connect([ + (inputnode, sdc_unwarp_wf, [ + ('bold_ref', 'inputnode.in_reference'), + ('bold_ref_brain', 'inputnode.in_reference_brain'), + ('bold_mask', 'inputnode.in_mask')]), + (fmap_estimator_wf, sdc_unwarp_wf, [ + ('outputnode.fmap', 'inputnode.fmap'), + ('outputnode.fmap_ref', 'inputnode.fmap_ref'), + ('outputnode.fmap_mask', 'inputnode.fmap_mask')]), + ]) + + sdc_prep_wf.connect([ + (sdc_unwarp_wf, outputnode, [ + ('outputnode.out_warp', 'out_warp'), + ('outputnode.out_reference', 'bold_ref'), + ('outputnode.out_reference_brain', 'bold_ref_brain'), + ('outputnode.out_mask', 'bold_mask')]), + ]) return sdc_prep_wf diff --git a/dmriprep/workflows/fieldmap/phdiff.py b/dmriprep/workflows/fieldmap/phdiff.py new file mode 100644 index 00000000..82ff08ea --- /dev/null +++ b/dmriprep/workflows/fieldmap/phdiff.py @@ -0,0 +1,435 @@ +#!/usr/bin/env python + +""" +.. _sdc_phasediff : +Phase-difference B0 estimation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The field inhomogeneity inside the scanner (fieldmap) is proportional to the +phase drift between two subsequent :abbr:`GRE (gradient recall echo)` +sequence. +Fieldmap preprocessing workflow for fieldmap data structure +8.9.1 in BIDS 1.0.0: one phase diff and at least one magnitude image +8.9.2 in BIDS 1.0.0: two phases and at least one magnitude image +""" + +from nipype.interfaces import ants, fsl, utility as niu +from nipype.pipeline import engine as pe +from nipype.workflows.dmri.fsl.utils import siemens2rads, demean_image, \ + cleanup_edge_pipeline +#from niworkflows.engine.workflows import LiterateWorkflow as Workflow +#from niworkflows.interfaces.bids import ReadSidecarJSON +#from niworkflows.interfaces.images import IntraModalMerge +#from niworkflows.interfaces.masks import BETRPT + +from ...interfaces import Phasediff2Fieldmap, Phases2Fieldmap + +def init_phdiff_wf(omp_nthreads, layout, phasetype='phasediff', name='phdiff_wf'): + """ + Estimates the fieldmap using a phase-difference image and one or more + magnitude images corresponding to two or more :abbr:`GRE (Gradient Echo sequence)` + acquisitions. The `original code was taken from nipype + `_. + .. workflow :: + :graph2use: orig + :simple_form: yes + from fmriprep.workflows.fieldmap.phdiff import init_phdiff_wf + wf = init_phdiff_wf(omp_nthreads=1) + Outputs:: + outputnode.fmap_ref - The average magnitude image, skull-stripped + outputnode.fmap_mask - The brain mask applied to the fieldmap + outputnode.fmap - The estimated fieldmap in Hz + """ + + workflow = pe.Workflow(name=name) + ''' + workflow.__desc__ = """\ +A deformation field to correct for susceptibility distortions was estimated +based on a field map that was co-registered to the BOLD reference, +using a custom workflow of *fMRIPrep* derived from D. Greve's `epidewarp.fsl` +[script](http://www.nmr.mgh.harvard.edu/~greve/fbirn/b0/epidewarp.fsl) and +further improvements of HCP Pipelines [@hcppipelines]. +""" + ''' + inputnode = pe.Node(niu.IdentityInterface(fields=['magnitude', 'phasediff']), + name='inputnode') + + outputnode = pe.Node(niu.IdentityInterface( + fields=['fmap', 'fmap_ref', 'fmap_mask']), name='outputnode') + + # Merge input magnitude images + magmrg = pe.Node(IntraModalMerge(), name='magmrg') + + # de-gradient the fields ("bias/illumination artifact") + n4 = pe.Node(ants.N4BiasFieldCorrection(dimension=3, copy_header=True), + name='n4', n_procs=omp_nthreads) + #bet = pe.Node(BETRPT(generate_report=True, frac=0.6, mask=True), + # name='bet') + bet = pe.Node(fsl.BET(frac=0.6, mask=True), + name='bet') + 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') + + # FSL PRELUDE will perform phase-unwrapping + prelude = pe.Node(fsl.PRELUDE(), name='prelude') + + denoise = pe.Node(fsl.SpatialFilter(operation='median', kernel_shape='sphere', + kernel_size=3), name='denoise') + + demean = pe.Node(niu.Function(function=demean_image), name='demean') + + cleanup_wf = cleanup_edge_pipeline(name="cleanup_wf") + + compfmap = pe.Node(Phasediff2Fieldmap(), name='compfmap') + compfmap.inputs.metadata = metadata + + # The phdiff2fmap interface is equivalent to: + # rad2rsec (using rads2radsec from nipype.workflows.dmri.fsl.utils) + # pre_fugue = pe.Node(fsl.FUGUE(save_fmap=True), name='ComputeFieldmapFUGUE') + # rsec2hz (divide by 2pi) + + if phasetype == "phasediff": + # Read phasediff echo times + meta = pe.Node(ReadSidecarJSON(bids_validate=False), name='meta', mem_gb=0.01) + + # phase diff -> radians + pha2rads = pe.Node(niu.Function(function=siemens2rads), + name='pha2rads') + # Read phasediff echo times + meta = pe.Node(ReadSidecarJSON(), name='meta', mem_gb=0.01, + run_without_submitting=True) + workflow.connect([ + #(meta, compfmap, [('out_dict', 'metadata')]), + (inputnode, pha2rads, [('phasediff', 'in_file')]), + (pha2rads, prelude, [('out', 'phase_file')]), + (inputnode, ds_report_fmap_mask, [('phasediff', 'source_file')]), + ]) + + elif phasetype == "phase": + workflow.__desc__ += """\ +The phase difference used for unwarping was calculated using two separate phase measurements + [@pncprocessing]. + """ + # Special case for phase1, phase2 images + meta = pe.MapNode(ReadSidecarJSON(), name='meta', mem_gb=0.01, + run_without_submitting=True, iterfield=['in_file']) + phases2fmap = pe.Node(Phases2Fieldmap(), name='phases2fmap') + workflow.connect([ + (meta, phases2fmap, [('out_dict', 'metadatas')]), + (inputnode, phases2fmap, [('phasediff', 'phase_files')]), + (phases2fmap, prelude, [('out_file', 'phase_file')]), + (phases2fmap, compfmap, [('phasediff_metadata', 'metadata')]), + (phases2fmap, ds_report_fmap_mask, [('out_file', 'source_file')]) + ]) + + workflow.connect([ + (inputnode, meta, [('phasediff', 'in_file')]), + (inputnode, magmrg, [('magnitude', 'in_files')]), + (magmrg, n4, [('out_avg', 'input_image')]), + (n4, prelude, [('output_image', 'magnitude_file')]), + (n4, bet, [('output_image', 'in_file')]), + (bet, prelude, [('mask_file', 'mask_file')]), + (prelude, denoise, [('unwrapped_phase_file', 'in_file')]), + (denoise, demean, [('out_file', 'in_file')]), + (demean, cleanup_wf, [('out', 'inputnode.in_file')]), + (bet, cleanup_wf, [('mask_file', 'inputnode.in_mask')]), + (cleanup_wf, compfmap, [('outputnode.out_file', 'in_file')]), + (compfmap, outputnode, [('out_file', 'fmap')]), + (bet, outputnode, [('mask_file', 'fmap_mask'), + ('out_file', 'fmap_ref')]), + (bet, ds_report_fmap_mask, [('out_report', 'in_file')]), + ]) + + return workflow + +class ReadSidecarJSONInputSpec(BIDSBaseInputSpec): + in_file = File(exists=True, mandatory=True, desc='the input nifti file') + + +class ReadSidecarJSONOutputSpec(BIDSInfoOutputSpec): + out_dict = traits.Dict() + + +class ReadSidecarJSON(SimpleInterface): + input_spec = ReadSidecarJSONInputSpec + output_spec = ReadSidecarJSONOutputSpec + layout = None + _always_run = True + + def __init__(self, fields=None, undef_fields=False, **inputs): + super(ReadSidecarJSON, self).__init__(**inputs) + self._fields = fields or [] + if isinstance(self._fields, str): + self._fields = [self._fields] + + self._undef_fields = undef_fields + + def _outputs(self): + base = super(ReadSidecarJSON, self)._outputs() + if self._fields: + base = add_traits(base, self._fields) + return base + + def _run_interface(self, runtime): + self.layout = self.inputs.bids_dir or self.layout + self.layout = _init_layout(self.inputs.in_file, + self.layout, + self.inputs.bids_validate) + + # Fill in BIDS entities of the output ("*_id") + output_keys = list(BIDSInfoOutputSpec().get().keys()) + params = self.layout.parse_file_entities(self.inputs.in_file) + self._results = {key: params.get(key.split('_')[0], Undefined) + for key in output_keys} + + # Fill in metadata + metadata = self.layout.get_metadata(self.inputs.in_file) + self._results['out_dict'] = metadata + + # Set dynamic outputs if fields input is present + for fname in self._fields: + if not self._undef_fields and fname not in metadata: + raise KeyError( + 'Metadata field "%s" not found for file %s' % ( + fname, self.inputs.in_file)) + self._results[fname] = metadata.get(fname, Undefined) + return runtime + +class IntraModalMergeInputSpec(BaseInterfaceInputSpec): + in_files = InputMultiPath(File(exists=True), mandatory=True, + desc='input files') + hmc = traits.Bool(True, usedefault=True) + zero_based_avg = traits.Bool(True, usedefault=True) + to_ras = traits.Bool(True, usedefault=True) + + +class IntraModalMergeOutputSpec(TraitedSpec): + out_file = File(exists=True, desc='merged image') + out_avg = File(exists=True, desc='average image') + out_mats = OutputMultiPath(File(exists=True), desc='output matrices') + out_movpar = OutputMultiPath(File(exists=True), desc='output movement parameters') + + +class IntraModalMerge(SimpleInterface): + input_spec = IntraModalMergeInputSpec + output_spec = IntraModalMergeOutputSpec + + def _run_interface(self, runtime): + in_files = self.inputs.in_files + if not isinstance(in_files, list): + in_files = [self.inputs.in_files] + + # Generate output average name early + self._results['out_avg'] = fname_presuffix(self.inputs.in_files[0], + suffix='_avg', newpath=runtime.cwd) + + if self.inputs.to_ras: + in_files = [reorient(inf, newpath=runtime.cwd) + for inf in in_files] + + if len(in_files) == 1: + filenii = nb.load(in_files[0]) + filedata = filenii.get_data() + + # magnitude files can have an extra dimension empty + if filedata.ndim == 5: + sqdata = np.squeeze(filedata) + if sqdata.ndim == 5: + raise RuntimeError('Input image (%s) is 5D' % in_files[0]) + else: + in_files = [fname_presuffix(in_files[0], suffix='_squeezed', + newpath=runtime.cwd)] + nb.Nifti1Image(sqdata, filenii.affine, + filenii.header).to_filename(in_files[0]) + + if np.squeeze(nb.load(in_files[0]).get_data()).ndim < 4: + self._results['out_file'] = in_files[0] + self._results['out_avg'] = in_files[0] + # TODO: generate identity out_mats and zero-filled out_movpar + return runtime + in_files = in_files[0] + else: + magmrg = fsl.Merge(dimension='t', in_files=self.inputs.in_files) + in_files = magmrg.run().outputs.merged_file + mcflirt = fsl.MCFLIRT(cost='normcorr', save_mats=True, save_plots=True, + ref_vol=0, in_file=in_files) + mcres = mcflirt.run() + self._results['out_mats'] = mcres.outputs.mat_file + self._results['out_movpar'] = mcres.outputs.par_file + self._results['out_file'] = mcres.outputs.out_file + + hmcnii = nb.load(mcres.outputs.out_file) + hmcdat = hmcnii.get_data().mean(axis=3) + if self.inputs.zero_based_avg: + hmcdat -= hmcdat.min() + + nb.Nifti1Image( + hmcdat, hmcnii.affine, hmcnii.header).to_filename( + self._results['out_avg']) + + return runtime + +class DerivativesDataSinkInputSpec(DynamicTraitedSpec, BaseInterfaceInputSpec): + base_directory = traits.Directory( + desc='Path to the base directory for storing data.') + check_hdr = traits.Bool(True, usedefault=True, desc='fix headers of NIfTI outputs') + compress = traits.Bool(desc="force compression (True) or uncompression (False)" + " of the output file (default: same as input)") + desc = Str('', usedefault=True, desc='Label for description field') + extra_values = traits.List(Str) + in_file = InputMultiObject(File(exists=True), mandatory=True, + desc='the object to be saved') + keep_dtype = traits.Bool(False, usedefault=True, desc='keep datatype suffix') + meta_dict = traits.DictStrAny(desc='an input dictionary containing metadata') + source_file = File(exists=False, mandatory=True, desc='the input func file') + space = Str('', usedefault=True, desc='Label for space field') + suffix = Str('', usedefault=True, desc='suffix appended to source_file') + + +class DerivativesDataSinkOutputSpec(TraitedSpec): + out_file = OutputMultiObject(File(exists=True, desc='written file path')) + out_meta = OutputMultiObject(File(exists=True, desc='written JSON sidecar path')) + compression = OutputMultiObject( + traits.Bool, desc='whether ``in_file`` was compressed/uncompressed ' + 'or `it was copied directly.') + fixed_hdr = traits.List(traits.Bool, desc='whether derivative header was fixed') + + +class DerivativesDataSink(SimpleInterface): + input_spec = DerivativesDataSinkInputSpec + output_spec = DerivativesDataSinkOutputSpec + out_path_base = "niworkflows" + _always_run = True + + def __init__(self, allowed_entities=None, out_path_base=None, **inputs): + self._allowed_entities = allowed_entities or [] + + self._metadata = {} + self._static_traits = self.input_spec.class_editable_traits() + self._allowed_entities + for dynamic_input in set(inputs) - set(self._static_traits): + self._metadata[dynamic_input] = inputs.pop(dynamic_input) + + super(DerivativesDataSink, self).__init__(**inputs) + if self._allowed_entities: + add_traits(self.inputs, self._allowed_entities) + for k in set(self._allowed_entities).intersection(list(inputs.keys())): + setattr(self.inputs, k, inputs[k]) + + self._results['out_file'] = [] + if out_path_base: + self.out_path_base = out_path_base + + def _run_interface(self, runtime): + if isdefined(self.inputs.meta_dict): + meta = self.inputs.meta_dict + # inputs passed in construction take priority + meta.update(self._metadata) + self._metadata = meta + + src_fname, _ = _splitext(self.inputs.source_file) + src_fname, dtype = src_fname.rsplit('_', 1) + _, ext = _splitext(self.inputs.in_file[0]) + if self.inputs.compress is True and not ext.endswith('.gz'): + ext += '.gz' + elif self.inputs.compress is False and ext.endswith('.gz'): + ext = ext[:-3] + + m = BIDS_NAME.search(src_fname) + + mod = Path(self.inputs.source_file).parent.name + + base_directory = runtime.cwd + if isdefined(self.inputs.base_directory): + base_directory = self.inputs.base_directory + + base_directory = Path(base_directory).resolve() + out_path = base_directory / self.out_path_base / \ + '{subject_id}'.format(**m.groupdict()) + + if m.groupdict().get('session_id') is not None: + out_path = out_path / '{session_id}'.format(**m.groupdict()) + + out_path = out_path / '{}'.format(mod) + out_path.mkdir(exist_ok=True, parents=True) + base_fname = str(out_path / src_fname) + + allowed_entities = {} + for key in self._allowed_entities: + value = getattr(self.inputs, key) + if value is not None and isdefined(value): + allowed_entities[key] = '_%s-%s' % (key, value) + + formatbase = '{bname}{space}{desc}' + ''.join( + [allowed_entities.get(s, '') for s in self._allowed_entities]) + + formatstr = formatbase + '{extra}{suffix}{dtype}{ext}' + if len(self.inputs.in_file) > 1 and not isdefined(self.inputs.extra_values): + formatstr = formatbase + '{suffix}{i:04d}{dtype}{ext}' + + space = '_space-{}'.format(self.inputs.space) if self.inputs.space else '' + desc = '_desc-{}'.format(self.inputs.desc) if self.inputs.desc else '' + suffix = '_{}'.format(self.inputs.suffix) if self.inputs.suffix else '' + dtype = '' if not self.inputs.keep_dtype else ('_%s' % dtype) + + self._results['compression'] = [] + self._results['fixed_hdr'] = [False] * len(self.inputs.in_file) + + for i, fname in enumerate(self.inputs.in_file): + extra = '' + if isdefined(self.inputs.extra_values): + extra = '_{}'.format(self.inputs.extra_values[i]) + out_file = formatstr.format( + bname=base_fname, + space=space, + desc=desc, + extra=extra, + suffix=suffix, + i=i, + dtype=dtype, + ext=ext, + ) + self._results['out_file'].append(out_file) + self._results['compression'].append(_copy_any(fname, out_file)) + + is_nii = out_file.endswith('.nii') or out_file.endswith('.nii.gz') + if self.inputs.check_hdr and is_nii: + nii = nb.load(out_file) + if not isinstance(nii, (nb.Nifti1Image, nb.Nifti2Image)): + # .dtseries.nii are CIfTI2, therefore skip check + return runtime + hdr = nii.header.copy() + curr_units = tuple([None if u == 'unknown' else u + for u in hdr.get_xyzt_units()]) + curr_codes = (int(hdr['qform_code']), int(hdr['sform_code'])) + + # Default to mm, use sec if data type is bold + units = (curr_units[0] or 'mm', 'sec' if dtype == '_bold' else None) + xcodes = (1, 1) # Derivative in its original scanner space + if self.inputs.space: + xcodes = (4, 4) if self.inputs.space in STANDARD_SPACES \ + else (2, 2) + + if curr_codes != xcodes or curr_units != units: + self._results['fixed_hdr'][i] = True + hdr.set_qform(nii.affine, xcodes[0]) + hdr.set_sform(nii.affine, xcodes[1]) + hdr.set_xyzt_units(*units) + + # Rewrite file with new header + nii.__class__(np.array(nii.dataobj), nii.affine, hdr).to_filename( + out_file) + + if len(self._results['out_file']) == 1: + meta_fields = self.inputs.copyable_trait_names() + self._metadata.update({ + k: getattr(self.inputs, k) + for k in meta_fields if k not in self._static_traits}) + if self._metadata: + sidecar = (Path(self._results['out_file'][0]).parent / + ('%s.json' % _splitext(self._results['out_file'][0])[0])) + sidecar.write_text(dumps(self._metadata, sort_keys=True, indent=2)) + self._results['out_meta'] = str(sidecar) + return runtime diff --git a/dmriprep/workflows/fieldmap/unwarp.py b/dmriprep/workflows/fieldmap/unwarp.py new file mode 100644 index 00000000..1456cf11 --- /dev/null +++ b/dmriprep/workflows/fieldmap/unwarp.py @@ -0,0 +1,331 @@ +#!/usr/bin/env python + +""" +.. _sdc_unwarp : + +Unwarping +~~~~~~~~~ + +.. topic :: Abbreviations + + fmap + fieldmap + VSM + voxel-shift map -- a 3D nifti where displacements are in pixels (not mm) + DFM + displacements field map -- a nifti warp file compatible with ANTs (mm) + +""" + +import pkg_resources as pkgr + +from nipype.pipeline import engine as pe +from nipype.interfaces import ants, fsl, utility as niu +from niworkflows.engine.workflows import LiterateWorkflow as Workflow +from niworkflows.interfaces import itk +from niworkflows.interfaces.images import DemeanImage, FilledImageLike +from niworkflows.interfaces.registration import ANTSApplyTransformsRPT, ANTSRegistrationRPT + +from ...interfaces import DerivativesDataSink +from ...interfaces.fmap import get_ees as _get_ees, FieldToRadS +from ..bold.util import init_enhance_and_skullstrip_bold_wf + +class LiterateWorkflow(pe.Workflow): + """Controls the setup and execution of a pipeline of processes.""" + + def __init__(self, name, base_dir=None): + """Create a workflow object. + Parameters + ---------- + name : alphanumeric string + unique identifier for the workflow + base_dir : string, optional + path to workflow storage + """ + super(LiterateWorkflow, self).__init__(name, base_dir) + self.__desc__ = None + self.__postdesc__ = None + + def visit_desc(self): + """ + Builds a citation boilerplate by visiting all workflows + appending their ``__desc__`` field + """ + desc = [] + + if self.__desc__: + desc += [self.__desc__] + + for node in pe.utils.topological_sort(self._graph)[0]: + if isinstance(node, LiterateWorkflow): + add_desc = node.visit_desc() + if add_desc not in desc: + desc.append(add_desc) + + if self.__postdesc__: + desc += [self.__postdesc__] + + return ''.join(desc) + +def init_sdc_unwarp_wf(omp_nthreads, fmap_demean, debug, name='sdc_unwarp_wf'): + """ + This workflow takes in a displacements fieldmap and calculates the corresponding + displacements field (in other words, an ANTs-compatible warp file). + + It also calculates a new mask for the input dataset that takes into account the distortions. + The mask is restricted to the field of view of the fieldmap since outside of it corrections + could not be performed. + + .. workflow :: + :graph2use: orig + :simple_form: yes + + from fmriprep.workflows.fieldmap.unwarp import init_sdc_unwarp_wf + wf = init_sdc_unwarp_wf(omp_nthreads=8, + fmap_demean=True, + debug=False) + + + Inputs + + in_reference + the reference image + in_reference_brain + the reference image (skull-stripped) + in_mask + a brain mask corresponding to ``in_reference`` + metadata + metadata associated to the ``in_reference`` EPI input + fmap + the fieldmap in Hz + fmap_ref + the reference (anatomical) image corresponding to ``fmap`` + fmap_mask + a brain mask corresponding to ``fmap`` + + + Outputs + + out_reference + the ``in_reference`` after unwarping + out_reference_brain + the ``in_reference`` after unwarping and skullstripping + out_warp + the corresponding :abbr:`DFM (displacements field map)` compatible with + ANTs + out_jacobian + the jacobian of the field (for drop-out alleviation) + out_mask + mask of the unwarped input file + + """ + + workflow = Workflow(name=name) + inputnode = pe.Node(niu.IdentityInterface( + fields=['in_reference', 'in_reference_brain', 'in_mask', 'metadata', + 'fmap_ref', 'fmap_mask', 'fmap']), name='inputnode') + outputnode = pe.Node(niu.IdentityInterface( + fields=['out_reference', 'out_reference_brain', 'out_warp', 'out_mask', + 'out_jacobian']), name='outputnode') + + # Register the reference of the fieldmap to the reference + # of the target image (the one that shall be corrected) + ants_settings = pkgr.resource_filename('fmriprep', 'data/fmap-any_registration.json') + if debug: + ants_settings = pkgr.resource_filename( + 'fmriprep', 'data/fmap-any_registration_testing.json') + fmap2ref_reg = pe.Node( + ANTSRegistrationRPT(generate_report=True, from_file=ants_settings, + output_inverse_warped_image=True, output_warped_image=True), + name='fmap2ref_reg', n_procs=omp_nthreads) + + 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( + generate_report=True, dimension=3, interpolation='BSpline', float=True), + name='fmap2ref_apply') + + fmap_mask2ref_apply = pe.Node(ANTSApplyTransformsRPT( + generate_report=False, dimension=3, interpolation='MultiLabel', + float=True), + name='fmap_mask2ref_apply') + + 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') + + get_ees = pe.Node(niu.Function(function=_get_ees, output_names=['ees']), name='get_ees') + + gen_vsm = pe.Node(fsl.FUGUE(save_unmasked_shift=True), name='gen_vsm') + # Convert the VSM into a DFM (displacements field map) + # or: FUGUE shift to ANTS warping. + vsm2dfm = pe.Node(itk.FUGUEvsm2ANTSwarp(), name='vsm2dfm') + jac_dfm = pe.Node(ants.CreateJacobianDeterminantImage( + imageDimension=3, outputImage='jacobian.nii.gz'), name='jac_dfm') + + unwarp_reference = pe.Node(ANTSApplyTransformsRPT(dimension=3, + generate_report=False, + float=True, + interpolation='LanczosWindowedSinc'), + name='unwarp_reference') + + fieldmap_fov_mask = pe.Node(FilledImageLike(dtype='uint8'), name='fieldmap_fov_mask') + + fmap_fov2ref_apply = pe.Node(ANTSApplyTransformsRPT( + generate_report=False, dimension=3, interpolation='NearestNeighbor', + float=True), + name='fmap_fov2ref_apply') + + apply_fov_mask = pe.Node(fsl.ApplyMask(), name="apply_fov_mask") + + enhance_and_skullstrip_bold_wf = init_enhance_and_skullstrip_bold_wf(omp_nthreads=omp_nthreads, + pre_mask=True) + + workflow.connect([ + (inputnode, fmap2ref_reg, [('fmap_ref', 'moving_image')]), + (inputnode, fmap2ref_apply, [('in_reference', 'reference_image')]), + (fmap2ref_reg, fmap2ref_apply, [ + ('composite_transform', 'transforms')]), + (inputnode, fmap_mask2ref_apply, [('in_reference', 'reference_image')]), + (fmap2ref_reg, fmap_mask2ref_apply, [ + ('composite_transform', 'transforms')]), + (fmap2ref_apply, ds_report_vsm, [('out_report', 'in_file')]), + (inputnode, fmap2ref_reg, [('in_reference_brain', 'fixed_image')]), + (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')]), + (inputnode, get_ees, [('in_reference', 'in_file'), + ('metadata', 'in_meta')]), + (fmap_mask2ref_apply, gen_vsm, [('output_image', 'mask_file')]), + (get_ees, gen_vsm, [('ees', 'dwell_time')]), + (inputnode, gen_vsm, [(('metadata', _get_pedir_fugue), 'unwarp_direction')]), + (inputnode, vsm2dfm, [(('metadata', _get_pedir_bids), 'pe_dir')]), + (torads, gen_vsm, [('out_file', 'fmap_in_file')]), + (vsm2dfm, unwarp_reference, [('out_file', 'transforms')]), + (inputnode, unwarp_reference, [('in_reference', 'reference_image')]), + (inputnode, unwarp_reference, [('in_reference', 'input_image')]), + (vsm2dfm, outputnode, [('out_file', 'out_warp')]), + (vsm2dfm, jac_dfm, [('out_file', 'deformationField')]), + (inputnode, fieldmap_fov_mask, [('fmap_ref', 'in_file')]), + (fieldmap_fov_mask, fmap_fov2ref_apply, [('out_file', 'input_image')]), + (inputnode, fmap_fov2ref_apply, [('in_reference', 'reference_image')]), + (fmap2ref_reg, fmap_fov2ref_apply, [('composite_transform', 'transforms')]), + (fmap_fov2ref_apply, apply_fov_mask, [('output_image', 'mask_file')]), + (unwarp_reference, apply_fov_mask, [('output_image', 'in_file')]), + (apply_fov_mask, enhance_and_skullstrip_bold_wf, [('out_file', 'inputnode.in_file')]), + (fmap_mask2ref_apply, enhance_and_skullstrip_bold_wf, + [('output_image', 'inputnode.pre_mask')]), + (apply_fov_mask, outputnode, [('out_file', 'out_reference')]), + (enhance_and_skullstrip_bold_wf, outputnode, [ + ('outputnode.mask_file', 'out_mask'), + ('outputnode.skull_stripped_file', 'out_reference_brain')]), + (jac_dfm, outputnode, [('jacobian_image', 'out_jacobian')]), + ]) + + if fmap_demean: + # Demean within mask + demean = pe.Node(DemeanImage(), name='demean') + + workflow.connect([ + (gen_vsm, demean, [('shift_out_file', 'in_file')]), + (fmap_mask2ref_apply, demean, [('output_image', 'in_mask')]), + (demean, vsm2dfm, [('out_file', 'in_file')]), + ]) + + else: + workflow.connect([ + (gen_vsm, vsm2dfm, [('shift_out_file', 'in_file')]), + ]) + + return workflow + + +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. + + .. workflow:: + :graph2use: orig + :simple_form: yes + + from fmriprep.workflows.fieldmap.unwarp import init_fmap_unwarp_report_wf + wf = init_fmap_unwarp_report_wf() + + **Parameters** + + name : str, optional + Workflow name (default: fmap_unwarp_report_wf) + forcedsyn : bool, optional + Whether SyN-SDC was forced. + + **Inputs** + + in_pre + Reference image, before unwarping + in_post + Reference image, after unwarping + in_seg + Segmentation of preprocessed structural image, including + gray-matter (GM), white-matter (WM) and cerebrospinal fluid (CSF) + in_xfm + Affine transform from T1 space to BOLD space (ITK format) + + """ + from niworkflows.interfaces import SimpleBeforeAfter + from niworkflows.interfaces.fixes import FixHeaderApplyTransforms as ApplyTransforms + from niworkflows.interfaces.images import extract_wm + + DEFAULT_MEMORY_MIN_GB = 0.01 + + workflow = Workflow(name=name) + + inputnode = pe.Node(niu.IdentityInterface( + fields=['in_pre', 'in_post', 'in_seg', 'in_xfm']), name='inputnode') + + map_seg = pe.Node(ApplyTransforms( + dimension=3, float=True, interpolation='MultiLabel'), + name='map_seg', mem_gb=0.3) + + sel_wm = pe.Node(niu.Function(function=extract_wm), name='sel_wm', + mem_gb=DEFAULT_MEMORY_MIN_GB) + + bold_rpt = pe.Node(SimpleBeforeAfter(), name='bold_rpt', + mem_gb=0.1) + ds_report_sdc = pe.Node( + DerivativesDataSink(desc='sdc' if not forcedsyn else 'forcedsyn', + suffix='bold'), name='ds_report_sdc', + mem_gb=DEFAULT_MEMORY_MIN_GB, run_without_submitting=True + ) + + workflow.connect([ + (inputnode, bold_rpt, [('in_post', 'after'), + ('in_pre', 'before')]), + (bold_rpt, ds_report_sdc, [('out_report', 'in_file')]), + (inputnode, map_seg, [('in_post', 'reference_image'), + ('in_seg', 'input_image'), + ('in_xfm', 'transforms')]), + (map_seg, sel_wm, [('output_image', 'in_seg')]), + (sel_wm, bold_rpt, [('out', 'wm_seg')]), + ]) + + return workflow + + +# Helper functions +# ------------------------------------------------------------ + + +def _get_pedir_bids(in_dict): + return in_dict['PhaseEncodingDirection'] + + +def _get_pedir_fugue(in_dict): + return in_dict['PhaseEncodingDirection'].replace('i', 'x').replace('j', 'y').replace('k', 'z') From bf663f4d165fa5c76745df3905d94356855e4ce6 Mon Sep 17 00:00:00 2001 From: Salim Mansour Date: Tue, 25 Jun 2019 15:52:32 -0400 Subject: [PATCH 027/156] Replaced metadata and output nodes in phdiff workflows --- dmriprep/workflows/fieldmap/base.py | 6 +-- dmriprep/workflows/fieldmap/phdiff.py | 75 +++++++++++++++++++-------- 2 files changed, 56 insertions(+), 25 deletions(-) diff --git a/dmriprep/workflows/fieldmap/base.py b/dmriprep/workflows/fieldmap/base.py index 165bb7d5..89d4a641 100644 --- a/dmriprep/workflows/fieldmap/base.py +++ b/dmriprep/workflows/fieldmap/base.py @@ -3,7 +3,7 @@ FMAP_PRIORITY = {"epi": 0, "fieldmap": 1, "phasediff": 2, "phase": 3, "syn": 4} -def init_sdc_prep_wf(fmaps, dwi_meta, layout, omp_nthreads=1, fmap_bspline=False): +def init_sdc_prep_wf(fmaps, metadata, layout, omp_nthreads=1, fmap_bspline=False): from nipype.pipeline import engine as pe from nipype.interfaces import utility as niu @@ -48,8 +48,8 @@ def init_sdc_prep_wf(fmaps, dwi_meta, layout, omp_nthreads=1, fmap_bspline=False if fmap['suffix'] in ('phasediff', 'phase'): from .phdiff import init_phdiff_wf fmap_estimator_wf = init_phdiff_wf(omp_nthreads=omp_nthreads, - phasetype=fmap['suffix'], - layout=layout) + phasetype=fmap['suffix']) + fmap_estimator_wf.inputs.inputnode.layout = layout if fmap['suffix'] == 'phasediff': fmap_estimator_wf.inputs.inputnode.phasediff = fmap['phasediff'] elif fmap['suffix'] == 'phase': diff --git a/dmriprep/workflows/fieldmap/phdiff.py b/dmriprep/workflows/fieldmap/phdiff.py index 82ff08ea..2f5256c7 100644 --- a/dmriprep/workflows/fieldmap/phdiff.py +++ b/dmriprep/workflows/fieldmap/phdiff.py @@ -16,6 +16,12 @@ from nipype.pipeline import engine as pe from nipype.workflows.dmri.fsl.utils import siemens2rads, demean_image, \ cleanup_edge_pipeline +from nipype.interfaces.base import ( + traits, isdefined, Undefined, + TraitedSpec, BaseInterfaceInputSpec, DynamicTraitedSpec, + File, Directory, InputMultiObject, OutputMultiObject, Str, + SimpleInterface, InputMultiPath, OutputMultiPath +) #from niworkflows.engine.workflows import LiterateWorkflow as Workflow #from niworkflows.interfaces.bids import ReadSidecarJSON #from niworkflows.interfaces.images import IntraModalMerge @@ -23,7 +29,7 @@ from ...interfaces import Phasediff2Fieldmap, Phases2Fieldmap -def init_phdiff_wf(omp_nthreads, layout, phasetype='phasediff', name='phdiff_wf'): +def init_phdiff_wf(omp_nthreads, phasetype='phasediff', name='phdiff_wf'): """ Estimates the fieldmap using a phase-difference image and one or more magnitude images corresponding to two or more :abbr:`GRE (Gradient Echo sequence)` @@ -50,7 +56,7 @@ def init_phdiff_wf(omp_nthreads, layout, phasetype='phasediff', name='phdiff_wf' further improvements of HCP Pipelines [@hcppipelines]. """ ''' - inputnode = pe.Node(niu.IdentityInterface(fields=['magnitude', 'phasediff']), + inputnode = pe.Node(niu.IdentityInterface(fields=['magnitude', 'phasediff', 'layout']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface( @@ -66,9 +72,10 @@ def init_phdiff_wf(omp_nthreads, layout, phasetype='phasediff', name='phdiff_wf' # name='bet') bet = pe.Node(fsl.BET(frac=0.6, mask=True), name='bet') - ds_report_fmap_mask = pe.Node(DerivativesDataSink( - desc='brain', suffix='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') @@ -84,7 +91,6 @@ def init_phdiff_wf(omp_nthreads, layout, phasetype='phasediff', name='phdiff_wf' cleanup_wf = cleanup_edge_pipeline(name="cleanup_wf") compfmap = pe.Node(Phasediff2Fieldmap(), name='compfmap') - compfmap.inputs.metadata = metadata # The phdiff2fmap interface is equivalent to: # rad2rsec (using rads2radsec from nipype.workflows.dmri.fsl.utils) @@ -93,40 +99,60 @@ def init_phdiff_wf(omp_nthreads, layout, phasetype='phasediff', name='phdiff_wf' if phasetype == "phasediff": # Read phasediff echo times - meta = pe.Node(ReadSidecarJSON(bids_validate=False), name='meta', mem_gb=0.01) + #meta = pe.Node(ReadSidecarJSON(bids_validate=False), name='meta', mem_gb=0.01) # phase diff -> radians pha2rads = pe.Node(niu.Function(function=siemens2rads), name='pha2rads') # Read phasediff echo times - meta = pe.Node(ReadSidecarJSON(), name='meta', mem_gb=0.01, - run_without_submitting=True) + #meta = pe.Node(ReadSidecarJSON(bids_validate=False), name='meta', mem_gb=0.01, + # run_without_submitting=True) + + meta = pe.Node( + niu.Function( + input_names=["in_file", "in_layout"], output_names=["out_dict"], function=get_metadata + ), + name="meta", + ) + workflow.connect([ - #(meta, compfmap, [('out_dict', 'metadata')]), + (meta, compfmap, [('out_dict', 'metadata')]), (inputnode, pha2rads, [('phasediff', 'in_file')]), (pha2rads, prelude, [('out', 'phase_file')]), - (inputnode, ds_report_fmap_mask, [('phasediff', 'source_file')]), + #(inputnode, ds_report_fmap_mask, [('phasediff', 'source_file')]), ]) elif phasetype == "phase": - workflow.__desc__ += """\ -The phase difference used for unwarping was calculated using two separate phase measurements - [@pncprocessing]. - """ # Special case for phase1, phase2 images - meta = pe.MapNode(ReadSidecarJSON(), name='meta', mem_gb=0.01, - run_without_submitting=True, iterfield=['in_file']) + #meta = pe.MapNode(ReadSidecarJSON(), name='meta', mem_gb=0.01, + # run_without_submitting=True, iterfield=['in_file']) + + meta = pe.Node( + niu.Function( + input_names=["in_file", "in_layout"], output_names=["out_dict"], function=get_metadata + ), + name="meta", + ) + phases2fmap = pe.Node(Phases2Fieldmap(), name='phases2fmap') workflow.connect([ (meta, phases2fmap, [('out_dict', 'metadatas')]), (inputnode, phases2fmap, [('phasediff', 'phase_files')]), (phases2fmap, prelude, [('out_file', 'phase_file')]), (phases2fmap, compfmap, [('phasediff_metadata', 'metadata')]), - (phases2fmap, ds_report_fmap_mask, [('out_file', 'source_file')]) + #(phases2fmap, ds_report_fmap_mask, [('out_file', 'source_file')]) ]) workflow.connect([ - (inputnode, meta, [('phasediff', 'in_file')]), + #(inputnode, meta, [('phasediff', 'in_file')]), + ( + inputnode, + meta, + [ + ('phasediff', 'in_file'), + ('layout', 'in_layout'), + ], + ), (inputnode, magmrg, [('magnitude', 'in_files')]), (magmrg, n4, [('out_avg', 'input_image')]), (n4, prelude, [('output_image', 'magnitude_file')]), @@ -140,11 +166,15 @@ def init_phdiff_wf(omp_nthreads, layout, phasetype='phasediff', name='phdiff_wf' (compfmap, outputnode, [('out_file', 'fmap')]), (bet, outputnode, [('mask_file', 'fmap_mask'), ('out_file', 'fmap_ref')]), - (bet, ds_report_fmap_mask, [('out_report', 'in_file')]), + #(bet, ds_report_fmap_mask, [('out_report', 'in_file')]), ]) return workflow +def get_metadata(in_file, in_layout): + out_dict = in_layout.get_metadata(in_file) + return out_dict +''' class ReadSidecarJSONInputSpec(BIDSBaseInputSpec): in_file = File(exists=True, mandatory=True, desc='the input nifti file') @@ -197,7 +227,7 @@ def _run_interface(self, runtime): fname, self.inputs.in_file)) self._results[fname] = metadata.get(fname, Undefined) return runtime - +''' class IntraModalMergeInputSpec(BaseInterfaceInputSpec): in_files = InputMultiPath(File(exists=True), mandatory=True, desc='input files') @@ -271,7 +301,7 @@ def _run_interface(self, runtime): self._results['out_avg']) return runtime - +''' class DerivativesDataSinkInputSpec(DynamicTraitedSpec, BaseInterfaceInputSpec): base_directory = traits.Directory( desc='Path to the base directory for storing data.') @@ -433,3 +463,4 @@ def _run_interface(self, runtime): sidecar.write_text(dumps(self._metadata, sort_keys=True, indent=2)) self._results['out_meta'] = str(sidecar) return runtime +''' From 978a802480a386244836e81e921274eb5ea3f13f Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Tue, 25 Jun 2019 16:38:08 -0400 Subject: [PATCH 028/156] update requirements for EddyQuad fix in nipype --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 98f5fedd..6788228f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ Click>=6.0 dask==1.2.2 dipy==0.16.0 -nipype==1.2.0 +nipype==1.2.1 pandas==0.24.2 parse==1.12.0 tqdm==4.32.1 From 6589b4e59469b243cdf3e7f0627381050d724b99 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Tue, 25 Jun 2019 16:38:24 -0400 Subject: [PATCH 029/156] output into dmriprep folder --- dmriprep/workflows/dwi/datasink.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dmriprep/workflows/dwi/datasink.py b/dmriprep/workflows/dwi/datasink.py index 58500aae..af4455eb 100644 --- a/dmriprep/workflows/dwi/datasink.py +++ b/dmriprep/workflows/dwi/datasink.py @@ -31,7 +31,9 @@ def init_output_wf(subject_id, session_id, output_folder): def build_path(output_folder, subject_id, session_id): import os.path as op - return op.join(output_folder, "sub-" + subject_id, "ses-" + session_id, "dwi") + return op.join( + output_folder, "dmriprep", "sub-" + subject_id, "ses-" + session_id, "dwi" + ) concat = pe.Node( niu.Function( From afe56faab266ec669e2f2e9b03c17822b159893b Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Thu, 27 Jun 2019 18:39:53 -0400 Subject: [PATCH 030/156] fix denoise interface --- dmriprep/interfaces/__init__.py | 0 dmriprep/interfaces/mrtrix.py | 152 ++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 dmriprep/interfaces/__init__.py create mode 100644 dmriprep/interfaces/mrtrix.py diff --git a/dmriprep/interfaces/__init__.py b/dmriprep/interfaces/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/dmriprep/interfaces/mrtrix.py b/dmriprep/interfaces/mrtrix.py new file mode 100644 index 00000000..9dc585c1 --- /dev/null +++ b/dmriprep/interfaces/mrtrix.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python + +from __future__ import print_function, division, unicode_literals, absolute_import + +from nipype.interfaces.base import traits, TraitedSpec, File +from nipype.interfaces.mrtrix3.base import MRTrix3BaseInputSpec, MRTrix3Base + + +class DWIDenoiseInputSpec(MRTrix3BaseInputSpec): + in_file = File( + exists=True, argstr="%s", position=-2, mandatory=True, desc="input DWI image" + ) + mask = File(exists=True, argstr="-mask %s", position=1, desc="mask image") + extent = traits.Tuple( + (traits.Int, traits.Int, traits.Int), + argstr="-extent %d,%d,%d", + desc="set the window size of the denoising filter. (default = 5,5,5)", + ) + noise = File( + argstr="-noise %s", + name_template="%s_noise", + name_source=["in_file"], + keep_extension=True, + desc="the output noise map", + ) + out_file = File( + argstr="%s", + name_template="%s_denoised", + name_source=["in_file"], + keep_extension=True, + position=-1, + desc="the output denoised DWI image", + ) + + +class DWIDenoiseOutputSpec(TraitedSpec): + out_file = File(desc="the output denoised DWI image", exists=True) + noise = File(desc="the output noise map (if generated)", exists=True) + + +class DWIDenoise(MRTrix3Base): + """ + Denoise DWI data and estimate the noise level based on the optimal + threshold for PCA. + DWI data denoising and noise map estimation by exploiting data redundancy + in the PCA domain using the prior knowledge that the eigenspectrum of + random covariance matrices is described by the universal Marchenko Pastur + distribution. + Important note: image denoising must be performed as the first step of the + image processing pipeline. The routine will fail if interpolation or + smoothing has been applied to the data prior to denoising. + Note that this function does not correct for non-Gaussian noise biases. + For more information, see + + Example + ------- + >>> import nipype.interfaces.mrtrix3 as mrt + >>> denoise = mrt.DWIDenoise() + >>> denoise.inputs.in_file = 'dwi.mif' + >>> denoise.inputs.mask = 'mask.mif' + >>> denoise.cmdline # doctest: +ELLIPSIS + 'dwidenoise -mask mask.mif dwi.mif dwi_denoised.mif' + >>> denoise.run() # doctest: +SKIP + """ + + _cmd = "dwidenoise" + input_spec = DWIDenoiseInputSpec + output_spec = DWIDenoiseOutputSpec + + +class MRDeGibbsInputSpec(MRTrix3BaseInputSpec): + in_file = File( + exists=True, argstr="%s", position=-2, mandatory=True, desc="input DWI image" + ) + axes = traits.ListInt( + default_value=[0, 1], + usedefault=True, + sep=",", + minlen=2, + maxlen=2, + argstr="-axes %s", + desc="indicate the plane in which the data was acquired (axial = 0,1; " + "coronal = 0,2; sagittal = 1,2", + ) + nshifts = traits.Int( + default_value=20, + usedefault=True, + argstr="-nshifts %d", + desc="discretization of subpixel spacing (default = 20)", + ) + minW = traits.Int( + default_value=1, + usedefault=True, + argstr="-minW %d", + desc="left border of window used for total variation (TV) computation " + "(default = 1)", + ) + maxW = traits.Int( + default_value=3, + usedefault=True, + argstr="-maxW %d", + desc="right border of window used for total variation (TV) computation " + "(default = 3)", + ) + out_file = File( + name_template="%s_unr", + name_source="in_file", + keep_extension=True, + argstr="%s", + position=-1, + desc="the output unringed DWI image", + genfile=True, + ) + + +class MRDeGibbsOutputSpec(TraitedSpec): + out_file = File(desc="the output unringed DWI image", exists=True) + + +class MRDeGibbs(MRTrix3Base): + """ + Remove Gibbs ringing artifacts. + This application attempts to remove Gibbs ringing artefacts from MRI images + using the method of local subvoxel-shifts proposed by Kellner et al. + This command is designed to run on data directly after it has been + reconstructed by the scanner, before any interpolation of any kind has + taken place. You should not run this command after any form of motion + correction (e.g. not after dwipreproc). Similarly, if you intend running + dwidenoise, you should run this command afterwards, since it has the + potential to alter the noise structure, which would impact on dwidenoise's + performance. + Note that this method is designed to work on images acquired with full + k-space coverage. Running this method on partial Fourier ('half-scan') data + may lead to suboptimal and/or biased results, as noted in the original + reference below. There is currently no means of dealing with this; users + should exercise caution when using this method on partial Fourier data, and + inspect its output for any obvious artefacts. + For more information, see + + Example + ------- + >>> import nipype.interfaces.mrtrix3 as mrt + >>> unring = mrt.MRDeGibbs() + >>> unring.inputs.in_file = 'dwi.mif' + >>> unring.cmdline + 'mrdegibbs -axes 0,1 -maxW 3 -minW 1 -nshifts 20 dwi.mif dwi_unr.mif' + >>> unring.run() # doctest: +SKIP + """ + + _cmd = "mrdegibbs" + input_spec = MRDeGibbsInputSpec + output_spec = MRDeGibbsOutputSpec From 59680c56c1541ce02b5ff4c2e9555cae324bafe3 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Thu, 27 Jun 2019 18:40:53 -0400 Subject: [PATCH 031/156] add dtifit and denoise after eddy --- dmriprep/workflows/dwi/base.py | 49 +++++++++------------------------- 1 file changed, 12 insertions(+), 37 deletions(-) diff --git a/dmriprep/workflows/dwi/base.py b/dmriprep/workflows/dwi/base.py index 2c87bcaf..2218f864 100755 --- a/dmriprep/workflows/dwi/base.py +++ b/dmriprep/workflows/dwi/base.py @@ -1,20 +1,11 @@ #!/usr/bin/env python -import os - def init_dwi_preproc_wf(subject_id, dwi_file, metadata, layout): from nipype.pipeline import engine as pe - from nipype.interfaces import ( - freesurfer as fs, - fsl, - mrtrix3, - ants, - io as nio, - utility as niu, - ) - from nipype.utils.filemanip import fname_presuffix + from nipype.interfaces import fsl, utility as niu + from ...interfaces import mrtrix from ..fieldmap.base import init_sdc_prep_wf fmaps = [] @@ -27,11 +18,6 @@ def init_dwi_preproc_wf(subject_id, dwi_file, metadata, layout): ) for fmap in fmaps: - # if fmap["suffix"] == "phase": - # fmap_key = "phase1" - # else: - # fmap_key = fmap["suffix"] - # fmap["metadata"] = layout.get_metadata(fmap[fmap_key]) fmap["metadata"] = layout.get_metadata(fmap["suffix"]) sdc_wf = init_sdc_prep_wf(fmaps, metadata) @@ -59,26 +45,9 @@ def init_dwi_preproc_wf(subject_id, dwi_file, metadata, layout): name="outputnode", ) - denoise = pe.Node( - mrtrix3.DWIDenoise( - noise=fname_presuffix( - dwi_file, suffix="_noise", newpath=os.path.abspath("."), use_ext=True - ), - out_file=fname_presuffix( - dwi_file, suffix="_denoised", newpath=os.path.abspath("."), use_ext=True - ), - ), - name="denoise", - ) + denoise = pe.Node(mrtrix.DWIDenoise(), name="denoise") - unring = pe.Node( - mrtrix3.MRDeGibbs( - out_file=fname_presuffix( - dwi_file, suffix="_unringed", newpath=os.path.abspath("."), use_ext=True - ) - ), - name="unring", - ) + unring = pe.Node(mrtrix.MRDeGibbs(), name="unring") def gen_index(in_file): import os.path as op @@ -203,13 +172,13 @@ def b0_average(in_dwi, in_bval, b0_thresh=10.0, out_file=None): except: ecc.inputs.use_cuda = False + denoise_eddy = pe.Node(mrtrix.DWIDenoise(), name="denoise_eddy") + eddy_quad = pe.Node(fsl.EddyQuad(verbose=True), name="eddy_quad") get_path = lambda x: x.split(".nii.gz")[0].split("_fix")[0] get_qc_path = lambda x: x.split(".nii.gz")[0] + ".qc" - dwi_bias_corr = pe.Node(ants.N4BiasFieldCorrection(), name="dwi_bias_corr") - fslroi = pe.Node(fsl.ExtractROI(t_min=0, t_size=1), name="fslroi") def get_b0_mask_fn(b0_file): @@ -234,6 +203,8 @@ def get_b0_mask_fn(b0_file): name="getB0Mask", ) + dtifit = pe.Node(fsl.DTIFit(save_tensor=True, sse=True), name="dtifit") + dwi_wf.connect( [ (inputnode, denoise, [("dwi_file", "in_file")]), @@ -248,6 +219,7 @@ def get_b0_mask_fn(b0_file): (bet_dwi0, ecc, [("mask_file", "in_mask")]), (gen_idx, ecc, [("out_file", "in_index")]), (acqp, ecc, [("out_file", "in_acqp")]), + (ecc, denoise_eddy, [("out_corrected", "in_file")]), (ecc, fslroi, [("out_corrected", "in_file")]), (fslroi, b0mask_node, [("roi_file", "b0_file")]), ( @@ -269,6 +241,9 @@ def get_b0_mask_fn(b0_file): (bet_dwi0, sdc_wf, [("out_file", "inputnode.b0_stripped")]), (sdc_wf, ecc, [(("outputnode.out_fmap", get_path), "field")]), (sdc_wf, eddy_quad, [("outputnode.out_fmap", "field")]), + (ecc, dtifit, [("out_corrected", "dwi"), ("out_rotated_bvecs", "bvecs")]), + (b0mask_node, dtifit, [("mask_file", "mask")]), + (inputnode, dtifit, [("bval_file", "bvals")]), ] ) From 6365ef18324d784028f4a38102bc5e013a63d798 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Thu, 27 Jun 2019 18:41:04 -0400 Subject: [PATCH 032/156] update datasink --- dmriprep/workflows/base.py | 8 ++++++++ dmriprep/workflows/dwi/datasink.py | 25 +++++++++++++++++-------- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/dmriprep/workflows/base.py b/dmriprep/workflows/base.py index 644a8d07..ff92812d 100644 --- a/dmriprep/workflows/base.py +++ b/dmriprep/workflows/base.py @@ -89,10 +89,18 @@ def init_single_subject_wf(layout, subject_id, name, work_dir, output_dir): ("fsl_eddy.out_corrected", "inputnode.out_file"), ("fsl_eddy.out_rotated_bvecs", "inputnode.out_bvec"), ("getB0Mask.mask_file", "inputnode.out_mask"), + ("inputnode.bval_file", "inputnode.out_bval"), + ("bet_dwi_pre.out_file", "inputnode.out_b0_brain"), + ("bet_dwi_pre.mask_file", "inputnode.out_b0_mask"), + ("eddy_quad.qc_json", "inputnode.out_eddy_qc"), + ("dtifit.FA", "inputnode.out_FA"), + ("dtifit.V1", "inputnode.out_V1"), + ("denoise_eddy.noise", "inputnode.out_sh_residual"), ], ) ] ) + subject_wf.add_nodes([full_wf]) return subject_wf diff --git a/dmriprep/workflows/dwi/datasink.py b/dmriprep/workflows/dwi/datasink.py index af4455eb..30cd1bc0 100644 --- a/dmriprep/workflows/dwi/datasink.py +++ b/dmriprep/workflows/dwi/datasink.py @@ -3,14 +3,7 @@ def init_output_wf(subject_id, session_id, output_folder): from nipype.pipeline import engine as pe - from nipype.interfaces import ( - freesurfer as fs, - fsl, - mrtrix3, - io as nio, - utility as niu, - ) - from nipype import logging + from nipype.interfaces import io as nio, utility as niu op_wf = pe.Workflow(name="output_wf") @@ -22,6 +15,14 @@ def init_output_wf(subject_id, session_id, output_folder): "out_file", "out_mask", "out_bvec", + "out_bval", + "out_b0_brain", + "out_b0_mask", + "out_fieldmap_brain", + "out_eddy_qc", + "out_FA", + "out_V1", + "out_sh_residual", "output_folder", ] ), @@ -64,7 +65,15 @@ def build_path(output_folder, subject_id, session_id): [ ("out_file", "@result.@dwi"), ("out_bvec", "@result.@bvec"), + ("out_bval", "@result.@bval"), ("out_mask", "@result.@mask"), + ("out_b0_brain", "@result.@b0brain"), + ("out_b0_mask", "@result.@b0mask"), + # ("out_fieldmap_brain", "@result.@fmapbrain"), + ("out_eddy_qc", "@result.@eddyqc"), + ("out_FA", "@result.@fa"), + ("out_V1", "@result.@v1"), + ("out_sh_residual", "@result.@residual"), ], ), ] From f06e2ea9b3f74232fa6f416fd9332a4141c023dd Mon Sep 17 00:00:00 2001 From: Salim Mansour Date: Fri, 28 Jun 2019 10:20:50 -0400 Subject: [PATCH 033/156] Added new phasediff workflow suited for SIEMENS --- dmriprep/workflows/fieldmap/__init__.py | 1 + dmriprep/workflows/fieldmap/base.py | 36 ++++-- dmriprep/workflows/fieldmap/phasediff.py | 109 +++++++++++++++++++ dmriprep/workflows/fieldmap/phdiff.py | 72 +++++++++++- dmriprep/workflows/fieldmap/unwarp.py | 133 ++++++++++++++--------- 5 files changed, 287 insertions(+), 64 deletions(-) create mode 100644 dmriprep/workflows/fieldmap/phasediff.py diff --git a/dmriprep/workflows/fieldmap/__init__.py b/dmriprep/workflows/fieldmap/__init__.py index 0af22be8..cc9ce59d 100644 --- a/dmriprep/workflows/fieldmap/__init__.py +++ b/dmriprep/workflows/fieldmap/__init__.py @@ -2,3 +2,4 @@ from .base import init_sdc_prep_wf from .fmap import init_fmap_wf +from .phasediff import init_phase_wf diff --git a/dmriprep/workflows/fieldmap/base.py b/dmriprep/workflows/fieldmap/base.py index 89d4a641..790f55c3 100644 --- a/dmriprep/workflows/fieldmap/base.py +++ b/dmriprep/workflows/fieldmap/base.py @@ -46,18 +46,22 @@ def init_sdc_prep_wf(fmaps, metadata, layout, omp_nthreads=1, fmap_bspline=False ) if fmap['suffix'] in ('phasediff', 'phase'): - from .phdiff import init_phdiff_wf - fmap_estimator_wf = init_phdiff_wf(omp_nthreads=omp_nthreads, - phasetype=fmap['suffix']) - fmap_estimator_wf.inputs.inputnode.layout = layout + print("phase made") + #from .phdiff import init_phdiff_wf + from .phasediff import init_phase_wf + #fmap_estimator_wf = init_phdiff_wf(omp_nthreads=omp_nthreads, + # phasetype=fmap['suffix']) + phase_wf = init_phase_wf(layout) + #fmap_estimator_wf.inputs.inputnode.layout = layout if fmap['suffix'] == 'phasediff': - fmap_estimator_wf.inputs.inputnode.phasediff = fmap['phasediff'] + phase_wf.inputs.inputnode.phasediff = fmap['phasediff'] elif fmap['suffix'] == 'phase': + ''' # Check that fieldmap is not bipolar fmap_polarity = fmap['metadata'].get('DiffusionScheme', None) if fmap_polarity == 'Bipolar': - LOGGER.warning("Bipolar fieldmaps are not supported. Ignoring") - sdc_prep_wf.__postdesc__ = "" + #LOGGER.warning("Bipolar fieldmaps are not supported. Ignoring") + #sdc_prep_wf.__postdesc__ = "" outputnode.inputs.method = 'None' sdc_prep_wf.connect([ (inputnode, outputnode, [('bold_ref', 'bold_ref'), @@ -65,16 +69,24 @@ def init_sdc_prep_wf(fmaps, metadata, layout, omp_nthreads=1, fmap_bspline=False ('bold_ref_brain', 'bold_ref_brain')]), ]) return sdc_prep_wf - if fmap_polarity is None: - LOGGER.warning("Assuming phase images are Monopolar") + #if fmap_polarity is None: + #LOGGER.warning("Assuming phase images are Monopolar") + ''' - fmap_estimator_wf.inputs.inputnode.phasediff = [fmap['phase1'], fmap['phase2']] + phase_wf.inputs.inputnode.phasediff = [fmap['phase1'], fmap['phase2']] - fmap_estimator_wf.inputs.inputnode.magnitude = [ + phase_wf.inputs.inputnode.magnitude1 = [ fmap_ for key, fmap_ in sorted(fmap.items()) if key.startswith("magnitude") ] + sdc_prep_wf.connect( + [ + (phase_wf, outputnode, [("outputnode.out_fieldmap", "out_fmap")]) + ] + ) + + ''' sdc_unwarp_wf = init_sdc_unwarp_wf( omp_nthreads=omp_nthreads, fmap_demean=fmap_demean, @@ -100,4 +112,6 @@ def init_sdc_prep_wf(fmaps, metadata, layout, omp_nthreads=1, fmap_bspline=False ('outputnode.out_reference_brain', 'bold_ref_brain'), ('outputnode.out_mask', 'bold_mask')]), ]) + ''' + return sdc_prep_wf diff --git a/dmriprep/workflows/fieldmap/phasediff.py b/dmriprep/workflows/fieldmap/phasediff.py new file mode 100644 index 00000000..b0397557 --- /dev/null +++ b/dmriprep/workflows/fieldmap/phasediff.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python + +def init_phase_wf(layout): + from nipype.pipeline import engine as pe + from nipype.interfaces import fsl, utility as niu + from ...interfaces import Phasediff2Fieldmap, Phases2Fieldmap + + wf = pe.Workflow(name="phase_prep_wf") + + inputnode = pe.Node(niu.IdentityInterface(fields=["magnitude1", "phasediff", "b0_stripped"]), name="inputnode") + + outputnode = pe.Node(niu.IdentityInterface(fields=["out_fieldmap"]), name="outputnode") + + mag_bet = pe.Node(fsl.BET(frac=0.6, robust=True), + name='mag_bet') + + prep_fmap = pe.Node(fsl.PrepareFieldmap(scanner='SIEMENS'), + name='prep_fmap') + + rad_to_hz = pe.Node( + fsl.BinaryMaths(operation="div", operand_value=6.28), name="radToHz" + ) + + phases_meta = pe.Node( + niu.Function( + input_names=["in_file", "in_layout"], output_names=["out_dict"], function=get_metadata + ), + name="phases_meta", + ) + phases_meta.inputs.in_layout = layout + + phdiff_meta = pe.Node( + niu.Function( + input_names=["in_file", "in_layout"], output_names=["out_dict"], function=get_metadata + ), + name="phdiff_meta", + ) + phdiff_meta.inputs.in_layout = layout + + delta_te = pe.Node( + niu.Function( + input_names=["in_values"], output_names=["out_value"], function=_delta_te + ), + name="delta_te", + ) + + phases2fmap = pe.Node(Phases2Fieldmap(), name='phases2fmap') + + wf.connect( + [ + # Mag bet + (inputnode, mag_bet, [("magnitude1", "in_file")]), + # phases -> phdiff + (inputnode, phases_meta, [('out_dict', 'in_file')]), + (phases_meta, phases2fmap, [('out_dict', 'metadatas')]), + (inputnode, phases2fmap, [('phasediff', 'phase_files')]), + # phdiff delta_te + (phases2fmap, phdiff_meta, [('out_file', 'in_file')]), + (phdiff_meta, delta_te, [('out_dict', 'in_values')]), + # prep fmap + (mag_bet, prep_fmap, [("out_file", "in_magnitude")]), + (phases2fmap, prep_fmap, [('out_file', 'in_phase')]), + (delta_te, prep_fmap, [('out_value', 'delta_TE')]), + # radToHz + (prep_fmap, rad_to_hz, [("out_fieldmap", "in_file")]), + (rad_to_hz, outputnode, [("out_file", "fieldmap")]) + ] + ) + + return wf + + +def get_metadata(in_file, in_layout): + out_dict = in_layout.get_metadata(in_file) + return out_dict + +def _delta_te(in_values, te1=None, te2=None): + """Read :math:`\Delta_\text{TE}` from BIDS metadata dict""" + if isinstance(in_values, float): + te2 = in_values + te1 = 0. + + if isinstance(in_values, dict): + te1 = in_values.get('EchoTime1') + te2 = in_values.get('EchoTime2') + + if not all((te1, te2)): + te2 = in_values.get('EchoTimeDifference') + te1 = 0 + + if isinstance(in_values, list): + te2, te1 = in_values + if isinstance(te1, list): + te1 = te1[1] + if isinstance(te2, list): + te2 = te2[1] + + # For convienience if both are missing we should give one error about them + if te1 is None and te2 is None: + raise RuntimeError('EchoTime1 and EchoTime2 metadata fields not found. ' + 'Please consult the BIDS specification.') + if te1 is None: + raise RuntimeError( + 'EchoTime1 metadata field not found. Please consult the BIDS specification.') + if te2 is None: + raise RuntimeError( + 'EchoTime2 metadata field not found. Please consult the BIDS specification.') + + return abs(float(te2) - float(te1)) diff --git a/dmriprep/workflows/fieldmap/phdiff.py b/dmriprep/workflows/fieldmap/phdiff.py index 2f5256c7..8591b230 100644 --- a/dmriprep/workflows/fieldmap/phdiff.py +++ b/dmriprep/workflows/fieldmap/phdiff.py @@ -16,12 +16,14 @@ from nipype.pipeline import engine as pe from nipype.workflows.dmri.fsl.utils import siemens2rads, demean_image, \ cleanup_edge_pipeline +''' from nipype.interfaces.base import ( traits, isdefined, Undefined, TraitedSpec, BaseInterfaceInputSpec, DynamicTraitedSpec, File, Directory, InputMultiObject, OutputMultiObject, Str, SimpleInterface, InputMultiPath, OutputMultiPath ) +''' #from niworkflows.engine.workflows import LiterateWorkflow as Workflow #from niworkflows.interfaces.bids import ReadSidecarJSON #from niworkflows.interfaces.images import IntraModalMerge @@ -63,7 +65,13 @@ def init_phdiff_wf(omp_nthreads, phasetype='phasediff', name='phdiff_wf'): fields=['fmap', 'fmap_ref', 'fmap_mask']), name='outputnode') # Merge input magnitude images - magmrg = pe.Node(IntraModalMerge(), name='magmrg') + #magmrg = pe.Node(IntraModalMerge(), name='magmrg') + magmrg = pe.Node( + niu.Function( + input_names=["in_file", "hmc", "zero_based_avg", "to_ras"], output_names=["out_dict"], function=intra_modal_merge + ), + name="magmrg", + ) # de-gradient the fields ("bias/illumination artifact") n4 = pe.Node(ants.N4BiasFieldCorrection(dimension=3, copy_header=True), @@ -228,6 +236,66 @@ def _run_interface(self, runtime): self._results[fname] = metadata.get(fname, Undefined) return runtime ''' + +def intra_modal_merge(in_file, hmc, zero_based_avg, to_ras): + #OUTPUTS + # out_avg, out_file, out_mats, out_movpar + + #in_files = self.inputs.in_files + #if not isinstance(in_files, list): + # in_files = [self.inputs.in_files] + in_files = [in_file] + + # Generate output average name early + #self._results['out_avg'] = fname_presuffix(self.inputs.in_files[0], + # suffix='_avg', newpath=runtime.cwd) + out_avg = fname_presuffix(in_files, suffix='_avg', newpath=runtime.cwd) + if to_ras: + in_files = [reorient(inf, newpath=runtime.cwd) + for inf in in_files] + + if len(in_files) == 1: + filenii = nb.load(in_files[0]) + filedata = filenii.get_data() + + # magnitude files can have an extra dimension empty + if filedata.ndim == 5: + sqdata = np.squeeze(filedata) + if sqdata.ndim == 5: + raise RuntimeError('Input image (%s) is 5D' % in_files[0]) + else: + in_files = [fname_presuffix(in_files[0], suffix='_squeezed', + newpath=runtime.cwd)] + nb.Nifti1Image(sqdata, filenii.affine, + filenii.header).to_filename(in_files[0]) + + if np.squeeze(nb.load(in_files[0]).get_data()).ndim < 4: + out_file = in_files[0] + out_avg = in_files[0] + # TODO: generate identity out_mats and zero-filled out_movpar + return runtime + in_files = in_files[0] + else: + magmrg = fsl.Merge(dimension='t', in_files=in_files) + in_files = magmrg.run().outputs.merged_file + mcflirt = fsl.MCFLIRT(cost='normcorr', save_mats=True, save_plots=True, + ref_vol=0, in_file=in_files) + mcres = mcflirt.run() + out_mats = mcres.outputs.mat_file + out_movpar = mcres.outputs.par_file + out_file = mcres.outputs.out_file + + hmcnii = nb.load(mcres.outputs.out_file) + hmcdat = hmcnii.get_data().mean(axis=3) + if zero_based_avg: + hmcdat -= hmcdat.min() + + nb.Nifti1Image( + hmcdat, hmcnii.affine, hmcnii.header).to_filename( + out_avg) + + return runtime +''' class IntraModalMergeInputSpec(BaseInterfaceInputSpec): in_files = InputMultiPath(File(exists=True), mandatory=True, desc='input files') @@ -301,7 +369,7 @@ def _run_interface(self, runtime): self._results['out_avg']) return runtime -''' + class DerivativesDataSinkInputSpec(DynamicTraitedSpec, BaseInterfaceInputSpec): base_directory = traits.Directory( desc='Path to the base directory for storing data.') diff --git a/dmriprep/workflows/fieldmap/unwarp.py b/dmriprep/workflows/fieldmap/unwarp.py index 1456cf11..0fb8c306 100644 --- a/dmriprep/workflows/fieldmap/unwarp.py +++ b/dmriprep/workflows/fieldmap/unwarp.py @@ -21,52 +21,20 @@ from nipype.pipeline import engine as pe from nipype.interfaces import ants, fsl, utility as niu -from niworkflows.engine.workflows import LiterateWorkflow as Workflow -from niworkflows.interfaces import itk -from niworkflows.interfaces.images import DemeanImage, FilledImageLike -from niworkflows.interfaces.registration import ANTSApplyTransformsRPT, ANTSRegistrationRPT - -from ...interfaces import DerivativesDataSink +#from niworkflows.engine.workflows import LiterateWorkflow as Workflow +#from niworkflows.interfaces import itk +#from niworkflows.interfaces.images import DemeanImage, FilledImageLike +#from niworkflows.interfaces.registration import ANTSApplyTransformsRPT, ANTSRegistrationRPT +from nipype.interfaces.base import ( + traits, isdefined, Undefined, + TraitedSpec, BaseInterfaceInputSpec, DynamicTraitedSpec, + File, Directory, InputMultiObject, OutputMultiObject, Str, + SimpleInterface, InputMultiPath, OutputMultiPath +) +#from ...interfaces import DerivativesDataSink from ...interfaces.fmap import get_ees as _get_ees, FieldToRadS from ..bold.util import init_enhance_and_skullstrip_bold_wf -class LiterateWorkflow(pe.Workflow): - """Controls the setup and execution of a pipeline of processes.""" - - def __init__(self, name, base_dir=None): - """Create a workflow object. - Parameters - ---------- - name : alphanumeric string - unique identifier for the workflow - base_dir : string, optional - path to workflow storage - """ - super(LiterateWorkflow, self).__init__(name, base_dir) - self.__desc__ = None - self.__postdesc__ = None - - def visit_desc(self): - """ - Builds a citation boilerplate by visiting all workflows - appending their ``__desc__`` field - """ - desc = [] - - if self.__desc__: - desc += [self.__desc__] - - for node in pe.utils.topological_sort(self._graph)[0]: - if isinstance(node, LiterateWorkflow): - add_desc = node.visit_desc() - if add_desc not in desc: - desc.append(add_desc) - - if self.__postdesc__: - desc += [self.__postdesc__] - - return ''.join(desc) - def init_sdc_unwarp_wf(omp_nthreads, fmap_demean, debug, name='sdc_unwarp_wf'): """ This workflow takes in a displacements fieldmap and calculates the corresponding @@ -120,7 +88,7 @@ def init_sdc_unwarp_wf(omp_nthreads, fmap_demean, debug, name='sdc_unwarp_wf'): """ - workflow = Workflow(name=name) + workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface( fields=['in_reference', 'in_reference_brain', 'in_mask', 'metadata', 'fmap_ref', 'fmap_mask', 'fmap']), name='inputnode') @@ -197,7 +165,7 @@ def init_sdc_unwarp_wf(omp_nthreads, fmap_demean, debug, name='sdc_unwarp_wf'): ('composite_transform', 'transforms')]), (fmap2ref_apply, ds_report_vsm, [('out_report', 'in_file')]), (inputnode, fmap2ref_reg, [('in_reference_brain', 'fixed_image')]), - (fmap2ref_reg, ds_report_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')]), @@ -299,16 +267,16 @@ def init_fmap_unwarp_report_wf(name='fmap_unwarp_report_wf', forcedsyn=False): bold_rpt = pe.Node(SimpleBeforeAfter(), name='bold_rpt', mem_gb=0.1) - ds_report_sdc = pe.Node( - DerivativesDataSink(desc='sdc' if not forcedsyn else 'forcedsyn', - suffix='bold'), name='ds_report_sdc', - mem_gb=DEFAULT_MEMORY_MIN_GB, run_without_submitting=True - ) + #ds_report_sdc = pe.Node( + # DerivativesDataSink(desc='sdc' if not forcedsyn else 'forcedsyn', + # suffix='bold'), name='ds_report_sdc', + # mem_gb=DEFAULT_MEMORY_MIN_GB, run_without_submitting=True + #) workflow.connect([ (inputnode, bold_rpt, [('in_post', 'after'), ('in_pre', 'before')]), - (bold_rpt, ds_report_sdc, [('out_report', 'in_file')]), + #(bold_rpt, ds_report_sdc, [('out_report', 'in_file')]), (inputnode, map_seg, [('in_post', 'reference_image'), ('in_seg', 'input_image'), ('in_xfm', 'transforms')]), @@ -318,6 +286,69 @@ def init_fmap_unwarp_report_wf(name='fmap_unwarp_report_wf', forcedsyn=False): return workflow +class FUGUEvsm2ANTSwarpInputSpec(BaseInterfaceInputSpec): + in_file = File(exists=True, mandatory=True, + desc='input displacements field map') + pe_dir = traits.Enum('i', 'i-', 'j', 'j-', 'k', 'k-', + desc='phase-encoding axis') + + +class FUGUEvsm2ANTSwarpOutputSpec(TraitedSpec): + out_file = File(desc='the output warp field') + + +class FUGUEvsm2ANTSwarp(SimpleInterface): + + """ + Convert a voxel-shift-map to ants warp + """ + input_spec = FUGUEvsm2ANTSwarpInputSpec + output_spec = FUGUEvsm2ANTSwarpOutputSpec + + def _run_interface(self, runtime): + + nii = nb.load(self.inputs.in_file) + + phaseEncDim = {'i': 0, 'j': 1, 'k': 2}[self.inputs.pe_dir[0]] + + if len(self.inputs.pe_dir) == 2: + phaseEncSign = 1.0 + else: + phaseEncSign = -1.0 + + # Fix header + hdr = nii.header.copy() + hdr.set_data_dtype(np.dtype(' Date: Wed, 3 Jul 2019 10:10:51 -0400 Subject: [PATCH 034/156] Added phasediff support for SIEMENS scanners --- dmriprep/cli.py | 2 +- dmriprep/workflows/base.py | 7 ++- dmriprep/workflows/dwi/base.py | 5 +- dmriprep/workflows/fieldmap/base.py | 72 ++++++------------------ dmriprep/workflows/fieldmap/phasediff.py | 59 +++++++------------ 5 files changed, 45 insertions(+), 100 deletions(-) diff --git a/dmriprep/cli.py b/dmriprep/cli.py index 2f5dab9f..e19317d5 100644 --- a/dmriprep/cli.py +++ b/dmriprep/cli.py @@ -85,7 +85,7 @@ def main( ) work_dir = os.path.join(output_dir, "scratch") - wf = init_dmriprep_wf(layout, subject_list, work_dir, output_dir) + wf = init_dmriprep_wf(layout, subject_list, work_dir, output_dir, bids_dir) wf.write_graph(graph2use="colored") wf.config["execution"]["remove_unnecessary_outputs"] = False wf.config["execution"]["keep_inputs"] = True diff --git a/dmriprep/workflows/base.py b/dmriprep/workflows/base.py index ddbb04fb..8509c4d5 100644 --- a/dmriprep/workflows/base.py +++ b/dmriprep/workflows/base.py @@ -7,7 +7,7 @@ from .dwi import init_dwi_preproc_wf, init_output_wf -def init_dmriprep_wf(layout, subject_list, work_dir, output_dir): +def init_dmriprep_wf(layout, subject_list, work_dir, output_dir, bids_dir): dmriprep_wf = pe.Workflow(name="dmriprep_wf") dmriprep_wf.base_dir = work_dir @@ -19,6 +19,7 @@ def init_dmriprep_wf(layout, subject_list, work_dir, output_dir): name="single_subject_" + subject_id + "_wf", work_dir=work_dir, output_dir=output_dir, + bids_dir=bids_dir ) single_subject_wf.config["execution"]["crashdump_dir"] = os.path.join( @@ -33,7 +34,7 @@ def init_dmriprep_wf(layout, subject_list, work_dir, output_dir): return dmriprep_wf -def init_single_subject_wf(layout, subject_id, name, work_dir, output_dir): +def init_single_subject_wf(layout, subject_id, name, work_dir, output_dir, bids_dir): dwi_files = layout.get( subject=subject_id, @@ -56,7 +57,7 @@ def init_single_subject_wf(layout, subject_id, name, work_dir, output_dir): session_id = entities["session"] metadata = layout.get_metadata(dwi_file) dwi_preproc_wf = init_dwi_preproc_wf( - subject_id=subject_id, dwi_file=dwi_file, metadata=metadata, layout=layout + subject_id=subject_id, dwi_file=dwi_file, metadata=metadata, layout=layout, bids_dir=bids_dir ) datasink_wf = init_output_wf( subject_id=subject_id, session_id=session_id, output_folder=output_dir diff --git a/dmriprep/workflows/dwi/base.py b/dmriprep/workflows/dwi/base.py index f89a650e..4cf79935 100755 --- a/dmriprep/workflows/dwi/base.py +++ b/dmriprep/workflows/dwi/base.py @@ -3,7 +3,7 @@ import os -def init_dwi_preproc_wf(subject_id, dwi_file, metadata, layout): +def init_dwi_preproc_wf(subject_id, dwi_file, metadata, layout, bids_dir): from nipype.pipeline import engine as pe from nipype.interfaces import ( freesurfer as fs, @@ -34,7 +34,7 @@ def init_dwi_preproc_wf(subject_id, dwi_file, metadata, layout): # fmap["metadata"] = layout.get_metadata(fmap[fmap_key]) fmap["metadata"] = layout.get_metadata(fmap["suffix"]) - sdc_wf = init_sdc_prep_wf(fmaps, metadata, layout) + sdc_wf = init_sdc_prep_wf(fmaps, metadata, layout, bids_dir) dwi_wf = pe.Workflow(name="dwi_preproc_wf") @@ -202,6 +202,7 @@ def b0_average(in_dwi, in_bval, b0_thresh=10.0, out_file=None): ecc.inputs.use_cuda = True except: ecc.inputs.use_cuda = False + ecc.inputs.use_cuda = False eddy_quad = pe.Node(fsl.EddyQuad(verbose=True), name="eddy_quad") diff --git a/dmriprep/workflows/fieldmap/base.py b/dmriprep/workflows/fieldmap/base.py index 790f55c3..5b59d4f1 100644 --- a/dmriprep/workflows/fieldmap/base.py +++ b/dmriprep/workflows/fieldmap/base.py @@ -3,13 +3,13 @@ FMAP_PRIORITY = {"epi": 0, "fieldmap": 1, "phasediff": 2, "phase": 3, "syn": 4} -def init_sdc_prep_wf(fmaps, metadata, layout, omp_nthreads=1, fmap_bspline=False): +def init_sdc_prep_wf(fmaps, metadata, layout, bids_dir, omp_nthreads=1, fmap_bspline=False): from nipype.pipeline import engine as pe from nipype.interfaces import utility as niu sdc_prep_wf = pe.Workflow(name="sdc_prep_wf") - inputnode = pe.Node(niu.IdentityInterface(fields=["b0_stripped"]), name="inputnode") + inputnode = pe.Node(niu.IdentityInterface(fields=["b0_stripped", "bids_dir"]), name="inputnode") #outputnode = pe.Node(niu.IdentityInterface(fields=["out_fmap",]), name="outputnode") @@ -46,72 +46,32 @@ def init_sdc_prep_wf(fmaps, metadata, layout, omp_nthreads=1, fmap_bspline=False ) if fmap['suffix'] in ('phasediff', 'phase'): - print("phase made") - #from .phdiff import init_phdiff_wf from .phasediff import init_phase_wf - #fmap_estimator_wf = init_phdiff_wf(omp_nthreads=omp_nthreads, - # phasetype=fmap['suffix']) - phase_wf = init_phase_wf(layout) - #fmap_estimator_wf.inputs.inputnode.layout = layout + from .fmap import init_fmap_wf + phase_wf = init_phase_wf() if fmap['suffix'] == 'phasediff': phase_wf.inputs.inputnode.phasediff = fmap['phasediff'] elif fmap['suffix'] == 'phase': - ''' - # Check that fieldmap is not bipolar - fmap_polarity = fmap['metadata'].get('DiffusionScheme', None) - if fmap_polarity == 'Bipolar': - #LOGGER.warning("Bipolar fieldmaps are not supported. Ignoring") - #sdc_prep_wf.__postdesc__ = "" - outputnode.inputs.method = 'None' - sdc_prep_wf.connect([ - (inputnode, outputnode, [('bold_ref', 'bold_ref'), - ('bold_mask', 'bold_mask'), - ('bold_ref_brain', 'bold_ref_brain')]), - ]) - return sdc_prep_wf - #if fmap_polarity is None: - #LOGGER.warning("Assuming phase images are Monopolar") - ''' - phase_wf.inputs.inputnode.phasediff = [fmap['phase1'], fmap['phase2']] phase_wf.inputs.inputnode.magnitude1 = [ fmap_ for key, fmap_ in sorted(fmap.items()) - if key.startswith("magnitude") + if key.startswith("magnitude1") + ][0] + + phase_wf.inputs.inputnode.phases_meta = [ + layout.get_metadata(i) + for i in phase_wf.inputs.inputnode.phasediff ] + post_phase_wf = init_fmap_wf() + sdc_prep_wf.connect( [ - (phase_wf, outputnode, [("outputnode.out_fieldmap", "out_fmap")]) + (inputnode, post_phase_wf, [("b0_stripped", "inputnode.b0_stripped")]), + (phase_wf, post_phase_wf, [("outputnode.out_fmap", "inputnode.fieldmap")]), + (phase_wf, post_phase_wf, [("outputnode.out_mag", "inputnode.magnitude")]), + (post_phase_wf, outputnode, [("outputnode.out_fmap", "out_fmap")]) ] ) - - ''' - sdc_unwarp_wf = init_sdc_unwarp_wf( - omp_nthreads=omp_nthreads, - fmap_demean=fmap_demean, - debug=debug, - name='sdc_unwarp_wf') - sdc_unwarp_wf.inputs.inputnode.metadata = bold_meta - - sdc_prep_wf.connect([ - (inputnode, sdc_unwarp_wf, [ - ('bold_ref', 'inputnode.in_reference'), - ('bold_ref_brain', 'inputnode.in_reference_brain'), - ('bold_mask', 'inputnode.in_mask')]), - (fmap_estimator_wf, sdc_unwarp_wf, [ - ('outputnode.fmap', 'inputnode.fmap'), - ('outputnode.fmap_ref', 'inputnode.fmap_ref'), - ('outputnode.fmap_mask', 'inputnode.fmap_mask')]), - ]) - - sdc_prep_wf.connect([ - (sdc_unwarp_wf, outputnode, [ - ('outputnode.out_warp', 'out_warp'), - ('outputnode.out_reference', 'bold_ref'), - ('outputnode.out_reference_brain', 'bold_ref_brain'), - ('outputnode.out_mask', 'bold_mask')]), - ]) - ''' - return sdc_prep_wf diff --git a/dmriprep/workflows/fieldmap/phasediff.py b/dmriprep/workflows/fieldmap/phasediff.py index b0397557..d994489e 100644 --- a/dmriprep/workflows/fieldmap/phasediff.py +++ b/dmriprep/workflows/fieldmap/phasediff.py @@ -1,47 +1,29 @@ #!/usr/bin/env python -def init_phase_wf(layout): +def init_phase_wf(): from nipype.pipeline import engine as pe from nipype.interfaces import fsl, utility as niu from ...interfaces import Phasediff2Fieldmap, Phases2Fieldmap wf = pe.Workflow(name="phase_prep_wf") - inputnode = pe.Node(niu.IdentityInterface(fields=["magnitude1", "phasediff", "b0_stripped"]), name="inputnode") + inputnode = pe.Node(niu.IdentityInterface(fields=["magnitude1", "phasediff", "b0_stripped", "phases_meta"]), name="inputnode") - outputnode = pe.Node(niu.IdentityInterface(fields=["out_fieldmap"]), name="outputnode") + outputnode = pe.Node(niu.IdentityInterface(fields=["out_fmap", "out_mag"]), name="outputnode") - mag_bet = pe.Node(fsl.BET(frac=0.6, robust=True), + mag_bet = pe.Node(fsl.BET(frac=0.3, robust=True), name='mag_bet') - prep_fmap = pe.Node(fsl.PrepareFieldmap(scanner='SIEMENS'), + prep_fmap = pe.Node(fsl.PrepareFieldmap(scanner='SIEMENS', nocheck=True), name='prep_fmap') - rad_to_hz = pe.Node( - fsl.BinaryMaths(operation="div", operand_value=6.28), name="radToHz" - ) - - phases_meta = pe.Node( - niu.Function( - input_names=["in_file", "in_layout"], output_names=["out_dict"], function=get_metadata - ), - name="phases_meta", - ) - phases_meta.inputs.in_layout = layout - - phdiff_meta = pe.Node( - niu.Function( - input_names=["in_file", "in_layout"], output_names=["out_dict"], function=get_metadata - ), - name="phdiff_meta", - ) - phdiff_meta.inputs.in_layout = layout + fslroi = pe.Node(fsl.ExtractROI(t_min=0, t_size=1), name="fslroi_phase") - delta_te = pe.Node( + delta = pe.Node( niu.Function( - input_names=["in_values"], output_names=["out_value"], function=_delta_te + input_names=["in_values"], output_names=["out_value"], function=delta_te ), - name="delta_te", + name="delta", ) phases2fmap = pe.Node(Phases2Fieldmap(), name='phases2fmap') @@ -51,30 +33,31 @@ def init_phase_wf(layout): # Mag bet (inputnode, mag_bet, [("magnitude1", "in_file")]), # phases -> phdiff - (inputnode, phases_meta, [('out_dict', 'in_file')]), - (phases_meta, phases2fmap, [('out_dict', 'metadatas')]), + (inputnode, phases2fmap, [('phases_meta', 'metadatas')]), (inputnode, phases2fmap, [('phasediff', 'phase_files')]), # phdiff delta_te - (phases2fmap, phdiff_meta, [('out_file', 'in_file')]), - (phdiff_meta, delta_te, [('out_dict', 'in_values')]), + (phases2fmap, delta, [('phasediff_metadata', 'in_values')]), # prep fmap (mag_bet, prep_fmap, [("out_file", "in_magnitude")]), (phases2fmap, prep_fmap, [('out_file', 'in_phase')]), - (delta_te, prep_fmap, [('out_value', 'delta_TE')]), - # radToHz - (prep_fmap, rad_to_hz, [("out_fieldmap", "in_file")]), - (rad_to_hz, outputnode, [("out_file", "fieldmap")]) + (delta, prep_fmap, [('out_value', 'delta_TE')]), + # Remove second empty volume + (prep_fmap, fslroi, [("out_fieldmap", "in_file")]), + (fslroi, outputnode, [("roi_file", "out_fmap")]), + (inputnode, outputnode, [("magnitude1", "out_mag")]) ] ) return wf -def get_metadata(in_file, in_layout): - out_dict = in_layout.get_metadata(in_file) +def get_metadata(in_file, bids_dir): + from bids import BIDSLayout + layout = BIDSLayout(bids_dir, validate=False) + out_dict = layout.get_metadata(in_file) return out_dict -def _delta_te(in_values, te1=None, te2=None): +def delta_te(in_values, te1=None, te2=None): """Read :math:`\Delta_\text{TE}` from BIDS metadata dict""" if isinstance(in_values, float): te2 = in_values From bc26d3027d1a35509a22c4d308fa0f2ef1870d72 Mon Sep 17 00:00:00 2001 From: Salim Mansour Date: Thu, 4 Jul 2019 13:24:02 -0400 Subject: [PATCH 035/156] Added support for when one phdiff file is given, removed unecessary parameters --- dmriprep/cli.py | 2 +- dmriprep/workflows/base.py | 9 ++-- dmriprep/workflows/dwi/base.py | 4 +- dmriprep/workflows/fieldmap/__init__.py | 2 +- dmriprep/workflows/fieldmap/base.py | 62 +++++++++++++++--------- dmriprep/workflows/fieldmap/phasediff.py | 53 ++++++++++++++++++-- 6 files changed, 98 insertions(+), 34 deletions(-) diff --git a/dmriprep/cli.py b/dmriprep/cli.py index e19317d5..2f5dab9f 100644 --- a/dmriprep/cli.py +++ b/dmriprep/cli.py @@ -85,7 +85,7 @@ def main( ) work_dir = os.path.join(output_dir, "scratch") - wf = init_dmriprep_wf(layout, subject_list, work_dir, output_dir, bids_dir) + wf = init_dmriprep_wf(layout, subject_list, work_dir, output_dir) wf.write_graph(graph2use="colored") wf.config["execution"]["remove_unnecessary_outputs"] = False wf.config["execution"]["keep_inputs"] = True diff --git a/dmriprep/workflows/base.py b/dmriprep/workflows/base.py index 8509c4d5..2d6f014b 100644 --- a/dmriprep/workflows/base.py +++ b/dmriprep/workflows/base.py @@ -7,7 +7,7 @@ from .dwi import init_dwi_preproc_wf, init_output_wf -def init_dmriprep_wf(layout, subject_list, work_dir, output_dir, bids_dir): +def init_dmriprep_wf(layout, subject_list, work_dir, output_dir): dmriprep_wf = pe.Workflow(name="dmriprep_wf") dmriprep_wf.base_dir = work_dir @@ -18,8 +18,7 @@ def init_dmriprep_wf(layout, subject_list, work_dir, output_dir, bids_dir): subject_id=subject_id, name="single_subject_" + subject_id + "_wf", work_dir=work_dir, - output_dir=output_dir, - bids_dir=bids_dir + output_dir=output_dir ) single_subject_wf.config["execution"]["crashdump_dir"] = os.path.join( @@ -34,7 +33,7 @@ def init_dmriprep_wf(layout, subject_list, work_dir, output_dir, bids_dir): return dmriprep_wf -def init_single_subject_wf(layout, subject_id, name, work_dir, output_dir, bids_dir): +def init_single_subject_wf(layout, subject_id, name, work_dir, output_dir): dwi_files = layout.get( subject=subject_id, @@ -57,7 +56,7 @@ def init_single_subject_wf(layout, subject_id, name, work_dir, output_dir, bids_ session_id = entities["session"] metadata = layout.get_metadata(dwi_file) dwi_preproc_wf = init_dwi_preproc_wf( - subject_id=subject_id, dwi_file=dwi_file, metadata=metadata, layout=layout, bids_dir=bids_dir + subject_id=subject_id, dwi_file=dwi_file, metadata=metadata, layout=layout ) datasink_wf = init_output_wf( subject_id=subject_id, session_id=session_id, output_folder=output_dir diff --git a/dmriprep/workflows/dwi/base.py b/dmriprep/workflows/dwi/base.py index 4cf79935..0e822fb8 100755 --- a/dmriprep/workflows/dwi/base.py +++ b/dmriprep/workflows/dwi/base.py @@ -3,7 +3,7 @@ import os -def init_dwi_preproc_wf(subject_id, dwi_file, metadata, layout, bids_dir): +def init_dwi_preproc_wf(subject_id, dwi_file, metadata, layout): from nipype.pipeline import engine as pe from nipype.interfaces import ( freesurfer as fs, @@ -34,7 +34,7 @@ def init_dwi_preproc_wf(subject_id, dwi_file, metadata, layout, bids_dir): # fmap["metadata"] = layout.get_metadata(fmap[fmap_key]) fmap["metadata"] = layout.get_metadata(fmap["suffix"]) - sdc_wf = init_sdc_prep_wf(fmaps, metadata, layout, bids_dir) + sdc_wf = init_sdc_prep_wf(fmaps, metadata, layout) dwi_wf = pe.Workflow(name="dwi_preproc_wf") diff --git a/dmriprep/workflows/fieldmap/__init__.py b/dmriprep/workflows/fieldmap/__init__.py index cc9ce59d..2b2d1f4b 100644 --- a/dmriprep/workflows/fieldmap/__init__.py +++ b/dmriprep/workflows/fieldmap/__init__.py @@ -2,4 +2,4 @@ from .base import init_sdc_prep_wf from .fmap import init_fmap_wf -from .phasediff import init_phase_wf +from .phasediff import init_phase_wf, init_phdiff_wf diff --git a/dmriprep/workflows/fieldmap/base.py b/dmriprep/workflows/fieldmap/base.py index 5b59d4f1..6d37e8df 100644 --- a/dmriprep/workflows/fieldmap/base.py +++ b/dmriprep/workflows/fieldmap/base.py @@ -3,13 +3,13 @@ FMAP_PRIORITY = {"epi": 0, "fieldmap": 1, "phasediff": 2, "phase": 3, "syn": 4} -def init_sdc_prep_wf(fmaps, metadata, layout, bids_dir, omp_nthreads=1, fmap_bspline=False): +def init_sdc_prep_wf(fmaps, metadata, layout, omp_nthreads=1, fmap_bspline=False): from nipype.pipeline import engine as pe from nipype.interfaces import utility as niu sdc_prep_wf = pe.Workflow(name="sdc_prep_wf") - inputnode = pe.Node(niu.IdentityInterface(fields=["b0_stripped", "bids_dir"]), name="inputnode") + inputnode = pe.Node(niu.IdentityInterface(fields=["b0_stripped"]), name="inputnode") #outputnode = pe.Node(niu.IdentityInterface(fields=["out_fmap",]), name="outputnode") @@ -46,32 +46,50 @@ def init_sdc_prep_wf(fmaps, metadata, layout, bids_dir, omp_nthreads=1, fmap_bsp ) if fmap['suffix'] in ('phasediff', 'phase'): - from .phasediff import init_phase_wf + from .phasediff import init_phase_wf, init_phdiff_wf from .fmap import init_fmap_wf - phase_wf = init_phase_wf() if fmap['suffix'] == 'phasediff': + phase_wf = init_phdiff_wf() phase_wf.inputs.inputnode.phasediff = fmap['phasediff'] - elif fmap['suffix'] == 'phase': - phase_wf.inputs.inputnode.phasediff = [fmap['phase1'], fmap['phase2']] - phase_wf.inputs.inputnode.magnitude1 = [ - fmap_ for key, fmap_ in sorted(fmap.items()) - if key.startswith("magnitude1") - ][0] + phase_wf.inputs.inputnode.magnitude1 = [ + fmap_ for key, fmap_ in sorted(fmap.items()) + if key.startswith("magnitude1") + ][0] - phase_wf.inputs.inputnode.phases_meta = [ - layout.get_metadata(i) - for i in phase_wf.inputs.inputnode.phasediff - ] + phase_wf.inputs.inputnode.phases_meta = layout.get_metadata(phase_wf.inputs.inputnode.phasediff) + post_phase_wf = init_fmap_wf() - post_phase_wf = init_fmap_wf() + sdc_prep_wf.connect( + [ + (inputnode, post_phase_wf, [("b0_stripped", "inputnode.b0_stripped")]), + (phase_wf, post_phase_wf, [("outputnode.out_fmap", "inputnode.fieldmap")]), + (phase_wf, post_phase_wf, [("outputnode.out_mag", "inputnode.magnitude")]), + (post_phase_wf, outputnode, [("outputnode.out_fmap", "out_fmap")]) + ] + ) - sdc_prep_wf.connect( - [ - (inputnode, post_phase_wf, [("b0_stripped", "inputnode.b0_stripped")]), - (phase_wf, post_phase_wf, [("outputnode.out_fmap", "inputnode.fieldmap")]), - (phase_wf, post_phase_wf, [("outputnode.out_mag", "inputnode.magnitude")]), - (post_phase_wf, outputnode, [("outputnode.out_fmap", "out_fmap")]) + elif fmap['suffix'] == 'phase': + phase_wf = init_phase_wf() + phase_wf.inputs.inputnode.phasediff = [fmap['phase1'], fmap['phase2']] + + phase_wf.inputs.inputnode.magnitude1 = [ + fmap_ for key, fmap_ in sorted(fmap.items()) + if key.startswith("magnitude1") + ][0] + + phase_wf.inputs.inputnode.phases_meta = [ + layout.get_metadata(i) + for i in phase_wf.inputs.inputnode.phasediff ] - ) + post_phase_wf = init_fmap_wf() + + sdc_prep_wf.connect( + [ + (inputnode, post_phase_wf, [("b0_stripped", "inputnode.b0_stripped")]), + (phase_wf, post_phase_wf, [("outputnode.out_fmap", "inputnode.fieldmap")]), + (phase_wf, post_phase_wf, [("outputnode.out_mag", "inputnode.magnitude")]), + (post_phase_wf, outputnode, [("outputnode.out_fmap", "out_fmap")]) + ] + ) return sdc_prep_wf diff --git a/dmriprep/workflows/fieldmap/phasediff.py b/dmriprep/workflows/fieldmap/phasediff.py index d994489e..ef3d4f88 100644 --- a/dmriprep/workflows/fieldmap/phasediff.py +++ b/dmriprep/workflows/fieldmap/phasediff.py @@ -11,6 +11,8 @@ def init_phase_wf(): outputnode = pe.Node(niu.IdentityInterface(fields=["out_fmap", "out_mag"]), name="outputnode") + phases2fmap = pe.Node(Phases2Fieldmap(), name='phases2fmap') + mag_bet = pe.Node(fsl.BET(frac=0.3, robust=True), name='mag_bet') @@ -26,8 +28,6 @@ def init_phase_wf(): name="delta", ) - phases2fmap = pe.Node(Phases2Fieldmap(), name='phases2fmap') - wf.connect( [ # Mag bet @@ -50,6 +50,53 @@ def init_phase_wf(): return wf +def init_phdiff_wf(): + from nipype.pipeline import engine as pe + from nipype.interfaces import fsl, utility as niu + from ...interfaces import Phasediff2Fieldmap + + wf = pe.Workflow(name="phase_prep_wf") + + inputnode = pe.Node(niu.IdentityInterface(fields=["magnitude1", "phasediff", "b0_stripped", "phases_meta"]), name="inputnode") + + outputnode = pe.Node(niu.IdentityInterface(fields=["out_fmap", "out_mag"]), name="outputnode") + + mag_bet = pe.Node(fsl.BET(frac=0.3, robust=True), + name='mag_bet') + + prep_fmap = pe.Node(fsl.PrepareFieldmap(scanner='SIEMENS'), + name='prep_fmap') + + fslroi = pe.Node(fsl.ExtractROI(t_min=0, t_size=1), name="fslroi_phase") + + delta = pe.Node( + niu.Function( + input_names=["in_values"], output_names=["out_value"], function=delta_te + ), + name="delta", + ) + + #phdiff2fmap = pe.Node(Phasediff2Fieldmap(), name='phdiff2fmap') + + wf.connect( + [ + # mag bet + (inputnode, mag_bet, [("magnitude1", "in_file")]), + # phdiff delta_te + (inputnode, delta, [('phases_meta', 'in_values')]), + # prep fmap + (mag_bet, prep_fmap, [("out_file", "in_magnitude")]), + (inputnode, prep_fmap, [('phasediff', 'in_phase')]), + (delta, prep_fmap, [('out_value', 'delta_TE')]), + # Remove second empty volume + (prep_fmap, fslroi, [("out_fieldmap", "in_file")]), + # Output + (fslroi, outputnode, [("roi_file", "out_fmap")]), + (inputnode, outputnode, [("magnitude1", "out_mag")]) + ] + ) + return wf + def get_metadata(in_file, bids_dir): from bids import BIDSLayout @@ -89,4 +136,4 @@ def delta_te(in_values, te1=None, te2=None): raise RuntimeError( 'EchoTime2 metadata field not found. Please consult the BIDS specification.') - return abs(float(te2) - float(te1)) + return 1000*abs(float(te2) - float(te1)) From e96932f2161c5f023cbc707ebc8781a946f6af3c Mon Sep 17 00:00:00 2001 From: Salim Mansour Date: Thu, 4 Jul 2019 14:50:39 -0400 Subject: [PATCH 036/156] Renamed phdiff workflow and removed cuda debug line --- dmriprep/workflows/dwi/base.py | 1 - dmriprep/workflows/fieldmap/base.py | 2 -- dmriprep/workflows/fieldmap/phasediff.py | 2 +- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/dmriprep/workflows/dwi/base.py b/dmriprep/workflows/dwi/base.py index d9247d53..871a7808 100755 --- a/dmriprep/workflows/dwi/base.py +++ b/dmriprep/workflows/dwi/base.py @@ -171,7 +171,6 @@ def b0_average(in_dwi, in_bval, b0_thresh=10.0, out_file=None): ecc.inputs.use_cuda = True except: ecc.inputs.use_cuda = False - ecc.inputs.use_cuda = False denoise_eddy = pe.Node(mrtrix.DWIDenoise(), name="denoise_eddy") diff --git a/dmriprep/workflows/fieldmap/base.py b/dmriprep/workflows/fieldmap/base.py index 6d37e8df..cd1286bf 100644 --- a/dmriprep/workflows/fieldmap/base.py +++ b/dmriprep/workflows/fieldmap/base.py @@ -11,8 +11,6 @@ def init_sdc_prep_wf(fmaps, metadata, layout, omp_nthreads=1, fmap_bspline=False inputnode = pe.Node(niu.IdentityInterface(fields=["b0_stripped"]), name="inputnode") - #outputnode = pe.Node(niu.IdentityInterface(fields=["out_fmap",]), name="outputnode") - outputnode = pe.Node( niu.IdentityInterface( fields=[ diff --git a/dmriprep/workflows/fieldmap/phasediff.py b/dmriprep/workflows/fieldmap/phasediff.py index ef3d4f88..72372f7d 100644 --- a/dmriprep/workflows/fieldmap/phasediff.py +++ b/dmriprep/workflows/fieldmap/phasediff.py @@ -55,7 +55,7 @@ def init_phdiff_wf(): from nipype.interfaces import fsl, utility as niu from ...interfaces import Phasediff2Fieldmap - wf = pe.Workflow(name="phase_prep_wf") + wf = pe.Workflow(name="phdiff_prep_wf") inputnode = pe.Node(niu.IdentityInterface(fields=["magnitude1", "phasediff", "b0_stripped", "phases_meta"]), name="inputnode") From 47ba1477d685ca3460b2ea8ce2608fc5bec2c57c Mon Sep 17 00:00:00 2001 From: Salim Mansour Date: Fri, 5 Jul 2019 10:57:51 -0400 Subject: [PATCH 037/156] Added dwi and mag frac parameters for BET nodes in command line --- dmriprep/cli.py | 24 +++++++++++++++++++++++- dmriprep/workflows/base.py | 11 +++++++---- dmriprep/workflows/dwi/base.py | 6 +++--- dmriprep/workflows/fieldmap/base.py | 6 +++--- dmriprep/workflows/fieldmap/fmap.py | 2 +- dmriprep/workflows/fieldmap/phasediff.py | 8 ++++---- 6 files changed, 41 insertions(+), 16 deletions(-) diff --git a/dmriprep/cli.py b/dmriprep/cli.py index 2f5dab9f..75f6b3fa 100644 --- a/dmriprep/cli.py +++ b/dmriprep/cli.py @@ -47,6 +47,24 @@ "allowed outlier slices.", default=0.02, ) +@click.option( + "--bet-dwi", + help="Fractional intensity threshold for BET on the DWI. " + "A higher value will be more strict; it will cut off more " + "around what it analyzes the brain to be. " + "If this parameter is not provided a default of 0.3 will " + "be used.", + default=0.3, +) +@click.option( + "--bet-mag", + help="Fractional intensity threshold for BET on the magnitude. " + "A higher value will be more strict; it will cut off more " + "around what it analyzes the brain to be. " + "If this parameter is not provided a default of 0.3 will " + "be used.", + default=0.3, +) @click.argument("bids_dir") @click.argument("output_dir") @click.argument( @@ -57,6 +75,8 @@ def main( bids_dir, output_dir, eddy_niter=5, + bet_dwi=0.3, + bet_mag=0.3, slice_outlier_threshold=0.02, analysis_level="participant", ): @@ -85,7 +105,9 @@ def main( ) work_dir = os.path.join(output_dir, "scratch") - wf = init_dmriprep_wf(layout, subject_list, work_dir, output_dir) + wf = init_dmriprep_wf( + layout, subject_list, work_dir, output_dir, bet_dwi, bet_mag + ) wf.write_graph(graph2use="colored") wf.config["execution"]["remove_unnecessary_outputs"] = False wf.config["execution"]["keep_inputs"] = True diff --git a/dmriprep/workflows/base.py b/dmriprep/workflows/base.py index da8bf01c..f787b046 100644 --- a/dmriprep/workflows/base.py +++ b/dmriprep/workflows/base.py @@ -7,7 +7,7 @@ from .dwi import init_dwi_preproc_wf, init_output_wf -def init_dmriprep_wf(layout, subject_list, work_dir, output_dir): +def init_dmriprep_wf(layout, subject_list, work_dir, output_dir, bet_dwi, bet_mag): dmriprep_wf = pe.Workflow(name="dmriprep_wf") dmriprep_wf.base_dir = work_dir @@ -18,7 +18,9 @@ def init_dmriprep_wf(layout, subject_list, work_dir, output_dir): subject_id=subject_id, name="single_subject_" + subject_id + "_wf", work_dir=work_dir, - output_dir=output_dir + output_dir=output_dir, + bet_dwi=bet_dwi, + bet_mag=bet_mag ) single_subject_wf.config["execution"]["crashdump_dir"] = os.path.join( @@ -33,7 +35,7 @@ def init_dmriprep_wf(layout, subject_list, work_dir, output_dir): return dmriprep_wf -def init_single_subject_wf(layout, subject_id, name, work_dir, output_dir): +def init_single_subject_wf(layout, subject_id, name, work_dir, output_dir, bet_dwi, bet_mag): dwi_files = layout.get( subject=subject_id, @@ -56,7 +58,8 @@ def init_single_subject_wf(layout, subject_id, name, work_dir, output_dir): session_id = entities["session"] metadata = layout.get_metadata(dwi_file) dwi_preproc_wf = init_dwi_preproc_wf( - subject_id=subject_id, dwi_file=dwi_file, metadata=metadata, layout=layout + subject_id=subject_id, dwi_file=dwi_file, metadata=metadata, layout=layout, + bet_dwi_frac=bet_dwi, bet_mag_frac=bet_mag ) datasink_wf = init_output_wf( subject_id=subject_id, session_id=session_id, output_folder=output_dir diff --git a/dmriprep/workflows/dwi/base.py b/dmriprep/workflows/dwi/base.py index 871a7808..63f66342 100755 --- a/dmriprep/workflows/dwi/base.py +++ b/dmriprep/workflows/dwi/base.py @@ -1,7 +1,7 @@ #!/usr/bin/env python -def init_dwi_preproc_wf(subject_id, dwi_file, metadata, layout): +def init_dwi_preproc_wf(subject_id, dwi_file, metadata, layout, bet_dwi_frac, bet_mag_frac): from nipype.pipeline import engine as pe from nipype.interfaces import fsl, utility as niu @@ -20,7 +20,7 @@ def init_dwi_preproc_wf(subject_id, dwi_file, metadata, layout): for fmap in fmaps: fmap["metadata"] = layout.get_metadata(fmap["suffix"]) - sdc_wf = init_sdc_prep_wf(fmaps, metadata, layout) + sdc_wf = init_sdc_prep_wf(fmaps, metadata, layout, bet_mag_frac) dwi_wf = pe.Workflow(name="dwi_preproc_wf") @@ -151,7 +151,7 @@ def b0_average(in_dwi, in_bval, b0_thresh=10.0, out_file=None): ) # dilate mask - bet_dwi0 = pe.Node(fsl.BET(frac=0.3, mask=True, robust=True), name="bet_dwi_pre") + bet_dwi0 = pe.Node(fsl.BET(frac=bet_dwi_frac, mask=True, robust=True), name="bet_dwi_pre") # mrtrix3.MaskFilter diff --git a/dmriprep/workflows/fieldmap/base.py b/dmriprep/workflows/fieldmap/base.py index cd1286bf..1447c7f0 100644 --- a/dmriprep/workflows/fieldmap/base.py +++ b/dmriprep/workflows/fieldmap/base.py @@ -3,7 +3,7 @@ FMAP_PRIORITY = {"epi": 0, "fieldmap": 1, "phasediff": 2, "phase": 3, "syn": 4} -def init_sdc_prep_wf(fmaps, metadata, layout, omp_nthreads=1, fmap_bspline=False): +def init_sdc_prep_wf(fmaps, metadata, layout, bet_mag_frac, omp_nthreads=1, fmap_bspline=False): from nipype.pipeline import engine as pe from nipype.interfaces import utility as niu @@ -47,7 +47,7 @@ def init_sdc_prep_wf(fmaps, metadata, layout, omp_nthreads=1, fmap_bspline=False from .phasediff import init_phase_wf, init_phdiff_wf from .fmap import init_fmap_wf if fmap['suffix'] == 'phasediff': - phase_wf = init_phdiff_wf() + phase_wf = init_phdiff_wf(bet_mag_frac) phase_wf.inputs.inputnode.phasediff = fmap['phasediff'] phase_wf.inputs.inputnode.magnitude1 = [ @@ -68,7 +68,7 @@ def init_sdc_prep_wf(fmaps, metadata, layout, omp_nthreads=1, fmap_bspline=False ) elif fmap['suffix'] == 'phase': - phase_wf = init_phase_wf() + phase_wf = init_phase_wf(bet_mag_frac) phase_wf.inputs.inputnode.phasediff = [fmap['phase1'], fmap['phase2']] phase_wf.inputs.inputnode.magnitude1 = [ diff --git a/dmriprep/workflows/fieldmap/fmap.py b/dmriprep/workflows/fieldmap/fmap.py index 64716f07..df002a5d 100644 --- a/dmriprep/workflows/fieldmap/fmap.py +++ b/dmriprep/workflows/fieldmap/fmap.py @@ -15,7 +15,7 @@ def init_fmap_wf(): fsl.BinaryMaths(operation="div", operand_value=6.28), name="radToHz" ) - mag_flirt = pe.Node(fsl.FLIRT(), name="magFlirt") + mag_flirt = pe.Node(fsl.FLIRT(dof=6), name="magFlirt") fmap_flirt = pe.Node(fsl.FLIRT(apply_xfm=True), name="fmapFlirt") diff --git a/dmriprep/workflows/fieldmap/phasediff.py b/dmriprep/workflows/fieldmap/phasediff.py index 72372f7d..de5e7cae 100644 --- a/dmriprep/workflows/fieldmap/phasediff.py +++ b/dmriprep/workflows/fieldmap/phasediff.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -def init_phase_wf(): +def init_phase_wf(bet_mag_frac): from nipype.pipeline import engine as pe from nipype.interfaces import fsl, utility as niu from ...interfaces import Phasediff2Fieldmap, Phases2Fieldmap @@ -13,7 +13,7 @@ def init_phase_wf(): phases2fmap = pe.Node(Phases2Fieldmap(), name='phases2fmap') - mag_bet = pe.Node(fsl.BET(frac=0.3, robust=True), + mag_bet = pe.Node(fsl.BET(frac=bet_mag_frac, robust=True), name='mag_bet') prep_fmap = pe.Node(fsl.PrepareFieldmap(scanner='SIEMENS', nocheck=True), @@ -50,7 +50,7 @@ def init_phase_wf(): return wf -def init_phdiff_wf(): +def init_phdiff_wf(bet_mag_frac): from nipype.pipeline import engine as pe from nipype.interfaces import fsl, utility as niu from ...interfaces import Phasediff2Fieldmap @@ -61,7 +61,7 @@ def init_phdiff_wf(): outputnode = pe.Node(niu.IdentityInterface(fields=["out_fmap", "out_mag"]), name="outputnode") - mag_bet = pe.Node(fsl.BET(frac=0.3, robust=True), + mag_bet = pe.Node(fsl.BET(frac=bet_mag_frac, robust=True), name='mag_bet') prep_fmap = pe.Node(fsl.PrepareFieldmap(scanner='SIEMENS'), From 2e6453e554d968938f5b94ce2a01fb67388ab087 Mon Sep 17 00:00:00 2001 From: Salim Mansour Date: Fri, 5 Jul 2019 17:36:18 -0400 Subject: [PATCH 038/156] Deleted unneeded phdiff and unwarp files --- dmriprep/workflows/fieldmap/phdiff.py | 534 -------------------------- dmriprep/workflows/fieldmap/unwarp.py | 362 ----------------- 2 files changed, 896 deletions(-) delete mode 100644 dmriprep/workflows/fieldmap/phdiff.py delete mode 100644 dmriprep/workflows/fieldmap/unwarp.py diff --git a/dmriprep/workflows/fieldmap/phdiff.py b/dmriprep/workflows/fieldmap/phdiff.py deleted file mode 100644 index 8591b230..00000000 --- a/dmriprep/workflows/fieldmap/phdiff.py +++ /dev/null @@ -1,534 +0,0 @@ -#!/usr/bin/env python - -""" -.. _sdc_phasediff : -Phase-difference B0 estimation -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The field inhomogeneity inside the scanner (fieldmap) is proportional to the -phase drift between two subsequent :abbr:`GRE (gradient recall echo)` -sequence. -Fieldmap preprocessing workflow for fieldmap data structure -8.9.1 in BIDS 1.0.0: one phase diff and at least one magnitude image -8.9.2 in BIDS 1.0.0: two phases and at least one magnitude image -""" - -from nipype.interfaces import ants, fsl, utility as niu -from nipype.pipeline import engine as pe -from nipype.workflows.dmri.fsl.utils import siemens2rads, demean_image, \ - cleanup_edge_pipeline -''' -from nipype.interfaces.base import ( - traits, isdefined, Undefined, - TraitedSpec, BaseInterfaceInputSpec, DynamicTraitedSpec, - File, Directory, InputMultiObject, OutputMultiObject, Str, - SimpleInterface, InputMultiPath, OutputMultiPath -) -''' -#from niworkflows.engine.workflows import LiterateWorkflow as Workflow -#from niworkflows.interfaces.bids import ReadSidecarJSON -#from niworkflows.interfaces.images import IntraModalMerge -#from niworkflows.interfaces.masks import BETRPT - -from ...interfaces import Phasediff2Fieldmap, Phases2Fieldmap - -def init_phdiff_wf(omp_nthreads, phasetype='phasediff', name='phdiff_wf'): - """ - Estimates the fieldmap using a phase-difference image and one or more - magnitude images corresponding to two or more :abbr:`GRE (Gradient Echo sequence)` - acquisitions. The `original code was taken from nipype - `_. - .. workflow :: - :graph2use: orig - :simple_form: yes - from fmriprep.workflows.fieldmap.phdiff import init_phdiff_wf - wf = init_phdiff_wf(omp_nthreads=1) - Outputs:: - outputnode.fmap_ref - The average magnitude image, skull-stripped - outputnode.fmap_mask - The brain mask applied to the fieldmap - outputnode.fmap - The estimated fieldmap in Hz - """ - - workflow = pe.Workflow(name=name) - ''' - workflow.__desc__ = """\ -A deformation field to correct for susceptibility distortions was estimated -based on a field map that was co-registered to the BOLD reference, -using a custom workflow of *fMRIPrep* derived from D. Greve's `epidewarp.fsl` -[script](http://www.nmr.mgh.harvard.edu/~greve/fbirn/b0/epidewarp.fsl) and -further improvements of HCP Pipelines [@hcppipelines]. -""" - ''' - inputnode = pe.Node(niu.IdentityInterface(fields=['magnitude', 'phasediff', 'layout']), - name='inputnode') - - outputnode = pe.Node(niu.IdentityInterface( - fields=['fmap', 'fmap_ref', 'fmap_mask']), name='outputnode') - - # Merge input magnitude images - #magmrg = pe.Node(IntraModalMerge(), name='magmrg') - magmrg = pe.Node( - niu.Function( - input_names=["in_file", "hmc", "zero_based_avg", "to_ras"], output_names=["out_dict"], function=intra_modal_merge - ), - name="magmrg", - ) - - # de-gradient the fields ("bias/illumination artifact") - n4 = pe.Node(ants.N4BiasFieldCorrection(dimension=3, copy_header=True), - name='n4', n_procs=omp_nthreads) - #bet = pe.Node(BETRPT(generate_report=True, frac=0.6, mask=True), - # name='bet') - bet = pe.Node(fsl.BET(frac=0.6, mask=True), - name='bet') - #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') - - # FSL PRELUDE will perform phase-unwrapping - prelude = pe.Node(fsl.PRELUDE(), name='prelude') - - denoise = pe.Node(fsl.SpatialFilter(operation='median', kernel_shape='sphere', - kernel_size=3), name='denoise') - - demean = pe.Node(niu.Function(function=demean_image), name='demean') - - cleanup_wf = cleanup_edge_pipeline(name="cleanup_wf") - - compfmap = pe.Node(Phasediff2Fieldmap(), name='compfmap') - - # The phdiff2fmap interface is equivalent to: - # rad2rsec (using rads2radsec from nipype.workflows.dmri.fsl.utils) - # pre_fugue = pe.Node(fsl.FUGUE(save_fmap=True), name='ComputeFieldmapFUGUE') - # rsec2hz (divide by 2pi) - - if phasetype == "phasediff": - # Read phasediff echo times - #meta = pe.Node(ReadSidecarJSON(bids_validate=False), name='meta', mem_gb=0.01) - - # phase diff -> radians - pha2rads = pe.Node(niu.Function(function=siemens2rads), - name='pha2rads') - # Read phasediff echo times - #meta = pe.Node(ReadSidecarJSON(bids_validate=False), name='meta', mem_gb=0.01, - # run_without_submitting=True) - - meta = pe.Node( - niu.Function( - input_names=["in_file", "in_layout"], output_names=["out_dict"], function=get_metadata - ), - name="meta", - ) - - workflow.connect([ - (meta, compfmap, [('out_dict', 'metadata')]), - (inputnode, pha2rads, [('phasediff', 'in_file')]), - (pha2rads, prelude, [('out', 'phase_file')]), - #(inputnode, ds_report_fmap_mask, [('phasediff', 'source_file')]), - ]) - - elif phasetype == "phase": - # Special case for phase1, phase2 images - #meta = pe.MapNode(ReadSidecarJSON(), name='meta', mem_gb=0.01, - # run_without_submitting=True, iterfield=['in_file']) - - meta = pe.Node( - niu.Function( - input_names=["in_file", "in_layout"], output_names=["out_dict"], function=get_metadata - ), - name="meta", - ) - - phases2fmap = pe.Node(Phases2Fieldmap(), name='phases2fmap') - workflow.connect([ - (meta, phases2fmap, [('out_dict', 'metadatas')]), - (inputnode, phases2fmap, [('phasediff', 'phase_files')]), - (phases2fmap, prelude, [('out_file', 'phase_file')]), - (phases2fmap, compfmap, [('phasediff_metadata', 'metadata')]), - #(phases2fmap, ds_report_fmap_mask, [('out_file', 'source_file')]) - ]) - - workflow.connect([ - #(inputnode, meta, [('phasediff', 'in_file')]), - ( - inputnode, - meta, - [ - ('phasediff', 'in_file'), - ('layout', 'in_layout'), - ], - ), - (inputnode, magmrg, [('magnitude', 'in_files')]), - (magmrg, n4, [('out_avg', 'input_image')]), - (n4, prelude, [('output_image', 'magnitude_file')]), - (n4, bet, [('output_image', 'in_file')]), - (bet, prelude, [('mask_file', 'mask_file')]), - (prelude, denoise, [('unwrapped_phase_file', 'in_file')]), - (denoise, demean, [('out_file', 'in_file')]), - (demean, cleanup_wf, [('out', 'inputnode.in_file')]), - (bet, cleanup_wf, [('mask_file', 'inputnode.in_mask')]), - (cleanup_wf, compfmap, [('outputnode.out_file', 'in_file')]), - (compfmap, outputnode, [('out_file', 'fmap')]), - (bet, outputnode, [('mask_file', 'fmap_mask'), - ('out_file', 'fmap_ref')]), - #(bet, ds_report_fmap_mask, [('out_report', 'in_file')]), - ]) - - return workflow - -def get_metadata(in_file, in_layout): - out_dict = in_layout.get_metadata(in_file) - return out_dict -''' -class ReadSidecarJSONInputSpec(BIDSBaseInputSpec): - in_file = File(exists=True, mandatory=True, desc='the input nifti file') - - -class ReadSidecarJSONOutputSpec(BIDSInfoOutputSpec): - out_dict = traits.Dict() - - -class ReadSidecarJSON(SimpleInterface): - input_spec = ReadSidecarJSONInputSpec - output_spec = ReadSidecarJSONOutputSpec - layout = None - _always_run = True - - def __init__(self, fields=None, undef_fields=False, **inputs): - super(ReadSidecarJSON, self).__init__(**inputs) - self._fields = fields or [] - if isinstance(self._fields, str): - self._fields = [self._fields] - - self._undef_fields = undef_fields - - def _outputs(self): - base = super(ReadSidecarJSON, self)._outputs() - if self._fields: - base = add_traits(base, self._fields) - return base - - def _run_interface(self, runtime): - self.layout = self.inputs.bids_dir or self.layout - self.layout = _init_layout(self.inputs.in_file, - self.layout, - self.inputs.bids_validate) - - # Fill in BIDS entities of the output ("*_id") - output_keys = list(BIDSInfoOutputSpec().get().keys()) - params = self.layout.parse_file_entities(self.inputs.in_file) - self._results = {key: params.get(key.split('_')[0], Undefined) - for key in output_keys} - - # Fill in metadata - metadata = self.layout.get_metadata(self.inputs.in_file) - self._results['out_dict'] = metadata - - # Set dynamic outputs if fields input is present - for fname in self._fields: - if not self._undef_fields and fname not in metadata: - raise KeyError( - 'Metadata field "%s" not found for file %s' % ( - fname, self.inputs.in_file)) - self._results[fname] = metadata.get(fname, Undefined) - return runtime -''' - -def intra_modal_merge(in_file, hmc, zero_based_avg, to_ras): - #OUTPUTS - # out_avg, out_file, out_mats, out_movpar - - #in_files = self.inputs.in_files - #if not isinstance(in_files, list): - # in_files = [self.inputs.in_files] - in_files = [in_file] - - # Generate output average name early - #self._results['out_avg'] = fname_presuffix(self.inputs.in_files[0], - # suffix='_avg', newpath=runtime.cwd) - out_avg = fname_presuffix(in_files, suffix='_avg', newpath=runtime.cwd) - if to_ras: - in_files = [reorient(inf, newpath=runtime.cwd) - for inf in in_files] - - if len(in_files) == 1: - filenii = nb.load(in_files[0]) - filedata = filenii.get_data() - - # magnitude files can have an extra dimension empty - if filedata.ndim == 5: - sqdata = np.squeeze(filedata) - if sqdata.ndim == 5: - raise RuntimeError('Input image (%s) is 5D' % in_files[0]) - else: - in_files = [fname_presuffix(in_files[0], suffix='_squeezed', - newpath=runtime.cwd)] - nb.Nifti1Image(sqdata, filenii.affine, - filenii.header).to_filename(in_files[0]) - - if np.squeeze(nb.load(in_files[0]).get_data()).ndim < 4: - out_file = in_files[0] - out_avg = in_files[0] - # TODO: generate identity out_mats and zero-filled out_movpar - return runtime - in_files = in_files[0] - else: - magmrg = fsl.Merge(dimension='t', in_files=in_files) - in_files = magmrg.run().outputs.merged_file - mcflirt = fsl.MCFLIRT(cost='normcorr', save_mats=True, save_plots=True, - ref_vol=0, in_file=in_files) - mcres = mcflirt.run() - out_mats = mcres.outputs.mat_file - out_movpar = mcres.outputs.par_file - out_file = mcres.outputs.out_file - - hmcnii = nb.load(mcres.outputs.out_file) - hmcdat = hmcnii.get_data().mean(axis=3) - if zero_based_avg: - hmcdat -= hmcdat.min() - - nb.Nifti1Image( - hmcdat, hmcnii.affine, hmcnii.header).to_filename( - out_avg) - - return runtime -''' -class IntraModalMergeInputSpec(BaseInterfaceInputSpec): - in_files = InputMultiPath(File(exists=True), mandatory=True, - desc='input files') - hmc = traits.Bool(True, usedefault=True) - zero_based_avg = traits.Bool(True, usedefault=True) - to_ras = traits.Bool(True, usedefault=True) - - -class IntraModalMergeOutputSpec(TraitedSpec): - out_file = File(exists=True, desc='merged image') - out_avg = File(exists=True, desc='average image') - out_mats = OutputMultiPath(File(exists=True), desc='output matrices') - out_movpar = OutputMultiPath(File(exists=True), desc='output movement parameters') - - -class IntraModalMerge(SimpleInterface): - input_spec = IntraModalMergeInputSpec - output_spec = IntraModalMergeOutputSpec - - def _run_interface(self, runtime): - in_files = self.inputs.in_files - if not isinstance(in_files, list): - in_files = [self.inputs.in_files] - - # Generate output average name early - self._results['out_avg'] = fname_presuffix(self.inputs.in_files[0], - suffix='_avg', newpath=runtime.cwd) - - if self.inputs.to_ras: - in_files = [reorient(inf, newpath=runtime.cwd) - for inf in in_files] - - if len(in_files) == 1: - filenii = nb.load(in_files[0]) - filedata = filenii.get_data() - - # magnitude files can have an extra dimension empty - if filedata.ndim == 5: - sqdata = np.squeeze(filedata) - if sqdata.ndim == 5: - raise RuntimeError('Input image (%s) is 5D' % in_files[0]) - else: - in_files = [fname_presuffix(in_files[0], suffix='_squeezed', - newpath=runtime.cwd)] - nb.Nifti1Image(sqdata, filenii.affine, - filenii.header).to_filename(in_files[0]) - - if np.squeeze(nb.load(in_files[0]).get_data()).ndim < 4: - self._results['out_file'] = in_files[0] - self._results['out_avg'] = in_files[0] - # TODO: generate identity out_mats and zero-filled out_movpar - return runtime - in_files = in_files[0] - else: - magmrg = fsl.Merge(dimension='t', in_files=self.inputs.in_files) - in_files = magmrg.run().outputs.merged_file - mcflirt = fsl.MCFLIRT(cost='normcorr', save_mats=True, save_plots=True, - ref_vol=0, in_file=in_files) - mcres = mcflirt.run() - self._results['out_mats'] = mcres.outputs.mat_file - self._results['out_movpar'] = mcres.outputs.par_file - self._results['out_file'] = mcres.outputs.out_file - - hmcnii = nb.load(mcres.outputs.out_file) - hmcdat = hmcnii.get_data().mean(axis=3) - if self.inputs.zero_based_avg: - hmcdat -= hmcdat.min() - - nb.Nifti1Image( - hmcdat, hmcnii.affine, hmcnii.header).to_filename( - self._results['out_avg']) - - return runtime - -class DerivativesDataSinkInputSpec(DynamicTraitedSpec, BaseInterfaceInputSpec): - base_directory = traits.Directory( - desc='Path to the base directory for storing data.') - check_hdr = traits.Bool(True, usedefault=True, desc='fix headers of NIfTI outputs') - compress = traits.Bool(desc="force compression (True) or uncompression (False)" - " of the output file (default: same as input)") - desc = Str('', usedefault=True, desc='Label for description field') - extra_values = traits.List(Str) - in_file = InputMultiObject(File(exists=True), mandatory=True, - desc='the object to be saved') - keep_dtype = traits.Bool(False, usedefault=True, desc='keep datatype suffix') - meta_dict = traits.DictStrAny(desc='an input dictionary containing metadata') - source_file = File(exists=False, mandatory=True, desc='the input func file') - space = Str('', usedefault=True, desc='Label for space field') - suffix = Str('', usedefault=True, desc='suffix appended to source_file') - - -class DerivativesDataSinkOutputSpec(TraitedSpec): - out_file = OutputMultiObject(File(exists=True, desc='written file path')) - out_meta = OutputMultiObject(File(exists=True, desc='written JSON sidecar path')) - compression = OutputMultiObject( - traits.Bool, desc='whether ``in_file`` was compressed/uncompressed ' - 'or `it was copied directly.') - fixed_hdr = traits.List(traits.Bool, desc='whether derivative header was fixed') - - -class DerivativesDataSink(SimpleInterface): - input_spec = DerivativesDataSinkInputSpec - output_spec = DerivativesDataSinkOutputSpec - out_path_base = "niworkflows" - _always_run = True - - def __init__(self, allowed_entities=None, out_path_base=None, **inputs): - self._allowed_entities = allowed_entities or [] - - self._metadata = {} - self._static_traits = self.input_spec.class_editable_traits() + self._allowed_entities - for dynamic_input in set(inputs) - set(self._static_traits): - self._metadata[dynamic_input] = inputs.pop(dynamic_input) - - super(DerivativesDataSink, self).__init__(**inputs) - if self._allowed_entities: - add_traits(self.inputs, self._allowed_entities) - for k in set(self._allowed_entities).intersection(list(inputs.keys())): - setattr(self.inputs, k, inputs[k]) - - self._results['out_file'] = [] - if out_path_base: - self.out_path_base = out_path_base - - def _run_interface(self, runtime): - if isdefined(self.inputs.meta_dict): - meta = self.inputs.meta_dict - # inputs passed in construction take priority - meta.update(self._metadata) - self._metadata = meta - - src_fname, _ = _splitext(self.inputs.source_file) - src_fname, dtype = src_fname.rsplit('_', 1) - _, ext = _splitext(self.inputs.in_file[0]) - if self.inputs.compress is True and not ext.endswith('.gz'): - ext += '.gz' - elif self.inputs.compress is False and ext.endswith('.gz'): - ext = ext[:-3] - - m = BIDS_NAME.search(src_fname) - - mod = Path(self.inputs.source_file).parent.name - - base_directory = runtime.cwd - if isdefined(self.inputs.base_directory): - base_directory = self.inputs.base_directory - - base_directory = Path(base_directory).resolve() - out_path = base_directory / self.out_path_base / \ - '{subject_id}'.format(**m.groupdict()) - - if m.groupdict().get('session_id') is not None: - out_path = out_path / '{session_id}'.format(**m.groupdict()) - - out_path = out_path / '{}'.format(mod) - out_path.mkdir(exist_ok=True, parents=True) - base_fname = str(out_path / src_fname) - - allowed_entities = {} - for key in self._allowed_entities: - value = getattr(self.inputs, key) - if value is not None and isdefined(value): - allowed_entities[key] = '_%s-%s' % (key, value) - - formatbase = '{bname}{space}{desc}' + ''.join( - [allowed_entities.get(s, '') for s in self._allowed_entities]) - - formatstr = formatbase + '{extra}{suffix}{dtype}{ext}' - if len(self.inputs.in_file) > 1 and not isdefined(self.inputs.extra_values): - formatstr = formatbase + '{suffix}{i:04d}{dtype}{ext}' - - space = '_space-{}'.format(self.inputs.space) if self.inputs.space else '' - desc = '_desc-{}'.format(self.inputs.desc) if self.inputs.desc else '' - suffix = '_{}'.format(self.inputs.suffix) if self.inputs.suffix else '' - dtype = '' if not self.inputs.keep_dtype else ('_%s' % dtype) - - self._results['compression'] = [] - self._results['fixed_hdr'] = [False] * len(self.inputs.in_file) - - for i, fname in enumerate(self.inputs.in_file): - extra = '' - if isdefined(self.inputs.extra_values): - extra = '_{}'.format(self.inputs.extra_values[i]) - out_file = formatstr.format( - bname=base_fname, - space=space, - desc=desc, - extra=extra, - suffix=suffix, - i=i, - dtype=dtype, - ext=ext, - ) - self._results['out_file'].append(out_file) - self._results['compression'].append(_copy_any(fname, out_file)) - - is_nii = out_file.endswith('.nii') or out_file.endswith('.nii.gz') - if self.inputs.check_hdr and is_nii: - nii = nb.load(out_file) - if not isinstance(nii, (nb.Nifti1Image, nb.Nifti2Image)): - # .dtseries.nii are CIfTI2, therefore skip check - return runtime - hdr = nii.header.copy() - curr_units = tuple([None if u == 'unknown' else u - for u in hdr.get_xyzt_units()]) - curr_codes = (int(hdr['qform_code']), int(hdr['sform_code'])) - - # Default to mm, use sec if data type is bold - units = (curr_units[0] or 'mm', 'sec' if dtype == '_bold' else None) - xcodes = (1, 1) # Derivative in its original scanner space - if self.inputs.space: - xcodes = (4, 4) if self.inputs.space in STANDARD_SPACES \ - else (2, 2) - - if curr_codes != xcodes or curr_units != units: - self._results['fixed_hdr'][i] = True - hdr.set_qform(nii.affine, xcodes[0]) - hdr.set_sform(nii.affine, xcodes[1]) - hdr.set_xyzt_units(*units) - - # Rewrite file with new header - nii.__class__(np.array(nii.dataobj), nii.affine, hdr).to_filename( - out_file) - - if len(self._results['out_file']) == 1: - meta_fields = self.inputs.copyable_trait_names() - self._metadata.update({ - k: getattr(self.inputs, k) - for k in meta_fields if k not in self._static_traits}) - if self._metadata: - sidecar = (Path(self._results['out_file'][0]).parent / - ('%s.json' % _splitext(self._results['out_file'][0])[0])) - sidecar.write_text(dumps(self._metadata, sort_keys=True, indent=2)) - self._results['out_meta'] = str(sidecar) - return runtime -''' diff --git a/dmriprep/workflows/fieldmap/unwarp.py b/dmriprep/workflows/fieldmap/unwarp.py deleted file mode 100644 index 0fb8c306..00000000 --- a/dmriprep/workflows/fieldmap/unwarp.py +++ /dev/null @@ -1,362 +0,0 @@ -#!/usr/bin/env python - -""" -.. _sdc_unwarp : - -Unwarping -~~~~~~~~~ - -.. topic :: Abbreviations - - fmap - fieldmap - VSM - voxel-shift map -- a 3D nifti where displacements are in pixels (not mm) - DFM - displacements field map -- a nifti warp file compatible with ANTs (mm) - -""" - -import pkg_resources as pkgr - -from nipype.pipeline import engine as pe -from nipype.interfaces import ants, fsl, utility as niu -#from niworkflows.engine.workflows import LiterateWorkflow as Workflow -#from niworkflows.interfaces import itk -#from niworkflows.interfaces.images import DemeanImage, FilledImageLike -#from niworkflows.interfaces.registration import ANTSApplyTransformsRPT, ANTSRegistrationRPT -from nipype.interfaces.base import ( - traits, isdefined, Undefined, - TraitedSpec, BaseInterfaceInputSpec, DynamicTraitedSpec, - File, Directory, InputMultiObject, OutputMultiObject, Str, - SimpleInterface, InputMultiPath, OutputMultiPath -) -#from ...interfaces import DerivativesDataSink -from ...interfaces.fmap import get_ees as _get_ees, FieldToRadS -from ..bold.util import init_enhance_and_skullstrip_bold_wf - -def init_sdc_unwarp_wf(omp_nthreads, fmap_demean, debug, name='sdc_unwarp_wf'): - """ - This workflow takes in a displacements fieldmap and calculates the corresponding - displacements field (in other words, an ANTs-compatible warp file). - - It also calculates a new mask for the input dataset that takes into account the distortions. - The mask is restricted to the field of view of the fieldmap since outside of it corrections - could not be performed. - - .. workflow :: - :graph2use: orig - :simple_form: yes - - from fmriprep.workflows.fieldmap.unwarp import init_sdc_unwarp_wf - wf = init_sdc_unwarp_wf(omp_nthreads=8, - fmap_demean=True, - debug=False) - - - Inputs - - in_reference - the reference image - in_reference_brain - the reference image (skull-stripped) - in_mask - a brain mask corresponding to ``in_reference`` - metadata - metadata associated to the ``in_reference`` EPI input - fmap - the fieldmap in Hz - fmap_ref - the reference (anatomical) image corresponding to ``fmap`` - fmap_mask - a brain mask corresponding to ``fmap`` - - - Outputs - - out_reference - the ``in_reference`` after unwarping - out_reference_brain - the ``in_reference`` after unwarping and skullstripping - out_warp - the corresponding :abbr:`DFM (displacements field map)` compatible with - ANTs - out_jacobian - the jacobian of the field (for drop-out alleviation) - out_mask - mask of the unwarped input file - - """ - - workflow = pe.Workflow(name=name) - inputnode = pe.Node(niu.IdentityInterface( - fields=['in_reference', 'in_reference_brain', 'in_mask', 'metadata', - 'fmap_ref', 'fmap_mask', 'fmap']), name='inputnode') - outputnode = pe.Node(niu.IdentityInterface( - fields=['out_reference', 'out_reference_brain', 'out_warp', 'out_mask', - 'out_jacobian']), name='outputnode') - - # Register the reference of the fieldmap to the reference - # of the target image (the one that shall be corrected) - ants_settings = pkgr.resource_filename('fmriprep', 'data/fmap-any_registration.json') - if debug: - ants_settings = pkgr.resource_filename( - 'fmriprep', 'data/fmap-any_registration_testing.json') - fmap2ref_reg = pe.Node( - ANTSRegistrationRPT(generate_report=True, from_file=ants_settings, - output_inverse_warped_image=True, output_warped_image=True), - name='fmap2ref_reg', n_procs=omp_nthreads) - - 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( - generate_report=True, dimension=3, interpolation='BSpline', float=True), - name='fmap2ref_apply') - - fmap_mask2ref_apply = pe.Node(ANTSApplyTransformsRPT( - generate_report=False, dimension=3, interpolation='MultiLabel', - float=True), - name='fmap_mask2ref_apply') - - 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') - - get_ees = pe.Node(niu.Function(function=_get_ees, output_names=['ees']), name='get_ees') - - gen_vsm = pe.Node(fsl.FUGUE(save_unmasked_shift=True), name='gen_vsm') - # Convert the VSM into a DFM (displacements field map) - # or: FUGUE shift to ANTS warping. - vsm2dfm = pe.Node(itk.FUGUEvsm2ANTSwarp(), name='vsm2dfm') - jac_dfm = pe.Node(ants.CreateJacobianDeterminantImage( - imageDimension=3, outputImage='jacobian.nii.gz'), name='jac_dfm') - - unwarp_reference = pe.Node(ANTSApplyTransformsRPT(dimension=3, - generate_report=False, - float=True, - interpolation='LanczosWindowedSinc'), - name='unwarp_reference') - - fieldmap_fov_mask = pe.Node(FilledImageLike(dtype='uint8'), name='fieldmap_fov_mask') - - fmap_fov2ref_apply = pe.Node(ANTSApplyTransformsRPT( - generate_report=False, dimension=3, interpolation='NearestNeighbor', - float=True), - name='fmap_fov2ref_apply') - - apply_fov_mask = pe.Node(fsl.ApplyMask(), name="apply_fov_mask") - - enhance_and_skullstrip_bold_wf = init_enhance_and_skullstrip_bold_wf(omp_nthreads=omp_nthreads, - pre_mask=True) - - workflow.connect([ - (inputnode, fmap2ref_reg, [('fmap_ref', 'moving_image')]), - (inputnode, fmap2ref_apply, [('in_reference', 'reference_image')]), - (fmap2ref_reg, fmap2ref_apply, [ - ('composite_transform', 'transforms')]), - (inputnode, fmap_mask2ref_apply, [('in_reference', 'reference_image')]), - (fmap2ref_reg, fmap_mask2ref_apply, [ - ('composite_transform', 'transforms')]), - (fmap2ref_apply, ds_report_vsm, [('out_report', 'in_file')]), - (inputnode, fmap2ref_reg, [('in_reference_brain', 'fixed_image')]), - #(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')]), - (inputnode, get_ees, [('in_reference', 'in_file'), - ('metadata', 'in_meta')]), - (fmap_mask2ref_apply, gen_vsm, [('output_image', 'mask_file')]), - (get_ees, gen_vsm, [('ees', 'dwell_time')]), - (inputnode, gen_vsm, [(('metadata', _get_pedir_fugue), 'unwarp_direction')]), - (inputnode, vsm2dfm, [(('metadata', _get_pedir_bids), 'pe_dir')]), - (torads, gen_vsm, [('out_file', 'fmap_in_file')]), - (vsm2dfm, unwarp_reference, [('out_file', 'transforms')]), - (inputnode, unwarp_reference, [('in_reference', 'reference_image')]), - (inputnode, unwarp_reference, [('in_reference', 'input_image')]), - (vsm2dfm, outputnode, [('out_file', 'out_warp')]), - (vsm2dfm, jac_dfm, [('out_file', 'deformationField')]), - (inputnode, fieldmap_fov_mask, [('fmap_ref', 'in_file')]), - (fieldmap_fov_mask, fmap_fov2ref_apply, [('out_file', 'input_image')]), - (inputnode, fmap_fov2ref_apply, [('in_reference', 'reference_image')]), - (fmap2ref_reg, fmap_fov2ref_apply, [('composite_transform', 'transforms')]), - (fmap_fov2ref_apply, apply_fov_mask, [('output_image', 'mask_file')]), - (unwarp_reference, apply_fov_mask, [('output_image', 'in_file')]), - (apply_fov_mask, enhance_and_skullstrip_bold_wf, [('out_file', 'inputnode.in_file')]), - (fmap_mask2ref_apply, enhance_and_skullstrip_bold_wf, - [('output_image', 'inputnode.pre_mask')]), - (apply_fov_mask, outputnode, [('out_file', 'out_reference')]), - (enhance_and_skullstrip_bold_wf, outputnode, [ - ('outputnode.mask_file', 'out_mask'), - ('outputnode.skull_stripped_file', 'out_reference_brain')]), - (jac_dfm, outputnode, [('jacobian_image', 'out_jacobian')]), - ]) - - if fmap_demean: - # Demean within mask - demean = pe.Node(DemeanImage(), name='demean') - - workflow.connect([ - (gen_vsm, demean, [('shift_out_file', 'in_file')]), - (fmap_mask2ref_apply, demean, [('output_image', 'in_mask')]), - (demean, vsm2dfm, [('out_file', 'in_file')]), - ]) - - else: - workflow.connect([ - (gen_vsm, vsm2dfm, [('shift_out_file', 'in_file')]), - ]) - - return workflow - - -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. - - .. workflow:: - :graph2use: orig - :simple_form: yes - - from fmriprep.workflows.fieldmap.unwarp import init_fmap_unwarp_report_wf - wf = init_fmap_unwarp_report_wf() - - **Parameters** - - name : str, optional - Workflow name (default: fmap_unwarp_report_wf) - forcedsyn : bool, optional - Whether SyN-SDC was forced. - - **Inputs** - - in_pre - Reference image, before unwarping - in_post - Reference image, after unwarping - in_seg - Segmentation of preprocessed structural image, including - gray-matter (GM), white-matter (WM) and cerebrospinal fluid (CSF) - in_xfm - Affine transform from T1 space to BOLD space (ITK format) - - """ - from niworkflows.interfaces import SimpleBeforeAfter - from niworkflows.interfaces.fixes import FixHeaderApplyTransforms as ApplyTransforms - from niworkflows.interfaces.images import extract_wm - - DEFAULT_MEMORY_MIN_GB = 0.01 - - workflow = Workflow(name=name) - - inputnode = pe.Node(niu.IdentityInterface( - fields=['in_pre', 'in_post', 'in_seg', 'in_xfm']), name='inputnode') - - map_seg = pe.Node(ApplyTransforms( - dimension=3, float=True, interpolation='MultiLabel'), - name='map_seg', mem_gb=0.3) - - sel_wm = pe.Node(niu.Function(function=extract_wm), name='sel_wm', - mem_gb=DEFAULT_MEMORY_MIN_GB) - - bold_rpt = pe.Node(SimpleBeforeAfter(), name='bold_rpt', - mem_gb=0.1) - #ds_report_sdc = pe.Node( - # DerivativesDataSink(desc='sdc' if not forcedsyn else 'forcedsyn', - # suffix='bold'), name='ds_report_sdc', - # mem_gb=DEFAULT_MEMORY_MIN_GB, run_without_submitting=True - #) - - workflow.connect([ - (inputnode, bold_rpt, [('in_post', 'after'), - ('in_pre', 'before')]), - #(bold_rpt, ds_report_sdc, [('out_report', 'in_file')]), - (inputnode, map_seg, [('in_post', 'reference_image'), - ('in_seg', 'input_image'), - ('in_xfm', 'transforms')]), - (map_seg, sel_wm, [('output_image', 'in_seg')]), - (sel_wm, bold_rpt, [('out', 'wm_seg')]), - ]) - - return workflow - -class FUGUEvsm2ANTSwarpInputSpec(BaseInterfaceInputSpec): - in_file = File(exists=True, mandatory=True, - desc='input displacements field map') - pe_dir = traits.Enum('i', 'i-', 'j', 'j-', 'k', 'k-', - desc='phase-encoding axis') - - -class FUGUEvsm2ANTSwarpOutputSpec(TraitedSpec): - out_file = File(desc='the output warp field') - - -class FUGUEvsm2ANTSwarp(SimpleInterface): - - """ - Convert a voxel-shift-map to ants warp - """ - input_spec = FUGUEvsm2ANTSwarpInputSpec - output_spec = FUGUEvsm2ANTSwarpOutputSpec - - def _run_interface(self, runtime): - - nii = nb.load(self.inputs.in_file) - - phaseEncDim = {'i': 0, 'j': 1, 'k': 2}[self.inputs.pe_dir[0]] - - if len(self.inputs.pe_dir) == 2: - phaseEncSign = 1.0 - else: - phaseEncSign = -1.0 - - # Fix header - hdr = nii.header.copy() - hdr.set_data_dtype(np.dtype(' Date: Mon, 8 Jul 2019 15:03:16 -0400 Subject: [PATCH 039/156] remove unnecessary files --- .travis.yml | 53 +- AUTHORS.md | 11 - AUTHORS.rst | 10 + Dockerfile-dmriprep | 6 - HISTORY.rst | 2 +- Makefile | 6 - dmriprep-docker | 22 - dmriprepViewer/.babelrc | 18 - dmriprepViewer/.editorconfig | 9 - dmriprepViewer/.eslintignore | 5 - dmriprepViewer/.eslintrc.js | 50 - dmriprepViewer/.gitignore | 17 - dmriprepViewer/.postcssrc.js | 10 - dmriprepViewer/README.md | 30 - dmriprepViewer/build/build.js | 41 - dmriprepViewer/build/check-versions.js | 54 - dmriprepViewer/build/logo.png | Bin 6849 -> 0 bytes dmriprepViewer/build/utils.js | 101 - dmriprepViewer/build/vue-loader.conf.js | 22 - dmriprepViewer/build/webpack.base.conf.js | 92 - dmriprepViewer/build/webpack.dev.conf.js | 95 - dmriprepViewer/build/webpack.prod.conf.js | 149 - dmriprepViewer/config/dev.env.js | 7 - dmriprepViewer/config/index.js | 76 - dmriprepViewer/config/prod.env.js | 4 - dmriprepViewer/config/test.env.js | 7 - dmriprepViewer/deploy.sh | 36 - dmriprepViewer/index.html | 12 - dmriprepViewer/package-lock.json | 15340 ---------------- dmriprepViewer/package.json | 91 - dmriprepViewer/src/App.vue | 30 - dmriprepViewer/src/assets/logo.svg | 1 - dmriprepViewer/src/components/BrainSprite.vue | 75 - dmriprepViewer/src/components/Bucket.vue | 195 - dmriprepViewer/src/components/GroupStats.vue | 96 - dmriprepViewer/src/components/HelloWorld.vue | 114 - dmriprepViewer/src/components/LineChart.vue | 191 - dmriprepViewer/src/components/Report.vue | 162 - dmriprepViewer/src/components/Sprite4D.vue | 63 - dmriprepViewer/src/components/brainsprite.js | 587 - dmriprepViewer/src/main.js | 15 - dmriprepViewer/src/router/index.js | 27 - dmriprepViewer/static/.gitkeep | 0 .../e2e/custom-assertions/elementCount.js | 27 - dmriprepViewer/test/e2e/nightwatch.conf.js | 46 - dmriprepViewer/test/e2e/runner.js | 48 - dmriprepViewer/test/e2e/specs/test.js | 19 - dmriprepViewer/test/unit/.eslintrc | 7 - dmriprepViewer/test/unit/jest.conf.js | 30 - dmriprepViewer/test/unit/setup.js | 3 - .../test/unit/specs/HelloWorld.spec.js | 11 - docker/Dockerfile | 174 - docker/environment.yml | 17 - docker/license.txt | 4 - kubernetes/create_kube_job.py | 27 - kubernetes/delete_cluster.sh | 2 - kubernetes/delete_job.sh | 2 - kubernetes/docker/build_tag_push.sh | 4 - kubernetes/docker/dmriprep_all.sh | 6 - kubernetes/docker/dockerfile-dmriprep-kube | 3 - kubernetes/run_dmriprep.yml.tmpl | 17 - kubernetes/run_job.sh | 2 - kubernetes/setup_gcp_kubernetes_dmriprep.sh | 18 - pyproject.toml | 22 + tox.ini | 10 +- 65 files changed, 62 insertions(+), 18369 deletions(-) delete mode 100644 AUTHORS.md create mode 100644 AUTHORS.rst delete mode 100644 Dockerfile-dmriprep delete mode 100755 dmriprep-docker delete mode 100644 dmriprepViewer/.babelrc delete mode 100644 dmriprepViewer/.editorconfig delete mode 100644 dmriprepViewer/.eslintignore delete mode 100644 dmriprepViewer/.eslintrc.js delete mode 100644 dmriprepViewer/.gitignore delete mode 100644 dmriprepViewer/.postcssrc.js delete mode 100644 dmriprepViewer/README.md delete mode 100644 dmriprepViewer/build/build.js delete mode 100644 dmriprepViewer/build/check-versions.js delete mode 100644 dmriprepViewer/build/logo.png delete mode 100644 dmriprepViewer/build/utils.js delete mode 100644 dmriprepViewer/build/vue-loader.conf.js delete mode 100644 dmriprepViewer/build/webpack.base.conf.js delete mode 100755 dmriprepViewer/build/webpack.dev.conf.js delete mode 100644 dmriprepViewer/build/webpack.prod.conf.js delete mode 100644 dmriprepViewer/config/dev.env.js delete mode 100644 dmriprepViewer/config/index.js delete mode 100644 dmriprepViewer/config/prod.env.js delete mode 100644 dmriprepViewer/config/test.env.js delete mode 100644 dmriprepViewer/deploy.sh delete mode 100644 dmriprepViewer/index.html delete mode 100644 dmriprepViewer/package-lock.json delete mode 100644 dmriprepViewer/package.json delete mode 100644 dmriprepViewer/src/App.vue delete mode 100644 dmriprepViewer/src/assets/logo.svg delete mode 100644 dmriprepViewer/src/components/BrainSprite.vue delete mode 100644 dmriprepViewer/src/components/Bucket.vue delete mode 100644 dmriprepViewer/src/components/GroupStats.vue delete mode 100644 dmriprepViewer/src/components/HelloWorld.vue delete mode 100644 dmriprepViewer/src/components/LineChart.vue delete mode 100644 dmriprepViewer/src/components/Report.vue delete mode 100644 dmriprepViewer/src/components/Sprite4D.vue delete mode 100644 dmriprepViewer/src/components/brainsprite.js delete mode 100644 dmriprepViewer/src/main.js delete mode 100644 dmriprepViewer/src/router/index.js delete mode 100644 dmriprepViewer/static/.gitkeep delete mode 100644 dmriprepViewer/test/e2e/custom-assertions/elementCount.js delete mode 100644 dmriprepViewer/test/e2e/nightwatch.conf.js delete mode 100644 dmriprepViewer/test/e2e/runner.js delete mode 100644 dmriprepViewer/test/e2e/specs/test.js delete mode 100644 dmriprepViewer/test/unit/.eslintrc delete mode 100644 dmriprepViewer/test/unit/jest.conf.js delete mode 100644 dmriprepViewer/test/unit/setup.js delete mode 100644 dmriprepViewer/test/unit/specs/HelloWorld.spec.js delete mode 100644 docker/Dockerfile delete mode 100644 docker/environment.yml delete mode 100644 docker/license.txt delete mode 100755 kubernetes/create_kube_job.py delete mode 100755 kubernetes/delete_cluster.sh delete mode 100755 kubernetes/delete_job.sh delete mode 100755 kubernetes/docker/build_tag_push.sh delete mode 100755 kubernetes/docker/dmriprep_all.sh delete mode 100644 kubernetes/docker/dockerfile-dmriprep-kube delete mode 100644 kubernetes/run_dmriprep.yml.tmpl delete mode 100755 kubernetes/run_job.sh delete mode 100755 kubernetes/setup_gcp_kubernetes_dmriprep.sh create mode 100644 pyproject.toml diff --git a/.travis.yml b/.travis.yml index 9a7b84e9..fee7fbc0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,25 +1,28 @@ -sudo: false -language: node_js -node_js: -- '6.0' -cache: - directories: - - dmriprepViewer/node_modules -branches: - only: - - master -notifications: - email: false -before_script: -- cd dmriprepViewer -- npm prune -script: -- npm install -- npm run build -after_success: -- bash ./deploy.sh -env: - global: - - GH_USER: nipy - - GH_REPO: dmriprep - - secure: raeexu68fu/Hu4Ey0VkP3ljvAJut2X8xRAkTXO9nL6HlWQGt1pjYhXSxaLLZVQpgpDcDeOziUUCb4y7UALFHTkIrYrzp/Jq3Cn10tmsItEMAhH0cfzTuMr0Huhl/Zeiw67Qn16W7xpYEtV7zHBOYicQIFvzAOhNWDy/A/9c7XkKsEU47FqN/vxodCv+WMHQyvuM3ceaofccF+mVekAg7PztPrukzpwP+uishNfSeoHI3WeUDjX82bQZ3KE/oRHrB+YCB5rFg0wfekfsUnkyhS9HWF9CECM5mKWcPle0AZpjQXODrUIXk8dRSAprAzD1XGxRzmYG3nYPGx9a6gm6ofueHbLPery/0cLVYNX4IHBR2UpW6aA9VsgMBgG8LAdGiMqvvio2CZ+/tGdmIXx5twRCTGaJ4qJUdyyL881ChaUl8rBde7R8EtCBThKeV8Tnnun2PfLPt12qAc+EAc1r6a9zf/Mb8ahjTrZSU1heUn5irs44KvJk+QuftW9hF6hIvBfug7SHBIAdvyCd8TjMNShJNVPqQZU6NWSwTAGAj4Y/QsocvxYRoKHIhpyHbMZ+Lj+P27VfNYdkTe/viQcLEg8x4YYHCaKiYl4iwepEtyevtbtzUtb1Ba5clotgisw/PFao9q6GrWuy57eb/ZM8aKSHfb4JhN0swmi1t0uBOzP0= +# Config file for automatic testing at travis-ci.org + +language: python +python: + - 3.7 + - 3.6 + - 3.5 + +# Command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors +install: pip install -U tox-travis + +# Command to run tests, e.g. python setup.py test +script: tox + +# Assuming you have installed the travis-ci CLI tool, after you +# create the Github repo and add it to Travis, run the +# following command to finish PyPI deployment setup: +# $ travis encrypt --add deploy.password +deploy: + provider: pypi + distributions: sdist bdist_wheel + user: josephmje + password: + secure: PLEASE_REPLACE_ME + on: + tags: true + repo: tigrlab/dmripreproc + python: 3.7 diff --git a/AUTHORS.md b/AUTHORS.md deleted file mode 100644 index 6312fe10..00000000 --- a/AUTHORS.md +++ /dev/null @@ -1,11 +0,0 @@ -# Credits - -## Development Leads - -- Anisha Keshavan [Corresponding developer] -- Adam Richie-Halford - -## Contributors - -- Ariel Rokem -- Add your name here diff --git a/AUTHORS.rst b/AUTHORS.rst new file mode 100644 index 00000000..bf162e51 --- /dev/null +++ b/AUTHORS.rst @@ -0,0 +1,10 @@ +# Credits + +## Development Leads + +- Michael Joseph [Corresponding developer] +- Salim Mansour + +## Contributors + +None yet. Why not be the first? diff --git a/Dockerfile-dmriprep b/Dockerfile-dmriprep deleted file mode 100644 index 6343c373..00000000 --- a/Dockerfile-dmriprep +++ /dev/null @@ -1,6 +0,0 @@ -FROM dmriprep:dev - -ADD . /dmriprep -WORKDIR /dmriprep -RUN /neurodocker/startup.sh python setup.py install -WORKDIR / diff --git a/HISTORY.rst b/HISTORY.rst index a7ebd0a9..96674295 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -2,7 +2,7 @@ History ======= -0.1.0 (2018-09-06) +0.1.0 (2019-07-08) ------------------ * First release on GitHub. diff --git a/Makefile b/Makefile index 4c042a3a..7f76d452 100644 --- a/Makefile +++ b/Makefile @@ -86,9 +86,3 @@ dist: clean ## builds source and wheel package install: clean ## install the package to the active Python's site-packages python setup.py install - -docker-dev: ## build the development environment - docker build -t dmriprep:dev -f docker/Dockerfile docker/. - -docker: docker-dev - docker build -t dmriprep:prod -f Dockerfile-dmriprep . diff --git a/dmriprep-docker b/dmriprep-docker deleted file mode 100755 index 3ae00d0f..00000000 --- a/dmriprep-docker +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -if [ -z "$1" ]; then - echo "usage: dmriprep-docker /input_dir /output_dir" - exit -fi - -if [ -z "$2" ]; then - echo "usage: dmriprep-docker /input_dir /output_dir" - exit -fi - -if [ -z "$3" ]; then - docker run --rm -ti -v "$1":/inputs -v "$2":/outputs dmriprep:prod dmriprep /inputs /outputs -else - inputDir=$1 - outputDir=$2 - - shift 2 - - docker run --rm -ti -v "$inputDir":/inputs -v "$outputDir":/outputs dmriprep:prod dmriprep /inputs /outputs --participant-label "$@" -fi diff --git a/dmriprepViewer/.babelrc b/dmriprepViewer/.babelrc deleted file mode 100644 index 9390d164..00000000 --- a/dmriprepViewer/.babelrc +++ /dev/null @@ -1,18 +0,0 @@ -{ - "presets": [ - ["env", { - "modules": false, - "targets": { - "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] - } - }], - "stage-2" - ], - "plugins": ["transform-vue-jsx", "transform-runtime"], - "env": { - "test": { - "presets": ["env", "stage-2"], - "plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"] - } - } -} diff --git a/dmriprepViewer/.editorconfig b/dmriprepViewer/.editorconfig deleted file mode 100644 index 9d08a1a8..00000000 --- a/dmriprepViewer/.editorconfig +++ /dev/null @@ -1,9 +0,0 @@ -root = true - -[*] -charset = utf-8 -indent_style = space -indent_size = 2 -end_of_line = lf -insert_final_newline = true -trim_trailing_whitespace = true diff --git a/dmriprepViewer/.eslintignore b/dmriprepViewer/.eslintignore deleted file mode 100644 index e2192c5c..00000000 --- a/dmriprepViewer/.eslintignore +++ /dev/null @@ -1,5 +0,0 @@ -/build/ -/config/ -/dist/ -/*.js -/test/unit/coverage/ diff --git a/dmriprepViewer/.eslintrc.js b/dmriprepViewer/.eslintrc.js deleted file mode 100644 index 3b855403..00000000 --- a/dmriprepViewer/.eslintrc.js +++ /dev/null @@ -1,50 +0,0 @@ -// https://eslint.org/docs/user-guide/configuring - -module.exports = { - root: true, - parserOptions: { - parser: 'babel-eslint' - }, - env: { - browser: true, - }, - // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention - // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules. - extends: ['plugin:vue/essential', 'airbnb-base'], - // required to lint *.vue files - plugins: [ - 'vue' - ], - // check if imports actually resolve - settings: { - 'import/resolver': { - webpack: { - config: 'build/webpack.base.conf.js' - } - } - }, - // add your custom rules here - rules: { - // don't require .vue extension when importing - 'import/extensions': ['error', 'always', { - js: 'never', - vue: 'never' - }], - // disallow reassignment of function parameters - // disallow parameter object manipulation except for specific exclusions - 'no-param-reassign': ['error', { - props: true, - ignorePropertyModificationsFor: [ - 'state', // for vuex state - 'acc', // for reduce accumulators - 'e' // for e.returnvalue - ] - }], - // allow optionalDependencies - 'import/no-extraneous-dependencies': ['error', { - optionalDependencies: ['test/unit/index.js'] - }], - // allow debugger during development - 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' - } -} diff --git a/dmriprepViewer/.gitignore b/dmriprepViewer/.gitignore deleted file mode 100644 index dfb4167f..00000000 --- a/dmriprepViewer/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -.DS_Store -node_modules/ -/dist/ -npm-debug.log* -yarn-debug.log* -yarn-error.log* -/test/unit/coverage/ -/test/e2e/reports/ -selenium-debug.log - -# Editor directories and files -.idea -.vscode -*.suo -*.ntvs* -*.njsproj -*.sln diff --git a/dmriprepViewer/.postcssrc.js b/dmriprepViewer/.postcssrc.js deleted file mode 100644 index eee3e92d..00000000 --- a/dmriprepViewer/.postcssrc.js +++ /dev/null @@ -1,10 +0,0 @@ -// https://github.com/michael-ciniawsky/postcss-load-config - -module.exports = { - "plugins": { - "postcss-import": {}, - "postcss-url": {}, - // to edit target browsers: use "browserslist" field in package.json - "autoprefixer": {} - } -} diff --git a/dmriprepViewer/README.md b/dmriprepViewer/README.md deleted file mode 100644 index c18e32fd..00000000 --- a/dmriprepViewer/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# dmriprep-viewer - -> Viewer for dmriprep reports - -## Build Setup - -``` bash -# install dependencies -npm install - -# serve with hot reload at localhost:8080 -npm run dev - -# build for production with minification -npm run build - -# build for production and view the bundle analyzer report -npm run build --report - -# run unit tests -npm run unit - -# run e2e tests -npm run e2e - -# run all tests -npm test -``` - -For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). diff --git a/dmriprepViewer/build/build.js b/dmriprepViewer/build/build.js deleted file mode 100644 index 8f2ad8ad..00000000 --- a/dmriprepViewer/build/build.js +++ /dev/null @@ -1,41 +0,0 @@ -'use strict' -require('./check-versions')() - -process.env.NODE_ENV = 'production' - -const ora = require('ora') -const rm = require('rimraf') -const path = require('path') -const chalk = require('chalk') -const webpack = require('webpack') -const config = require('../config') -const webpackConfig = require('./webpack.prod.conf') - -const spinner = ora('building for production...') -spinner.start() - -rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { - if (err) throw err - webpack(webpackConfig, (err, stats) => { - spinner.stop() - if (err) throw err - process.stdout.write(stats.toString({ - colors: true, - modules: false, - children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. - chunks: false, - chunkModules: false - }) + '\n\n') - - if (stats.hasErrors()) { - console.log(chalk.red(' Build failed with errors.\n')) - process.exit(1) - } - - console.log(chalk.cyan(' Build complete.\n')) - console.log(chalk.yellow( - ' Tip: built files are meant to be served over an HTTP server.\n' + - ' Opening index.html over file:// won\'t work.\n' - )) - }) -}) diff --git a/dmriprepViewer/build/check-versions.js b/dmriprepViewer/build/check-versions.js deleted file mode 100644 index 3ef972a0..00000000 --- a/dmriprepViewer/build/check-versions.js +++ /dev/null @@ -1,54 +0,0 @@ -'use strict' -const chalk = require('chalk') -const semver = require('semver') -const packageConfig = require('../package.json') -const shell = require('shelljs') - -function exec (cmd) { - return require('child_process').execSync(cmd).toString().trim() -} - -const versionRequirements = [ - { - name: 'node', - currentVersion: semver.clean(process.version), - versionRequirement: packageConfig.engines.node - } -] - -if (shell.which('npm')) { - versionRequirements.push({ - name: 'npm', - currentVersion: exec('npm --version'), - versionRequirement: packageConfig.engines.npm - }) -} - -module.exports = function () { - const warnings = [] - - for (let i = 0; i < versionRequirements.length; i++) { - const mod = versionRequirements[i] - - if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { - warnings.push(mod.name + ': ' + - chalk.red(mod.currentVersion) + ' should be ' + - chalk.green(mod.versionRequirement) - ) - } - } - - if (warnings.length) { - console.log('') - console.log(chalk.yellow('To use this template, you must update following to modules:')) - console.log() - - for (let i = 0; i < warnings.length; i++) { - const warning = warnings[i] - console.log(' ' + warning) - } - - console.log() - process.exit(1) - } -} diff --git a/dmriprepViewer/build/logo.png b/dmriprepViewer/build/logo.png deleted file mode 100644 index f3d2503fc2a44b5053b0837ebea6e87a2d339a43..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6849 zcmaKRcUV(fvo}bjDT-7nLI_nlK}sT_69H+`qzVWDA|yaU?}j417wLi^B1KB1SLsC& zL0ag7$U(XW5YR7p&Ux?sP$d4lvMt8C^+TcQu4F zQqv!UF!I+kw)c0jhd6+g6oCr9P?7)?!qX1ui*iL{p}sKCAGuJ{{W)0z1pLF|=>h}& zt(2Lr0Z`2ig8<5i%Zk}cO5Fm=LByqGWaS`oqChZdEFmc`0hSb#gg|Aap^{+WKOYcj zHjINK)KDG%&s?Mt4CL(T=?;~U@bU2x_mLKN!#GJuK_CzbNw5SMEJorG!}_5;?R>@1 zSl)jns3WlU7^J%=(hUtfmuUCU&C3%8B5C^f5>W2Cy8jW3#{Od{lF1}|?c61##3dzA zsPlFG;l_FzBK}8>|H_Ru_H#!_7$UH4UKo3lKOA}g1(R&|e@}GINYVzX?q=_WLZCgh z)L|eJMce`D0EIwgRaNETDsr+?vQknSGAi=7H00r`QnI%oQnFxm`G2umXso9l+8*&Q z7WqF|$p49js$mdzo^BXpH#gURy=UO;=IMrYc5?@+sR4y_?d*~0^YP7d+y0{}0)zBM zIKVM(DBvICK#~7N0a+PY6)7;u=dutmNqK3AlsrUU9U`d;msiucB_|8|2kY=(7XA;G zwDA8AR)VCA#JOkxm#6oHNS^YVuOU;8p$N)2{`;oF|rQ?B~K$%rHDxXs+_G zF5|-uqHZvSzq}L;5Kcy_P+x0${33}Ofb6+TX&=y;;PkEOpz%+_bCw_{<&~ zeLV|!bP%l1qxywfVr9Z9JI+++EO^x>ZuCK);=$VIG1`kxK8F2M8AdC$iOe3cj1fo(ce4l-9 z7*zKy3={MixvUk=enQE;ED~7tv%qh&3lR<0m??@w{ILF|e#QOyPkFYK!&Up7xWNtL zOW%1QMC<3o;G9_S1;NkPB6bqbCOjeztEc6TsBM<(q9((JKiH{01+Ud=uw9B@{;(JJ z-DxI2*{pMq`q1RQc;V8@gYAY44Z!%#W~M9pRxI(R?SJ7sy7em=Z5DbuDlr@*q|25V)($-f}9c#?D%dU^RS<(wz?{P zFFHtCab*!rl(~j@0(Nadvwg8q|4!}L^>d?0al6}Rrv9$0M#^&@zjbfJy_n!%mVHK4 z6pLRIQ^Uq~dnyy$`ay51Us6WaP%&O;@49m&{G3z7xV3dLtt1VTOMYl3UW~Rm{Eq4m zF?Zl_v;?7EFx1_+#WFUXxcK78IV)FO>42@cm@}2I%pVbZqQ}3;p;sDIm&knay03a^ zn$5}Q$G!@fTwD$e(x-~aWP0h+4NRz$KlnO_H2c< z(XX#lPuW_%H#Q+c&(nRyX1-IadKR-%$4FYC0fsCmL9ky3 zKpxyjd^JFR+vg2!=HWf}2Z?@Td`0EG`kU?{8zKrvtsm)|7>pPk9nu@2^z96aU2<#` z2QhvH5w&V;wER?mopu+nqu*n8p~(%QkwSs&*0eJwa zMXR05`OSFpfyRb!Y_+H@O%Y z0=K^y6B8Gcbl?SA)qMP3Z+=C(?8zL@=74R=EVnE?vY!1BQy2@q*RUgRx4yJ$k}MnL zs!?74QciNb-LcG*&o<9=DSL>1n}ZNd)w1z3-0Pd^4ED1{qd=9|!!N?xnXjM!EuylY z5=!H>&hSofh8V?Jofyd!h`xDI1fYAuV(sZwwN~{$a}MX^=+0TH*SFp$vyxmUv7C*W zv^3Gl0+eTFgBi3FVD;$nhcp)ka*4gSskYIqQ&+M}xP9yLAkWzBI^I%zR^l1e?bW_6 zIn{mo{dD=)9@V?s^fa55jh78rP*Ze<3`tRCN4*mpO$@7a^*2B*7N_|A(Ve2VB|)_o z$=#_=aBkhe(ifX}MLT()@5?OV+~7cXC3r!%{QJxriXo9I%*3q4KT4Xxzyd{ z9;_%=W%q!Vw$Z7F3lUnY+1HZ*lO;4;VR2+i4+D(m#01OYq|L_fbnT;KN<^dkkCwtd zF7n+O7KvAw8c`JUh6LmeIrk4`F3o|AagKSMK3))_5Cv~y2Bb2!Ibg9BO7Vkz?pAYX zoI=B}+$R22&IL`NCYUYjrdhwjnMx_v=-Qcx-jmtN>!Zqf|n1^SWrHy zK|MwJ?Z#^>)rfT5YSY{qjZ&`Fjd;^vv&gF-Yj6$9-Dy$<6zeP4s+78gS2|t%Z309b z0^fp~ue_}i`U9j!<|qF92_3oB09NqgAoehQ`)<)dSfKoJl_A6Ec#*Mx9Cpd-p#$Ez z={AM*r-bQs6*z$!*VA4|QE7bf@-4vb?Q+pPKLkY2{yKsw{&udv_2v8{Dbd zm~8VAv!G~s)`O3|Q6vFUV%8%+?ZSVUa(;fhPNg#vab@J*9XE4#D%)$UU-T5`fwjz! z6&gA^`OGu6aUk{l*h9eB?opVdrHK>Q@U>&JQ_2pR%}TyOXGq_6s56_`U(WoOaAb+K zXQr#6H}>a-GYs9^bGP2Y&hSP5gEtW+GVC4=wy0wQk=~%CSXj=GH6q z-T#s!BV`xZVxm{~jr_ezYRpqqIcXC=Oq`b{lu`Rt(IYr4B91hhVC?yg{ol4WUr3v9 zOAk2LG>CIECZ-WIs0$N}F#eoIUEtZudc7DPYIjzGqDLWk_A4#(LgacooD z2K4IWs@N`Bddm-{%oy}!k0^i6Yh)uJ1S*90>|bm3TOZxcV|ywHUb(+CeX-o1|LTZM zwU>dY3R&U)T(}5#Neh?-CWT~@{6Ke@sI)uSuzoah8COy)w)B)aslJmp`WUcjdia-0 zl2Y}&L~XfA`uYQboAJ1;J{XLhYjH){cObH3FDva+^8ioOQy%Z=xyjGLmWMrzfFoH; zEi3AG`_v+%)&lDJE;iJWJDI@-X9K5O)LD~j*PBe(wu+|%ar~C+LK1+-+lK=t# z+Xc+J7qp~5q=B~rD!x78)?1+KUIbYr^5rcl&tB-cTtj+e%{gpZZ4G~6r15+d|J(ky zjg@@UzMW0k9@S#W(1H{u;Nq(7llJbq;;4t$awM;l&(2s+$l!Ay9^Ge|34CVhr7|BG z?dAR83smef^frq9V(OH+a+ki#q&-7TkWfFM=5bsGbU(8mC;>QTCWL5ydz9s6k@?+V zcjiH`VI=59P-(-DWXZ~5DH>B^_H~;4$)KUhnmGo*G!Tq8^LjfUDO)lASN*=#AY_yS zqW9UX(VOCO&p@kHdUUgsBO0KhXxn1sprK5h8}+>IhX(nSXZKwlNsjk^M|RAaqmCZB zHBolOHYBas@&{PT=R+?d8pZu zUHfyucQ`(umXSW7o?HQ3H21M`ZJal+%*)SH1B1j6rxTlG3hx1IGJN^M7{$j(9V;MZ zRKybgVuxKo#XVM+?*yTy{W+XHaU5Jbt-UG33x{u(N-2wmw;zzPH&4DE103HV@ER86 z|FZEmQb|&1s5#`$4!Cm}&`^{(4V}OP$bk`}v6q6rm;P!H)W|2i^e{7lTk2W@jo_9q z*aw|U7#+g59Fv(5qI`#O-qPj#@_P>PC#I(GSp3DLv7x-dmYK=C7lPF8a)bxb=@)B1 zUZ`EqpXV2dR}B&r`uM}N(TS99ZT0UB%IN|0H%DcVO#T%L_chrgn#m6%x4KE*IMfjX zJ%4veCEqbXZ`H`F_+fELMC@wuy_ch%t*+Z+1I}wN#C+dRrf2X{1C8=yZ_%Pt6wL_~ zZ2NN-hXOT4P4n$QFO7yYHS-4wF1Xfr-meG9Pn;uK51?hfel`d38k{W)F*|gJLT2#T z<~>spMu4(mul-8Q3*pf=N4DcI)zzjqAgbE2eOT7~&f1W3VsdD44Ffe;3mJp-V@8UC z)|qnPc12o~$X-+U@L_lWqv-RtvB~%hLF($%Ew5w>^NR82qC_0FB z)=hP1-OEx?lLi#jnLzH}a;Nvr@JDO-zQWd}#k^an$Kwml;MrD&)sC5b`s0ZkVyPkb zt}-jOq^%_9>YZe7Y}PhW{a)c39G`kg(P4@kxjcYfgB4XOOcmezdUI7j-!gs7oAo2o zx(Ph{G+YZ`a%~kzK!HTAA5NXE-7vOFRr5oqY$rH>WI6SFvWmahFav!CfRMM3%8J&c z*p+%|-fNS_@QrFr(at!JY9jCg9F-%5{nb5Bo~z@Y9m&SHYV`49GAJjA5h~h4(G!Se zZmK{Bo7ivCfvl}@A-ptkFGcWXAzj3xfl{evi-OG(TaCn1FAHxRc{}B|x+Ua1D=I6M z!C^ZIvK6aS_c&(=OQDZfm>O`Nxsw{ta&yiYPA~@e#c%N>>#rq)k6Aru-qD4(D^v)y z*>Rs;YUbD1S8^D(ps6Jbj0K3wJw>L4m)0e(6Pee3Y?gy9i0^bZO?$*sv+xKV?WBlh zAp*;v6w!a8;A7sLB*g-^<$Z4L7|5jXxxP1}hQZ<55f9<^KJ>^mKlWSGaLcO0=$jem zWyZkRwe~u{{tU63DlCaS9$Y4CP4f?+wwa(&1ou)b>72ydrFvm`Rj-0`kBJgK@nd(*Eh!(NC{F-@=FnF&Y!q`7){YsLLHf0_B6aHc# z>WIuHTyJwIH{BJ4)2RtEauC7Yq7Cytc|S)4^*t8Va3HR zg=~sN^tp9re@w=GTx$;zOWMjcg-7X3Wk^N$n;&Kf1RgVG2}2L-(0o)54C509C&77i zrjSi{X*WV=%C17((N^6R4Ya*4#6s_L99RtQ>m(%#nQ#wrRC8Y%yxkH;d!MdY+Tw@r zjpSnK`;C-U{ATcgaxoEpP0Gf+tx);buOMlK=01D|J+ROu37qc*rD(w`#O=3*O*w9?biwNoq3WN1`&Wp8TvKj3C z3HR9ssH7a&Vr<6waJrU zdLg!ieYz%U^bmpn%;(V%%ugMk92&?_XX1K@mwnVSE6!&%P%Wdi7_h`CpScvspMx?N zQUR>oadnG17#hNc$pkTp+9lW+MBKHRZ~74XWUryd)4yd zj98$%XmIL4(9OnoeO5Fnyn&fpQ9b0h4e6EHHw*l68j;>(ya`g^S&y2{O8U>1*>4zR zq*WSI_2o$CHQ?x0!wl9bpx|Cm2+kFMR)oMud1%n2=qn5nE&t@Fgr#=Zv2?}wtEz^T z9rrj=?IH*qI5{G@Rn&}^Z{+TW}mQeb9=8b<_a`&Cm#n%n~ zU47MvCBsdXFB1+adOO)03+nczfWa#vwk#r{o{dF)QWya9v2nv43Zp3%Ps}($lA02*_g25t;|T{A5snSY?3A zrRQ~(Ygh_ebltHo1VCbJb*eOAr;4cnlXLvI>*$-#AVsGg6B1r7@;g^L zFlJ_th0vxO7;-opU@WAFe;<}?!2q?RBrFK5U{*ai@NLKZ^};Ul}beukveh?TQn;$%9=R+DX07m82gP$=}Uo_%&ngV`}Hyv8g{u z3SWzTGV|cwQuFIs7ZDOqO_fGf8Q`8MwL}eUp>q?4eqCmOTcwQuXtQckPy|4F1on8l zP*h>d+cH#XQf|+6c|S{7SF(Lg>bR~l(0uY?O{OEVlaxa5@e%T&xju=o1`=OD#qc16 zSvyH*my(dcp6~VqR;o(#@m44Lug@~_qw+HA=mS#Z^4reBy8iV?H~I;{LQWk3aKK8$bLRyt$g?- { - const notifier = require('node-notifier') - - return (severity, errors) => { - if (severity !== 'error') return - - const error = errors[0] - const filename = error.file && error.file.split('!').pop() - - notifier.notify({ - title: packageConfig.name, - message: severity + ': ' + error.name, - subtitle: filename || '', - icon: path.join(__dirname, 'logo.png') - }) - } -} diff --git a/dmriprepViewer/build/vue-loader.conf.js b/dmriprepViewer/build/vue-loader.conf.js deleted file mode 100644 index 33ed58bc..00000000 --- a/dmriprepViewer/build/vue-loader.conf.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict' -const utils = require('./utils') -const config = require('../config') -const isProduction = process.env.NODE_ENV === 'production' -const sourceMapEnabled = isProduction - ? config.build.productionSourceMap - : config.dev.cssSourceMap - -module.exports = { - loaders: utils.cssLoaders({ - sourceMap: sourceMapEnabled, - extract: isProduction - }), - cssSourceMap: sourceMapEnabled, - cacheBusting: config.dev.cacheBusting, - transformToRequire: { - video: ['src', 'poster'], - source: 'src', - img: 'src', - image: 'xlink:href' - } -} diff --git a/dmriprepViewer/build/webpack.base.conf.js b/dmriprepViewer/build/webpack.base.conf.js deleted file mode 100644 index 1f4f47e4..00000000 --- a/dmriprepViewer/build/webpack.base.conf.js +++ /dev/null @@ -1,92 +0,0 @@ -'use strict' -const path = require('path') -const utils = require('./utils') -const config = require('../config') -const vueLoaderConfig = require('./vue-loader.conf') - -function resolve (dir) { - return path.join(__dirname, '..', dir) -} - -const createLintingRule = () => ({ - test: /\.(js|vue)$/, - loader: 'eslint-loader', - enforce: 'pre', - include: [resolve('src'), resolve('test')], - options: { - formatter: require('eslint-friendly-formatter'), - emitWarning: !config.dev.showEslintErrorsInOverlay - } -}) - -module.exports = { - context: path.resolve(__dirname, '../'), - entry: { - app: './src/main.js' - }, - output: { - path: config.build.assetsRoot, - filename: '[name].js', - publicPath: process.env.NODE_ENV === 'production' - ? config.build.assetsPublicPath - : config.dev.assetsPublicPath - }, - resolve: { - extensions: ['.js', '.vue', '.json'], - alias: { - 'vue$': 'vue/dist/vue.esm.js', - '@': resolve('src'), - } - }, - module: { - rules: [ - ...(config.dev.useEslint ? [createLintingRule()] : []), - { - test: /\.vue$/, - loader: 'vue-loader', - options: vueLoaderConfig - }, - { - test: /\.js$/, - loader: 'babel-loader', - include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] - }, - { - test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, - loader: 'url-loader', - options: { - limit: 10000, - name: utils.assetsPath('img/[name].[hash:7].[ext]') - } - }, - { - test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, - loader: 'url-loader', - options: { - limit: 10000, - name: utils.assetsPath('media/[name].[hash:7].[ext]') - } - }, - { - test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, - loader: 'url-loader', - options: { - limit: 10000, - name: utils.assetsPath('fonts/[name].[hash:7].[ext]') - } - } - ] - }, - node: { - // prevent webpack from injecting useless setImmediate polyfill because Vue - // source contains it (although only uses it if it's native). - setImmediate: false, - // prevent webpack from injecting mocks to Node native modules - // that does not make sense for the client - dgram: 'empty', - fs: 'empty', - net: 'empty', - tls: 'empty', - child_process: 'empty' - } -} diff --git a/dmriprepViewer/build/webpack.dev.conf.js b/dmriprepViewer/build/webpack.dev.conf.js deleted file mode 100755 index 070ae221..00000000 --- a/dmriprepViewer/build/webpack.dev.conf.js +++ /dev/null @@ -1,95 +0,0 @@ -'use strict' -const utils = require('./utils') -const webpack = require('webpack') -const config = require('../config') -const merge = require('webpack-merge') -const path = require('path') -const baseWebpackConfig = require('./webpack.base.conf') -const CopyWebpackPlugin = require('copy-webpack-plugin') -const HtmlWebpackPlugin = require('html-webpack-plugin') -const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') -const portfinder = require('portfinder') - -const HOST = process.env.HOST -const PORT = process.env.PORT && Number(process.env.PORT) - -const devWebpackConfig = merge(baseWebpackConfig, { - module: { - rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) - }, - // cheap-module-eval-source-map is faster for development - devtool: config.dev.devtool, - - // these devServer options should be customized in /config/index.js - devServer: { - clientLogLevel: 'warning', - historyApiFallback: { - rewrites: [ - { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, - ], - }, - hot: true, - contentBase: false, // since we use CopyWebpackPlugin. - compress: true, - host: HOST || config.dev.host, - port: PORT || config.dev.port, - open: config.dev.autoOpenBrowser, - overlay: config.dev.errorOverlay - ? { warnings: false, errors: true } - : false, - publicPath: config.dev.assetsPublicPath, - proxy: config.dev.proxyTable, - quiet: true, // necessary for FriendlyErrorsPlugin - watchOptions: { - poll: config.dev.poll, - } - }, - plugins: [ - new webpack.DefinePlugin({ - 'process.env': require('../config/dev.env') - }), - new webpack.HotModuleReplacementPlugin(), - new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. - new webpack.NoEmitOnErrorsPlugin(), - // https://github.com/ampedandwired/html-webpack-plugin - new HtmlWebpackPlugin({ - filename: 'index.html', - template: 'index.html', - inject: true - }), - // copy custom static assets - new CopyWebpackPlugin([ - { - from: path.resolve(__dirname, '../static'), - to: config.dev.assetsSubDirectory, - ignore: ['.*'] - } - ]) - ] -}) - -module.exports = new Promise((resolve, reject) => { - portfinder.basePort = process.env.PORT || config.dev.port - portfinder.getPort((err, port) => { - if (err) { - reject(err) - } else { - // publish the new Port, necessary for e2e tests - process.env.PORT = port - // add port to devServer config - devWebpackConfig.devServer.port = port - - // Add FriendlyErrorsPlugin - devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ - compilationSuccessInfo: { - messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], - }, - onErrors: config.dev.notifyOnErrors - ? utils.createNotifierCallback() - : undefined - })) - - resolve(devWebpackConfig) - } - }) -}) diff --git a/dmriprepViewer/build/webpack.prod.conf.js b/dmriprepViewer/build/webpack.prod.conf.js deleted file mode 100644 index 2f172596..00000000 --- a/dmriprepViewer/build/webpack.prod.conf.js +++ /dev/null @@ -1,149 +0,0 @@ -'use strict' -const path = require('path') -const utils = require('./utils') -const webpack = require('webpack') -const config = require('../config') -const merge = require('webpack-merge') -const baseWebpackConfig = require('./webpack.base.conf') -const CopyWebpackPlugin = require('copy-webpack-plugin') -const HtmlWebpackPlugin = require('html-webpack-plugin') -const ExtractTextPlugin = require('extract-text-webpack-plugin') -const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') -const UglifyJsPlugin = require('uglifyjs-webpack-plugin') - -const env = process.env.NODE_ENV === 'testing' - ? require('../config/test.env') - : require('../config/prod.env') - -const webpackConfig = merge(baseWebpackConfig, { - module: { - rules: utils.styleLoaders({ - sourceMap: config.build.productionSourceMap, - extract: true, - usePostCSS: true - }) - }, - devtool: config.build.productionSourceMap ? config.build.devtool : false, - output: { - path: config.build.assetsRoot, - filename: utils.assetsPath('js/[name].[chunkhash].js'), - chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') - }, - plugins: [ - // http://vuejs.github.io/vue-loader/en/workflow/production.html - new webpack.DefinePlugin({ - 'process.env': env - }), - new UglifyJsPlugin({ - uglifyOptions: { - compress: { - warnings: false - } - }, - sourceMap: config.build.productionSourceMap, - parallel: true - }), - // extract css into its own file - new ExtractTextPlugin({ - filename: utils.assetsPath('css/[name].[contenthash].css'), - // Setting the following option to `false` will not extract CSS from codesplit chunks. - // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. - // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, - // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 - allChunks: true, - }), - // Compress extracted CSS. We are using this plugin so that possible - // duplicated CSS from different components can be deduped. - new OptimizeCSSPlugin({ - cssProcessorOptions: config.build.productionSourceMap - ? { safe: true, map: { inline: false } } - : { safe: true } - }), - // generate dist index.html with correct asset hash for caching. - // you can customize output by editing /index.html - // see https://github.com/ampedandwired/html-webpack-plugin - new HtmlWebpackPlugin({ - filename: process.env.NODE_ENV === 'testing' - ? 'index.html' - : config.build.index, - template: 'index.html', - inject: true, - minify: { - removeComments: true, - collapseWhitespace: true, - removeAttributeQuotes: true - // more options: - // https://github.com/kangax/html-minifier#options-quick-reference - }, - // necessary to consistently work with multiple chunks via CommonsChunkPlugin - chunksSortMode: 'dependency' - }), - // keep module.id stable when vendor modules does not change - new webpack.HashedModuleIdsPlugin(), - // enable scope hoisting - new webpack.optimize.ModuleConcatenationPlugin(), - // split vendor js into its own file - new webpack.optimize.CommonsChunkPlugin({ - name: 'vendor', - minChunks (module) { - // any required modules inside node_modules are extracted to vendor - return ( - module.resource && - /\.js$/.test(module.resource) && - module.resource.indexOf( - path.join(__dirname, '../node_modules') - ) === 0 - ) - } - }), - // extract webpack runtime and module manifest to its own file in order to - // prevent vendor hash from being updated whenever app bundle is updated - new webpack.optimize.CommonsChunkPlugin({ - name: 'manifest', - minChunks: Infinity - }), - // This instance extracts shared chunks from code splitted chunks and bundles them - // in a separate chunk, similar to the vendor chunk - // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk - new webpack.optimize.CommonsChunkPlugin({ - name: 'app', - async: 'vendor-async', - children: true, - minChunks: 3 - }), - - // copy custom static assets - new CopyWebpackPlugin([ - { - from: path.resolve(__dirname, '../static'), - to: config.build.assetsSubDirectory, - ignore: ['.*'] - } - ]) - ] -}) - -if (config.build.productionGzip) { - const CompressionWebpackPlugin = require('compression-webpack-plugin') - - webpackConfig.plugins.push( - new CompressionWebpackPlugin({ - asset: '[path].gz[query]', - algorithm: 'gzip', - test: new RegExp( - '\\.(' + - config.build.productionGzipExtensions.join('|') + - ')$' - ), - threshold: 10240, - minRatio: 0.8 - }) - ) -} - -if (config.build.bundleAnalyzerReport) { - const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin - webpackConfig.plugins.push(new BundleAnalyzerPlugin()) -} - -module.exports = webpackConfig diff --git a/dmriprepViewer/config/dev.env.js b/dmriprepViewer/config/dev.env.js deleted file mode 100644 index 1e22973a..00000000 --- a/dmriprepViewer/config/dev.env.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict' -const merge = require('webpack-merge') -const prodEnv = require('./prod.env') - -module.exports = merge(prodEnv, { - NODE_ENV: '"development"' -}) diff --git a/dmriprepViewer/config/index.js b/dmriprepViewer/config/index.js deleted file mode 100644 index c65b7762..00000000 --- a/dmriprepViewer/config/index.js +++ /dev/null @@ -1,76 +0,0 @@ -'use strict' -// Template version: 1.3.1 -// see http://vuejs-templates.github.io/webpack for documentation. - -const path = require('path') - -module.exports = { - dev: { - - // Paths - assetsSubDirectory: 'static', - assetsPublicPath: '/', - proxyTable: {}, - - // Various Dev Server settings - host: 'localhost', // can be overwritten by process.env.HOST - port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined - autoOpenBrowser: false, - errorOverlay: true, - notifyOnErrors: true, - poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- - - // Use Eslint Loader? - // If true, your code will be linted during bundling and - // linting errors and warnings will be shown in the console. - useEslint: true, - // If true, eslint errors and warnings will also be shown in the error overlay - // in the browser. - showEslintErrorsInOverlay: false, - - /** - * Source Maps - */ - - // https://webpack.js.org/configuration/devtool/#development - devtool: 'cheap-module-eval-source-map', - - // If you have problems debugging vue-files in devtools, - // set this to false - it *may* help - // https://vue-loader.vuejs.org/en/options.html#cachebusting - cacheBusting: true, - - cssSourceMap: true - }, - - build: { - // Template for index.html - index: path.resolve(__dirname, '../dist/index.html'), - - // Paths - assetsRoot: path.resolve(__dirname, '../dist'), - assetsSubDirectory: 'static', - assetsPublicPath: '/dmriprep/', - - /** - * Source Maps - */ - - productionSourceMap: true, - // https://webpack.js.org/configuration/devtool/#production - devtool: '#source-map', - - // Gzip off by default as many popular static hosts such as - // Surge or Netlify already gzip all static assets for you. - // Before setting to `true`, make sure to: - // npm install --save-dev compression-webpack-plugin - productionGzip: false, - productionGzipExtensions: ['js', 'css'], - - // Run the build command with an extra argument to - // View the bundle analyzer report after build finishes: - // `npm run build --report` - // Set to `true` or `false` to always turn it on or off - bundleAnalyzerReport: process.env.npm_config_report - } -} diff --git a/dmriprepViewer/config/prod.env.js b/dmriprepViewer/config/prod.env.js deleted file mode 100644 index a6f99761..00000000 --- a/dmriprepViewer/config/prod.env.js +++ /dev/null @@ -1,4 +0,0 @@ -'use strict' -module.exports = { - NODE_ENV: '"production"' -} diff --git a/dmriprepViewer/config/test.env.js b/dmriprepViewer/config/test.env.js deleted file mode 100644 index c2824a30..00000000 --- a/dmriprepViewer/config/test.env.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict' -const merge = require('webpack-merge') -const devEnv = require('./dev.env') - -module.exports = merge(devEnv, { - NODE_ENV: '"testing"' -}) diff --git a/dmriprepViewer/deploy.sh b/dmriprepViewer/deploy.sh deleted file mode 100644 index ae136ca1..00000000 --- a/dmriprepViewer/deploy.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -set -e # exit with nonzero exit code if anything fails - -if [[ $TRAVIS_BRANCH == "master" && $TRAVIS_PULL_REQUEST == "false" ]]; then - -echo "Starting to update gh-pages\n" - -#copy data we're interested in to other place -cp -R dist $HOME/dist - -#go to home and setup git -cd $HOME -git config --global user.email "travis@travis-ci.org" -git config --global user.name "Travis" - -#using token clone gh-pages branch -git clone --quiet --branch=gh-pages https://${GH_TOKEN}@github.com/${GH_USER}/${GH_REPO}.git gh-pages > /dev/null - -#go into directory and copy data we're interested in to that directory -cd gh-pages -cp -Rf $HOME/dist/* . - -echo "Allow files with underscore https://help.github.com/articles/files-that-start-with-an-underscore-are-missing/" > .nojekyll -echo "[View live](https://${GH_USER}.github.io/${GH_REPO}/)" > README.md - -#add, commit and push files -git add -f . -git commit -m "Travis build $TRAVIS_BUILD_NUMBER" -git push -fq origin gh-pages > /dev/null - -echo "Done updating gh-pages\n" - -else - echo "Skipped updating gh-pages, because build is not triggered from the master branch." -fi; diff --git a/dmriprepViewer/index.html b/dmriprepViewer/index.html deleted file mode 100644 index c2581b52..00000000 --- a/dmriprepViewer/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - dmriprep-viewer - - -
- - - diff --git a/dmriprepViewer/package-lock.json b/dmriprepViewer/package-lock.json deleted file mode 100644 index f76ffb85..00000000 --- a/dmriprepViewer/package-lock.json +++ /dev/null @@ -1,15340 +0,0 @@ -{ - "name": "dmriprep-viewer", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@babel/code-frame": { - "version": "7.0.0-beta.44", - "resolved": "http://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.44.tgz", - "integrity": "sha512-cuAuTTIQ9RqcFRJ/Y8PvTh+paepNcaGxwQwjIDRWPXmzzyAeCO4KqS9ikMvq0MCbRk6GlYKwfzStrcP3/jSL8g==", - "dev": true, - "requires": { - "@babel/highlight": "7.0.0-beta.44" - } - }, - "@babel/generator": { - "version": "7.0.0-beta.44", - "resolved": "http://registry.npmjs.org/@babel/generator/-/generator-7.0.0-beta.44.tgz", - "integrity": "sha512-5xVb7hlhjGcdkKpMXgicAVgx8syK5VJz193k0i/0sLP6DzE6lRrU1K3B/rFefgdo9LPGMAOOOAWW4jycj07ShQ==", - "dev": true, - "requires": { - "@babel/types": "7.0.0-beta.44", - "jsesc": "^2.5.1", - "lodash": "^4.2.0", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" - }, - "dependencies": { - "jsesc": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.1.tgz", - "integrity": "sha1-5CGiqOINawgZ3yiQj3glJrlt0f4=", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "@babel/helper-function-name": { - "version": "7.0.0-beta.44", - "resolved": "http://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.44.tgz", - "integrity": "sha512-MHRG2qZMKMFaBavX0LWpfZ2e+hLloT++N7rfM3DYOMUOGCD8cVjqZpwiL8a0bOX3IYcQev1ruciT0gdFFRTxzg==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "7.0.0-beta.44", - "@babel/template": "7.0.0-beta.44", - "@babel/types": "7.0.0-beta.44" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.0.0-beta.44", - "resolved": "http://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.44.tgz", - "integrity": "sha512-w0YjWVwrM2HwP6/H3sEgrSQdkCaxppqFeJtAnB23pRiJB5E/O9Yp7JAAeWBl+gGEgmBFinnTyOv2RN7rcSmMiw==", - "dev": true, - "requires": { - "@babel/types": "7.0.0-beta.44" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.0.0-beta.44", - "resolved": "http://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.44.tgz", - "integrity": "sha512-aQ7QowtkgKKzPGf0j6u77kBMdUFVBKNHw2p/3HX/POt5/oz8ec5cs0GwlgM8Hz7ui5EwJnzyfRmkNF1Nx1N7aA==", - "dev": true, - "requires": { - "@babel/types": "7.0.0-beta.44" - } - }, - "@babel/highlight": { - "version": "7.0.0-beta.44", - "resolved": "http://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.44.tgz", - "integrity": "sha512-Il19yJvy7vMFm8AVAh6OZzaFoAd0hbkeMZiX3P5HGD+z7dyI7RzndHB0dg6Urh/VAFfHtpOIzDUSxmY6coyZWQ==", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^3.0.0" - } - }, - "@babel/template": { - "version": "7.0.0-beta.44", - "resolved": "http://registry.npmjs.org/@babel/template/-/template-7.0.0-beta.44.tgz", - "integrity": "sha512-w750Sloq0UNifLx1rUqwfbnC6uSUk0mfwwgGRfdLiaUzfAOiH0tHJE6ILQIUi3KYkjiCDTskoIsnfqZvWLBDng==", - "dev": true, - "requires": { - "@babel/code-frame": "7.0.0-beta.44", - "@babel/types": "7.0.0-beta.44", - "babylon": "7.0.0-beta.44", - "lodash": "^4.2.0" - }, - "dependencies": { - "babylon": { - "version": "7.0.0-beta.44", - "resolved": "http://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.44.tgz", - "integrity": "sha512-5Hlm13BJVAioCHpImtFqNOF2H3ieTOHd0fmFGMxOJ9jgeFqeAwsv3u5P5cR7CSeFrkgHsT19DgFJkHV0/Mcd8g==", - "dev": true - } - } - }, - "@babel/traverse": { - "version": "7.0.0-beta.44", - "resolved": "http://registry.npmjs.org/@babel/traverse/-/traverse-7.0.0-beta.44.tgz", - "integrity": "sha512-UHuDz8ukQkJCDASKHf+oDt3FVUzFd+QYfuBIsiNu/4+/ix6pP/C+uQZJ6K1oEfbCMv/IKWbgDEh7fcsnIE5AtA==", - "dev": true, - "requires": { - "@babel/code-frame": "7.0.0-beta.44", - "@babel/generator": "7.0.0-beta.44", - "@babel/helper-function-name": "7.0.0-beta.44", - "@babel/helper-split-export-declaration": "7.0.0-beta.44", - "@babel/types": "7.0.0-beta.44", - "babylon": "7.0.0-beta.44", - "debug": "^3.1.0", - "globals": "^11.1.0", - "invariant": "^2.2.0", - "lodash": "^4.2.0" - }, - "dependencies": { - "babylon": { - "version": "7.0.0-beta.44", - "resolved": "http://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.44.tgz", - "integrity": "sha512-5Hlm13BJVAioCHpImtFqNOF2H3ieTOHd0fmFGMxOJ9jgeFqeAwsv3u5P5cR7CSeFrkgHsT19DgFJkHV0/Mcd8g==", - "dev": true - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "globals": { - "version": "11.7.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.7.0.tgz", - "integrity": "sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg==", - "dev": true - } - } - }, - "@babel/types": { - "version": "7.0.0-beta.44", - "resolved": "http://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.44.tgz", - "integrity": "sha512-5eTV4WRmqbaFM3v9gHAIljEQJU4Ssc6fxL61JN+Oe2ga/BwyjzjamwkCVVAQjHGuAX8i0BWo42dshL8eO5KfLQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.2.0", - "to-fast-properties": "^2.0.0" - }, - "dependencies": { - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - } - } - }, - "@types/commander": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/@types/commander/-/commander-2.12.2.tgz", - "integrity": "sha512-0QEFiR8ljcHp9bAbWxecjVRuAMr16ivPiGOw6KFQBVrVd0RQIcM3xKdRisH2EDWgVWujiYtHwhSkSUoAAGzH7Q==", - "dev": true, - "requires": { - "commander": "*" - } - }, - "@types/semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ==", - "dev": true - }, - "@types/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=", - "dev": true - }, - "@types/strip-json-comments": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", - "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", - "dev": true - }, - "abab": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz", - "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==", - "dev": true - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, - "accepts": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", - "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", - "dev": true, - "requires": { - "mime-types": "~2.1.18", - "negotiator": "0.6.1" - } - }, - "acorn": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.2.tgz", - "integrity": "sha512-cJrKCNcr2kv8dlDnbw+JPUGjHZzo4myaxOLmpOX8a+rgX94YeTcTMv/LFJUSByRpc+i4GgVnnhLxvMu/2Y+rqw==", - "dev": true - }, - "acorn-dynamic-import": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", - "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=", - "dev": true, - "requires": { - "acorn": "^4.0.3" - }, - "dependencies": { - "acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", - "dev": true - } - } - }, - "acorn-globals": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.1.0.tgz", - "integrity": "sha512-KjZwU26uG3u6eZcfGbTULzFcsoz6pegNKtHPksZPOUsiKo5bUmiBPa38FuHZ/Eun+XYh/JCCkS9AS3Lu4McQOQ==", - "dev": true, - "requires": { - "acorn": "^5.0.0" - } - }, - "acorn-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", - "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", - "dev": true, - "requires": { - "acorn": "^3.0.4" - }, - "dependencies": { - "acorn": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", - "dev": true - } - } - }, - "agent-base": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-2.1.1.tgz", - "integrity": "sha1-1t4Q1a9hMtW9aSQn1G/FOFOQlMc=", - "dev": true, - "requires": { - "extend": "~3.0.0", - "semver": "~5.0.1" - }, - "dependencies": { - "semver": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.0.3.tgz", - "integrity": "sha1-d0Zt5YnNXTyV8TiqeLxWmjy10no=", - "dev": true - } - } - }, - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, - "ajv-keywords": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", - "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", - "dev": true - }, - "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "dev": true, - "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" - } - }, - "alphanum-sort": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", - "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", - "dev": true - }, - "ansi-escapes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", - "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", - "dev": true - }, - "ansi-html": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", - "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", - "dev": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - } - } - }, - "append-transform": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz", - "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", - "dev": true, - "requires": { - "default-require-extensions": "^1.0.0" - } - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "dev": true, - "requires": { - "arr-flatten": "^1.0.1" - } - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true - }, - "array-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", - "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", - "dev": true - }, - "array-find": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-find/-/array-find-1.0.0.tgz", - "integrity": "sha1-bI4obRHtdoMn+OYuzuhzU8o+eLg=", - "dev": true - }, - "array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", - "dev": true - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", - "dev": true - }, - "array-includes": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", - "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.7.0" - } - }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "requires": { - "array-uniq": "^1.0.1" - } - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true - }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "assert": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", - "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", - "dev": true, - "requires": { - "util": "0.10.3" - }, - "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", - "dev": true - }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "dev": true, - "requires": { - "inherits": "2.0.1" - } - } - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, - "assertion-error": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz", - "integrity": "sha1-x/hUOP3UZrx8oWq5DIFRN5el0js=", - "dev": true - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true - }, - "ast-types": { - "version": "0.11.5", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.11.5.tgz", - "integrity": "sha512-oJjo+5e7/vEc2FBK8gUalV0pba4L3VdBIs2EKhOLHLcOd2FgQIVQN9xb0eZ9IjEWyAL7vq6fGJxOvVvdCHNyMw==", - "dev": true - }, - "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "dev": true - }, - "async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", - "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", - "dev": true, - "requires": { - "lodash": "^4.17.10" - } - }, - "async-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", - "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", - "dev": true - }, - "async-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true - }, - "autoprefixer": { - "version": "7.2.6", - "resolved": "http://registry.npmjs.org/autoprefixer/-/autoprefixer-7.2.6.tgz", - "integrity": "sha512-Iq8TRIB+/9eQ8rbGhcP7ct5cYb/3qjNYAR2SnzLCEcwF6rvVOax8+9+fccgXk4bEhQGjOZd5TLhsksmAdsbGqQ==", - "dev": true, - "requires": { - "browserslist": "^2.11.3", - "caniuse-lite": "^1.0.30000805", - "normalize-range": "^0.1.2", - "num2fraction": "^1.2.2", - "postcss": "^6.0.17", - "postcss-value-parser": "^3.2.3" - } - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", - "dev": true - }, - "axios": { - "version": "0.18.0", - "resolved": "http://registry.npmjs.org/axios/-/axios-0.18.0.tgz", - "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=", - "requires": { - "follow-redirects": "^1.3.0", - "is-buffer": "^1.1.5" - } - }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "babel-core": { - "version": "6.26.3", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", - "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", - "dev": true, - "requires": { - "babel-code-frame": "^6.26.0", - "babel-generator": "^6.26.0", - "babel-helpers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-register": "^6.26.0", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "convert-source-map": "^1.5.1", - "debug": "^2.6.9", - "json5": "^0.5.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.4", - "path-is-absolute": "^1.0.1", - "private": "^0.1.8", - "slash": "^1.0.0", - "source-map": "^0.5.7" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "babel-eslint": { - "version": "8.2.6", - "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-8.2.6.tgz", - "integrity": "sha512-aCdHjhzcILdP8c9lej7hvXKvQieyRt20SF102SIGyY4cUIiw6UaAtK4j2o3dXX74jEmy0TJ0CEhv4fTIM3SzcA==", - "dev": true, - "requires": { - "@babel/code-frame": "7.0.0-beta.44", - "@babel/traverse": "7.0.0-beta.44", - "@babel/types": "7.0.0-beta.44", - "babylon": "7.0.0-beta.44", - "eslint-scope": "3.7.1", - "eslint-visitor-keys": "^1.0.0" - }, - "dependencies": { - "babylon": { - "version": "7.0.0-beta.44", - "resolved": "http://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.44.tgz", - "integrity": "sha512-5Hlm13BJVAioCHpImtFqNOF2H3ieTOHd0fmFGMxOJ9jgeFqeAwsv3u5P5cR7CSeFrkgHsT19DgFJkHV0/Mcd8g==", - "dev": true - } - } - }, - "babel-generator": { - "version": "6.26.1", - "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", - "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", - "dev": true, - "requires": { - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "detect-indent": "^4.0.0", - "jsesc": "^1.3.0", - "lodash": "^4.17.4", - "source-map": "^0.5.7", - "trim-right": "^1.0.1" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "babel-helper-bindify-decorators": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.24.1.tgz", - "integrity": "sha1-FMGeXxQte0fxmlJDHlKxzLxAozA=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-builder-binary-assignment-operator-visitor": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", - "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", - "dev": true, - "requires": { - "babel-helper-explode-assignable-expression": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-helper-call-delegate": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", - "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", - "dev": true, - "requires": { - "babel-helper-hoist-variables": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-define-map": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", - "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", - "dev": true, - "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" - } - }, - "babel-helper-explode-assignable-expression": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", - "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-explode-class": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-explode-class/-/babel-helper-explode-class-6.24.1.tgz", - "integrity": "sha1-fcKjkQ3uAHBW4eMdZAztPVTqqes=", - "dev": true, - "requires": { - "babel-helper-bindify-decorators": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-function-name": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", - "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", - "dev": true, - "requires": { - "babel-helper-get-function-arity": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-get-function-arity": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", - "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-helper-hoist-variables": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", - "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-helper-optimise-call-expression": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", - "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-helper-regex": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", - "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" - } - }, - "babel-helper-remap-async-to-generator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", - "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", - "dev": true, - "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-replace-supers": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", - "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", - "dev": true, - "requires": { - "babel-helper-optimise-call-expression": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-vue-jsx-merge-props": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz", - "integrity": "sha512-gsLiKK7Qrb7zYJNgiXKpXblxbV5ffSwR0f5whkPAaBAR4fhi6bwRZxX9wBlIc5M/v8CCkXUbXZL4N/nSE97cqg==", - "dev": true - }, - "babel-helpers": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", - "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-jest": { - "version": "21.2.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-21.2.0.tgz", - "integrity": "sha512-O0W2qLoWu1QOoOGgxiR2JID4O6WSpxPiQanrkyi9SSlM0PJ60Ptzlck47lhtnr9YZO3zYOsxHwnyeWJ6AffoBQ==", - "dev": true, - "requires": { - "babel-plugin-istanbul": "^4.0.0", - "babel-preset-jest": "^21.2.0" - } - }, - "babel-loader": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-7.1.5.tgz", - "integrity": "sha512-iCHfbieL5d1LfOQeeVJEUyD9rTwBcP/fcEbRCfempxTDuqrKpu0AZjLAQHEQa3Yqyj9ORKe2iHfoj4rHLf7xpw==", - "dev": true, - "requires": { - "find-cache-dir": "^1.0.0", - "loader-utils": "^1.0.2", - "mkdirp": "^0.5.1" - } - }, - "babel-messages": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-check-es2015-constants": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", - "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-dynamic-import-node": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-1.2.0.tgz", - "integrity": "sha512-yeDwKaLgGdTpXL7RgGt5r6T4LmnTza/hUn5Ul8uZSGGMtEjYo13Nxai7SQaGCTEzUtg9Zq9qJn0EjEr7SeSlTQ==", - "dev": true, - "requires": { - "babel-plugin-syntax-dynamic-import": "^6.18.0" - } - }, - "babel-plugin-istanbul": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz", - "integrity": "sha512-PWP9FQ1AhZhS01T/4qLSKoHGY/xvkZdVBGlKM/HuxxS3+sC66HhTNR7+MpbO/so/cz/wY94MeSWJuP1hXIPfwQ==", - "dev": true, - "requires": { - "babel-plugin-syntax-object-rest-spread": "^6.13.0", - "find-up": "^2.1.0", - "istanbul-lib-instrument": "^1.10.1", - "test-exclude": "^4.2.1" - } - }, - "babel-plugin-jest-hoist": { - "version": "21.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-21.2.0.tgz", - "integrity": "sha512-yi5QuiVyyvhBUDLP4ButAnhYzkdrUwWDtvUJv71hjH3fclhnZg4HkDeqaitcR2dZZx/E67kGkRcPVjtVu+SJfQ==", - "dev": true - }, - "babel-plugin-jsx-event-modifiers": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/babel-plugin-jsx-event-modifiers/-/babel-plugin-jsx-event-modifiers-2.0.5.tgz", - "integrity": "sha512-tWGnCk0whZ+nZcj9tYLw4+y08tPJXqaEjIxRJZS6DkUUae72Kz4BsoGpxt/Kow7mmgQJpvFCw8IPLSNh5rkZCg==", - "dev": true - }, - "babel-plugin-jsx-v-model": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jsx-v-model/-/babel-plugin-jsx-v-model-2.0.3.tgz", - "integrity": "sha512-SIx3Y3XxwGEz56Q1atwr5GaZsxJ2IRYmn5dl38LFkaTAvjnbNQxsZHO+ylJPsd+Hmv+ixJBYYFEekPBTHwiGfQ==", - "dev": true, - "requires": { - "babel-plugin-syntax-jsx": "^6.18.0", - "html-tags": "^2.0.0", - "svg-tags": "^1.0.0" - } - }, - "babel-plugin-jsx-vue-functional": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jsx-vue-functional/-/babel-plugin-jsx-vue-functional-2.1.0.tgz", - "integrity": "sha1-VjCgyG/hkE0owwRl5r8c9xI1ojk=", - "dev": true - }, - "babel-plugin-syntax-async-functions": { - "version": "6.13.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", - "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=", - "dev": true - }, - "babel-plugin-syntax-async-generators": { - "version": "6.13.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz", - "integrity": "sha1-a8lj67FuzLrmuStZbrfzXDQqi5o=", - "dev": true - }, - "babel-plugin-syntax-class-properties": { - "version": "6.13.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz", - "integrity": "sha1-1+sjt5oxf4VDlixQW4J8fWysJ94=", - "dev": true - }, - "babel-plugin-syntax-decorators": { - "version": "6.13.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz", - "integrity": "sha1-MSVjtNvePMgGzuPkFszurd0RrAs=", - "dev": true - }, - "babel-plugin-syntax-dynamic-import": { - "version": "6.18.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", - "integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo=", - "dev": true - }, - "babel-plugin-syntax-exponentiation-operator": { - "version": "6.13.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", - "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", - "dev": true - }, - "babel-plugin-syntax-jsx": { - "version": "6.18.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", - "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=", - "dev": true - }, - "babel-plugin-syntax-object-rest-spread": { - "version": "6.13.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", - "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=", - "dev": true - }, - "babel-plugin-syntax-trailing-function-commas": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", - "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=", - "dev": true - }, - "babel-plugin-transform-async-generator-functions": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz", - "integrity": "sha1-8FiQAUX9PpkHpt3yjaWfIVJYpds=", - "dev": true, - "requires": { - "babel-helper-remap-async-to-generator": "^6.24.1", - "babel-plugin-syntax-async-generators": "^6.5.0", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-async-to-generator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", - "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", - "dev": true, - "requires": { - "babel-helper-remap-async-to-generator": "^6.24.1", - "babel-plugin-syntax-async-functions": "^6.8.0", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-class-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz", - "integrity": "sha1-anl2PqYdM9NvN7YRqp3vgagbRqw=", - "dev": true, - "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-plugin-syntax-class-properties": "^6.8.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-decorators": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.24.1.tgz", - "integrity": "sha1-eIAT2PjGtSIr33s0Q5Df13Vp4k0=", - "dev": true, - "requires": { - "babel-helper-explode-class": "^6.24.1", - "babel-plugin-syntax-decorators": "^6.13.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-arrow-functions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", - "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-block-scoped-functions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", - "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-block-scoping": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", - "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" - } - }, - "babel-plugin-transform-es2015-classes": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", - "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", - "dev": true, - "requires": { - "babel-helper-define-map": "^6.24.1", - "babel-helper-function-name": "^6.24.1", - "babel-helper-optimise-call-expression": "^6.24.1", - "babel-helper-replace-supers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-computed-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", - "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-destructuring": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", - "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-duplicate-keys": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", - "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-for-of": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", - "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-function-name": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", - "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", - "dev": true, - "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-literals": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", - "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-modules-amd": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", - "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", - "dev": true, - "requires": { - "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-modules-commonjs": { - "version": "6.26.2", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", - "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", - "dev": true, - "requires": { - "babel-plugin-transform-strict-mode": "^6.24.1", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-types": "^6.26.0" - } - }, - "babel-plugin-transform-es2015-modules-systemjs": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", - "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", - "dev": true, - "requires": { - "babel-helper-hoist-variables": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-modules-umd": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", - "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", - "dev": true, - "requires": { - "babel-plugin-transform-es2015-modules-amd": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-object-super": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", - "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", - "dev": true, - "requires": { - "babel-helper-replace-supers": "^6.24.1", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-parameters": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", - "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", - "dev": true, - "requires": { - "babel-helper-call-delegate": "^6.24.1", - "babel-helper-get-function-arity": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-shorthand-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", - "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-spread": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", - "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-sticky-regex": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", - "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", - "dev": true, - "requires": { - "babel-helper-regex": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-template-literals": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", - "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-typeof-symbol": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", - "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-unicode-regex": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", - "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", - "dev": true, - "requires": { - "babel-helper-regex": "^6.24.1", - "babel-runtime": "^6.22.0", - "regexpu-core": "^2.0.0" - } - }, - "babel-plugin-transform-exponentiation-operator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", - "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", - "dev": true, - "requires": { - "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1", - "babel-plugin-syntax-exponentiation-operator": "^6.8.0", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-object-rest-spread": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz", - "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=", - "dev": true, - "requires": { - "babel-plugin-syntax-object-rest-spread": "^6.8.0", - "babel-runtime": "^6.26.0" - } - }, - "babel-plugin-transform-regenerator": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", - "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", - "dev": true, - "requires": { - "regenerator-transform": "^0.10.0" - } - }, - "babel-plugin-transform-runtime": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.23.0.tgz", - "integrity": "sha1-iEkNRGUC6puOfvsP4J7E2ZR5se4=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-strict-mode": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", - "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-vue-jsx": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-vue-jsx/-/babel-plugin-transform-vue-jsx-3.7.0.tgz", - "integrity": "sha512-W39X07/n3oJMQd8tALBO+440NraGSF//Lo1ydd/9Nme3+QiRGFBb1Q39T9iixh0jZPPbfv3so18tNoIgLatymw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "babel-polyfill": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.23.0.tgz", - "integrity": "sha1-g2TKYt+Or7gwSZ9pkXdGbDsDSZ0=", - "requires": { - "babel-runtime": "^6.22.0", - "core-js": "^2.4.0", - "regenerator-runtime": "^0.10.0" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.10.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", - "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=" - } - } - }, - "babel-preset-env": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.7.0.tgz", - "integrity": "sha512-9OR2afuKDneX2/q2EurSftUYM0xGu4O2D9adAhVfADDhrYDaxXV0rBbevVYoY9n6nyX1PmQW/0jtpJvUNr9CHg==", - "dev": true, - "requires": { - "babel-plugin-check-es2015-constants": "^6.22.0", - "babel-plugin-syntax-trailing-function-commas": "^6.22.0", - "babel-plugin-transform-async-to-generator": "^6.22.0", - "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoping": "^6.23.0", - "babel-plugin-transform-es2015-classes": "^6.23.0", - "babel-plugin-transform-es2015-computed-properties": "^6.22.0", - "babel-plugin-transform-es2015-destructuring": "^6.23.0", - "babel-plugin-transform-es2015-duplicate-keys": "^6.22.0", - "babel-plugin-transform-es2015-for-of": "^6.23.0", - "babel-plugin-transform-es2015-function-name": "^6.22.0", - "babel-plugin-transform-es2015-literals": "^6.22.0", - "babel-plugin-transform-es2015-modules-amd": "^6.22.0", - "babel-plugin-transform-es2015-modules-commonjs": "^6.23.0", - "babel-plugin-transform-es2015-modules-systemjs": "^6.23.0", - "babel-plugin-transform-es2015-modules-umd": "^6.23.0", - "babel-plugin-transform-es2015-object-super": "^6.22.0", - "babel-plugin-transform-es2015-parameters": "^6.23.0", - "babel-plugin-transform-es2015-shorthand-properties": "^6.22.0", - "babel-plugin-transform-es2015-spread": "^6.22.0", - "babel-plugin-transform-es2015-sticky-regex": "^6.22.0", - "babel-plugin-transform-es2015-template-literals": "^6.22.0", - "babel-plugin-transform-es2015-typeof-symbol": "^6.23.0", - "babel-plugin-transform-es2015-unicode-regex": "^6.22.0", - "babel-plugin-transform-exponentiation-operator": "^6.22.0", - "babel-plugin-transform-regenerator": "^6.22.0", - "browserslist": "^3.2.6", - "invariant": "^2.2.2", - "semver": "^5.3.0" - }, - "dependencies": { - "browserslist": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz", - "integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30000844", - "electron-to-chromium": "^1.3.47" - } - } - } - }, - "babel-preset-jest": { - "version": "21.2.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-21.2.0.tgz", - "integrity": "sha512-hm9cBnr2h3J7yXoTtAVV0zg+3vg0Q/gT2GYuzlreTU0EPkJRtlNgKJJ3tBKEn0+VjAi3JykV6xCJkuUYttEEfA==", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^21.2.0", - "babel-plugin-syntax-object-rest-spread": "^6.13.0" - } - }, - "babel-preset-stage-2": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-stage-2/-/babel-preset-stage-2-6.24.1.tgz", - "integrity": "sha1-2eKWD7PXEYfw5k7sYrwHdnIZvcE=", - "dev": true, - "requires": { - "babel-plugin-syntax-dynamic-import": "^6.18.0", - "babel-plugin-transform-class-properties": "^6.24.1", - "babel-plugin-transform-decorators": "^6.24.1", - "babel-preset-stage-3": "^6.24.1" - } - }, - "babel-preset-stage-3": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz", - "integrity": "sha1-g2raCp56f6N8sTj7kyb4eTSkg5U=", - "dev": true, - "requires": { - "babel-plugin-syntax-trailing-function-commas": "^6.22.0", - "babel-plugin-transform-async-generator-functions": "^6.24.1", - "babel-plugin-transform-async-to-generator": "^6.24.1", - "babel-plugin-transform-exponentiation-operator": "^6.24.1", - "babel-plugin-transform-object-rest-spread": "^6.22.0" - } - }, - "babel-preset-vue": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/babel-preset-vue/-/babel-preset-vue-1.2.1.tgz", - "integrity": "sha512-a/Z+6SJ4GXyAoCMfYidDH6OzXnccPNJ5nEaPMjALqCkP9SJkqxz9V0uUS//sGuWszcD8kibdwJRzU+brl8DdFQ==", - "dev": true, - "requires": { - "babel-helper-vue-jsx-merge-props": "^2.0.2", - "babel-plugin-jsx-event-modifiers": "^2.0.2", - "babel-plugin-jsx-v-model": "^2.0.1", - "babel-plugin-jsx-vue-functional": "^2.1.0", - "babel-plugin-syntax-jsx": "^6.18.0", - "babel-plugin-transform-vue-jsx": "^3.5.0" - } - }, - "babel-preset-vue-app": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/babel-preset-vue-app/-/babel-preset-vue-app-1.3.2.tgz", - "integrity": "sha512-PLyyyVdrvgL4szMF7D5SuUhy85aBzy0+s5MO2QhpTwVqfW0qVaPFJi6K3d25CKz1nOV437JgpVvPj1W6tLGJ5g==", - "dev": true, - "requires": { - "babel-plugin-syntax-dynamic-import": "^6.18.0", - "babel-plugin-transform-object-rest-spread": "^6.26.0", - "babel-plugin-transform-runtime": "^6.15.0", - "babel-preset-env": "^1.6.0", - "babel-preset-vue": "^1.2.1", - "babel-runtime": "^6.20.0" - } - }, - "babel-register": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", - "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", - "dev": true, - "requires": { - "babel-core": "^6.26.0", - "babel-runtime": "^6.26.0", - "core-js": "^2.5.0", - "home-or-tmp": "^2.0.0", - "lodash": "^4.17.4", - "mkdirp": "^0.5.1", - "source-map-support": "^0.4.15" - } - }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - } - }, - "babel-template": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "lodash": "^4.17.4" - } - }, - "babel-traverse": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", - "dev": true, - "requires": { - "babel-code-frame": "^6.26.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "debug": "^2.6.8", - "globals": "^9.18.0", - "invariant": "^2.2.2", - "lodash": "^4.17.4" - } - }, - "babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" - } - }, - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - } - } - }, - "base64-js": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", - "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", - "dev": true - }, - "batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "optional": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "bfj-node4": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/bfj-node4/-/bfj-node4-5.3.1.tgz", - "integrity": "sha512-SOmOsowQWfXc7ybFARsK3C4MCOWzERaOMV/Fl3Tgjs+5dJWyzo3oa127jL44eMbQiAN17J7SvAs2TRxEScTUmg==", - "dev": true, - "requires": { - "bluebird": "^3.5.1", - "check-types": "^7.3.0", - "tryer": "^1.0.0" - } - }, - "big.js": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", - "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", - "dev": true - }, - "binary-extensions": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", - "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=", - "dev": true - }, - "bluebird": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.2.tgz", - "integrity": "sha512-dhHTWMI7kMx5whMQntl7Vr9C6BvV10lFXDAasnqnrMYhXVCzzk6IO9Fo2L75jXHT07WrOngL1WDXOp+yYS91Yg==", - "dev": true - }, - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true - }, - "body-parser": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", - "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", - "dev": true, - "requires": { - "bytes": "3.0.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.1", - "http-errors": "~1.6.2", - "iconv-lite": "0.4.19", - "on-finished": "~2.3.0", - "qs": "6.5.1", - "raw-body": "2.3.2", - "type-is": "~1.6.15" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", - "dev": true - }, - "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", - "dev": true - }, - "raw-body": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", - "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", - "dev": true, - "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.2", - "iconv-lite": "0.4.19", - "unpipe": "1.0.0" - }, - "dependencies": { - "depd": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", - "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", - "dev": true - }, - "http-errors": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", - "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", - "dev": true, - "requires": { - "depd": "1.1.1", - "inherits": "2.0.3", - "setprototypeof": "1.0.3", - "statuses": ">= 1.3.1 < 2" - } - } - } - }, - "setprototypeof": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", - "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=", - "dev": true - } - } - }, - "bonjour": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", - "dev": true, - "requires": { - "array-flatten": "^2.1.0", - "deep-equal": "^1.0.1", - "dns-equal": "^1.0.0", - "dns-txt": "^2.0.2", - "multicast-dns": "^6.0.1", - "multicast-dns-service-types": "^1.1.0" - }, - "dependencies": { - "array-flatten": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.1.tgz", - "integrity": "sha1-Qmu52oQJDBg42BLIFQryCoMx4pY=", - "dev": true - } - } - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", - "dev": true - }, - "bootstrap": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.1.3.tgz", - "integrity": "sha512-rDFIzgXcof0jDyjNosjv4Sno77X4KuPeFxG2XZZv1/Kc8DRVGVADdoQyyOVDwPqL36DDmtCQbrpMCqvpPLJQ0w==" - }, - "bootstrap-vue": { - "version": "2.0.0-rc.11", - "resolved": "https://registry.npmjs.org/bootstrap-vue/-/bootstrap-vue-2.0.0-rc.11.tgz", - "integrity": "sha512-LxR+oL8yKr1DVoWUWTX+XhiT0xaTMH6142u2VSFDm4tewTH8HIrzN2YIl7HLZrw2DIuE9bRMIdWJqqn3aQe7Hw==", - "requires": { - "bootstrap": "^4.1.1", - "lodash.get": "^4.4.2", - "lodash.startcase": "^4.4.0", - "opencollective": "^1.0.3", - "popper.js": "^1.12.9", - "vue-functional-data-merge": "^2.0.5" - }, - "dependencies": { - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" - } - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "dev": true, - "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" - } - }, - "brainsprite.js": { - "version": "git+https://git@github.com/SIMEXP/brainsprite.js.git#257f741c8231b4a58fa75212d58f67f671608513", - "from": "git+https://git@github.com/SIMEXP/brainsprite.js.git" - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true - }, - "browser-process-hrtime": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.2.tgz", - "integrity": "sha1-Ql1opY00R/AqBKqJQYf86K+Le44=", - "dev": true - }, - "browser-resolve": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", - "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", - "dev": true, - "requires": { - "resolve": "1.1.7" - }, - "dependencies": { - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - } - } - }, - "browser-stdout": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", - "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", - "dev": true - }, - "browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "dev": true, - "requires": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", - "dev": true, - "requires": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" - } - }, - "browserify-des": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", - "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "randombytes": "^2.0.1" - } - }, - "browserify-sign": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", - "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", - "dev": true, - "requires": { - "bn.js": "^4.1.1", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.2", - "elliptic": "^6.0.0", - "inherits": "^2.0.1", - "parse-asn1": "^5.0.0" - } - }, - "browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", - "dev": true, - "requires": { - "pako": "~1.0.5" - } - }, - "browserslist": { - "version": "2.11.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.11.3.tgz", - "integrity": "sha512-yWu5cXT7Av6mVwzWc8lMsJMHWn4xyjSuGYi4IozbVTLUOEYPSagUB8kiMDUHA1fS3zjr8nkxkn9jdvug4BBRmA==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30000792", - "electron-to-chromium": "^1.3.30" - } - }, - "bser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz", - "integrity": "sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk=", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "buffer": { - "version": "4.9.1", - "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", - "dev": true, - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "buffer-indexof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", - "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", - "dev": true - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", - "dev": true - }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true - }, - "builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", - "dev": true - }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "dev": true - }, - "cacache": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", - "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", - "dev": true, - "requires": { - "bluebird": "^3.5.1", - "chownr": "^1.0.1", - "glob": "^7.1.2", - "graceful-fs": "^4.1.11", - "lru-cache": "^4.1.1", - "mississippi": "^2.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.2", - "ssri": "^5.2.4", - "unique-filename": "^1.1.0", - "y18n": "^4.0.0" - } - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "caller-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", - "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", - "dev": true, - "requires": { - "callsites": "^0.2.0" - } - }, - "callsites": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", - "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", - "dev": true - }, - "camel-case": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", - "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", - "dev": true, - "requires": { - "no-case": "^2.2.0", - "upper-case": "^1.1.1" - } - }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "camelcase-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", - "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", - "dev": true, - "requires": { - "camelcase": "^2.0.0", - "map-obj": "^1.0.0" - }, - "dependencies": { - "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", - "dev": true - } - } - }, - "caniuse-api": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz", - "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=", - "dev": true, - "requires": { - "browserslist": "^1.3.6", - "caniuse-db": "^1.0.30000529", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - }, - "dependencies": { - "browserslist": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", - "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", - "dev": true, - "requires": { - "caniuse-db": "^1.0.30000639", - "electron-to-chromium": "^1.2.7" - } - } - } - }, - "caniuse-db": { - "version": "1.0.30000885", - "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000885.tgz", - "integrity": "sha512-Hy1a+UIXooG+tRlt3WnT9avMf+l999bR9J1MqlQdYKgbsYjKxV4a4rgcmiyMmdCLPBFsiRoDxdl9tnNyaq2RXw==", - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30000885", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000885.tgz", - "integrity": "sha512-cXKbYwpxBLd7qHyej16JazPoUacqoVuDhvR61U7Fr5vSxMUiodzcYa1rQYRYfZ5GexV03vGZHd722vNPLjPJGQ==", - "dev": true - }, - "capture-exit": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-1.2.0.tgz", - "integrity": "sha1-HF/MSJ/QqwDU8ax64QcuMXP7q28=", - "dev": true, - "requires": { - "rsvp": "^3.3.3" - } - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", - "dev": true, - "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" - } - }, - "chai-nightwatch": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/chai-nightwatch/-/chai-nightwatch-0.1.1.tgz", - "integrity": "sha1-HKVt52jTwIaP5/wvTTLC/olOa+k=", - "dev": true, - "requires": { - "assertion-error": "1.0.0", - "deep-eql": "0.1.3" - } - }, - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "chardet": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=" - }, - "check-types": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/check-types/-/check-types-7.4.0.tgz", - "integrity": "sha512-YbulWHdfP99UfZ73NcUDlNJhEIDgm9Doq9GhpyXbF+7Aegi3CVV7qqMCKTTqJxlvEvnQBp9IA+dxsGN6xK/nSg==", - "dev": true - }, - "chokidar": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", - "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", - "dev": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.0", - "braces": "^2.3.0", - "fsevents": "^1.2.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "lodash.debounce": "^4.0.8", - "normalize-path": "^2.1.1", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0", - "upath": "^1.0.5" - }, - "dependencies": { - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-glob": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", - "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "chownr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", - "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", - "dev": true - }, - "chromedriver": { - "version": "2.41.0", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-2.41.0.tgz", - "integrity": "sha512-6O9HxvrSuHqmRlIgMzi0/05GsDNHqs8kaF5gNTIyaZNwRzb/RBUWH1xNNXKNxyhXSnGSalH8hWsKP5mc/npSQQ==", - "dev": true, - "requires": { - "del": "^3.0.0", - "extract-zip": "^1.6.7", - "kew": "^0.7.0", - "mkdirp": "^0.5.1", - "request": "^2.87.0" - } - }, - "ci-info": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.4.0.tgz", - "integrity": "sha512-Oqmw2pVfCl8sCL+1QgMywPfdxPJPkC51y4usw0iiE2S9qnEOAqXy8bwl1CpMpnoU39g4iKJTz6QZj+28FvOnjQ==", - "dev": true - }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "circular-json": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", - "dev": true - }, - "clap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz", - "integrity": "sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==", - "dev": true, - "requires": { - "chalk": "^1.1.3" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "clean-css": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", - "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", - "dev": true, - "requires": { - "source-map": "~0.6.0" - } - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "cli-spinners": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-1.3.1.tgz", - "integrity": "sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg==", - "dev": true - }, - "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=" - }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "dev": true, - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "dev": true - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, - "coa": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz", - "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=", - "dev": true, - "requires": { - "q": "^1.1.2" - } - }, - "coalescy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/coalescy/-/coalescy-1.0.0.tgz", - "integrity": "sha1-SwZYRrg2NhrabEtKSr9LwcrDG/E=", - "dev": true - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, - "color": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", - "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=", - "dev": true, - "requires": { - "clone": "^1.0.2", - "color-convert": "^1.3.0", - "color-string": "^0.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "color-string": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", - "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", - "dev": true, - "requires": { - "color-name": "^1.0.0" - } - }, - "colormin": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz", - "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=", - "dev": true, - "requires": { - "color": "^0.11.0", - "css-color-names": "0.0.4", - "has": "^1.0.1" - } - }, - "colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", - "dev": true - }, - "combined-stream": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", - "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==" - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - }, - "compressible": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.14.tgz", - "integrity": "sha1-MmxfUH+7BV9UEWeCuWmoG2einac=", - "dev": true, - "requires": { - "mime-db": ">= 1.34.0 < 2" - } - }, - "compression": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.3.tgz", - "integrity": "sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==", - "dev": true, - "requires": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.14", - "debug": "2.6.9", - "on-headers": "~1.0.1", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "condense-newlines": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/condense-newlines/-/condense-newlines-0.2.1.tgz", - "integrity": "sha1-PemFVTE5R10yUCyDsC9gaE0kxV8=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-whitespace": "^0.3.0", - "kind-of": "^3.0.2" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "config-chain": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.11.tgz", - "integrity": "sha1-q6CXR9++TD5w52am5BWG4YWfxvI=", - "dev": true, - "requires": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - } - }, - "connect-history-api-fallback": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", - "integrity": "sha1-sGhzk0vF40T+9hGhlqb6rgruAVo=", - "dev": true - }, - "console-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", - "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", - "dev": true, - "requires": { - "date-now": "^0.1.4" - } - }, - "consolidate": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.14.5.tgz", - "integrity": "sha1-WiUEe8dvcwcmZ8jLUsmJiI9JTGM=", - "dev": true, - "requires": { - "bluebird": "^3.1.1" - } - }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", - "dev": true - }, - "contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", - "dev": true - }, - "content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", - "dev": true - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "dev": true - }, - "convert-source-map": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", - "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", - "dev": true - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", - "dev": true - }, - "copy-concurrently": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", - "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", - "dev": true, - "requires": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" - } - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true - }, - "copy-webpack-plugin": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.5.2.tgz", - "integrity": "sha512-zmC33E8FFSq3AbflTvqvPvBo621H36Afsxlui91d+QyZxPIuXghfnTsa1CuqiAaCPgJoSUWfTFbKJnadZpKEbQ==", - "dev": true, - "requires": { - "cacache": "^10.0.4", - "find-cache-dir": "^1.0.0", - "globby": "^7.1.1", - "is-glob": "^4.0.0", - "loader-utils": "^1.1.0", - "minimatch": "^3.0.4", - "p-limit": "^1.0.0", - "serialize-javascript": "^1.4.0" - }, - "dependencies": { - "globby": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", - "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "dir-glob": "^2.0.0", - "glob": "^7.1.2", - "ignore": "^3.3.5", - "pify": "^3.0.0", - "slash": "^1.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-glob": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", - "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "core-js": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", - "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "cosmiconfig": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-4.0.0.tgz", - "integrity": "sha512-6e5vDdrXZD+t5v0L8CrurPeybg4Fmf+FCSYxXKYVAqLUtyCSbuyqE059d0kDthTNRzKVjL7QMgNpEUlsoYH3iQ==", - "dev": true, - "requires": { - "is-directory": "^0.3.1", - "js-yaml": "^3.9.0", - "parse-json": "^4.0.0", - "require-from-string": "^2.0.1" - }, - "dependencies": { - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "js-yaml": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", - "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - } - } - }, - "create-ecdh": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", - "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.0.0" - } - }, - "create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dev": true, - "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "dev": true, - "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" - } - }, - "css-color-names": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", - "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", - "dev": true - }, - "css-loader": { - "version": "0.28.11", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.11.tgz", - "integrity": "sha512-wovHgjAx8ZIMGSL8pTys7edA1ClmzxHeY6n/d97gg5odgsxEgKjULPR0viqyC+FWMCL9sfqoC/QCUBo62tLvPg==", - "dev": true, - "requires": { - "babel-code-frame": "^6.26.0", - "css-selector-tokenizer": "^0.7.0", - "cssnano": "^3.10.0", - "icss-utils": "^2.1.0", - "loader-utils": "^1.0.2", - "lodash.camelcase": "^4.3.0", - "object-assign": "^4.1.1", - "postcss": "^5.0.6", - "postcss-modules-extract-imports": "^1.2.0", - "postcss-modules-local-by-default": "^1.2.0", - "postcss-modules-scope": "^1.1.0", - "postcss-modules-values": "^1.3.0", - "postcss-value-parser": "^3.3.0", - "source-list-map": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", - "dev": true, - "requires": { - "boolbase": "~1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "~1.0.1" - } - }, - "css-selector-tokenizer": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", - "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=", - "dev": true, - "requires": { - "cssesc": "^0.1.0", - "fastparse": "^1.1.1", - "regexpu-core": "^1.0.0" - }, - "dependencies": { - "regexpu-core": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", - "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", - "dev": true, - "requires": { - "regenerate": "^1.2.1", - "regjsgen": "^0.2.0", - "regjsparser": "^0.1.4" - } - } - } - }, - "css-what": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz", - "integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0=", - "dev": true - }, - "cssesc": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", - "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", - "dev": true - }, - "cssnano": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", - "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=", - "dev": true, - "requires": { - "autoprefixer": "^6.3.1", - "decamelize": "^1.1.2", - "defined": "^1.0.0", - "has": "^1.0.1", - "object-assign": "^4.0.1", - "postcss": "^5.0.14", - "postcss-calc": "^5.2.0", - "postcss-colormin": "^2.1.8", - "postcss-convert-values": "^2.3.4", - "postcss-discard-comments": "^2.0.4", - "postcss-discard-duplicates": "^2.0.1", - "postcss-discard-empty": "^2.0.1", - "postcss-discard-overridden": "^0.1.1", - "postcss-discard-unused": "^2.2.1", - "postcss-filter-plugins": "^2.0.0", - "postcss-merge-idents": "^2.1.5", - "postcss-merge-longhand": "^2.0.1", - "postcss-merge-rules": "^2.0.3", - "postcss-minify-font-values": "^1.0.2", - "postcss-minify-gradients": "^1.0.1", - "postcss-minify-params": "^1.0.4", - "postcss-minify-selectors": "^2.0.4", - "postcss-normalize-charset": "^1.1.0", - "postcss-normalize-url": "^3.0.7", - "postcss-ordered-values": "^2.1.0", - "postcss-reduce-idents": "^2.2.2", - "postcss-reduce-initial": "^1.0.0", - "postcss-reduce-transforms": "^1.0.3", - "postcss-svgo": "^2.1.1", - "postcss-unique-selectors": "^2.0.2", - "postcss-value-parser": "^3.2.3", - "postcss-zindex": "^2.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "autoprefixer": { - "version": "6.7.7", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", - "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", - "dev": true, - "requires": { - "browserslist": "^1.7.6", - "caniuse-db": "^1.0.30000634", - "normalize-range": "^0.1.2", - "num2fraction": "^1.2.2", - "postcss": "^5.2.16", - "postcss-value-parser": "^3.2.3" - } - }, - "browserslist": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", - "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", - "dev": true, - "requires": { - "caniuse-db": "^1.0.30000639", - "electron-to-chromium": "^1.2.7" - } - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "csso": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz", - "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=", - "dev": true, - "requires": { - "clap": "^1.0.9", - "source-map": "^0.5.3" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "cssom": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.4.tgz", - "integrity": "sha512-+7prCSORpXNeR4/fUP3rL+TzqtiFfhMvTd7uEqMdgPvLPt4+uzFUeufx5RHjGTACCargg/DiEt/moMQmvnfkog==", - "dev": true - }, - "cssstyle": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.1.1.tgz", - "integrity": "sha512-364AI1l/M5TYcFH83JnOH/pSqgaNnKmYgKrm0didZMGKWjQB60dymwWy1rKUgL3J1ffdq9xVi2yGLHdSjjSNog==", - "dev": true, - "requires": { - "cssom": "0.3.x" - } - }, - "cuint": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz", - "integrity": "sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs=", - "dev": true - }, - "currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", - "dev": true, - "requires": { - "array-find-index": "^1.0.1" - } - }, - "cyclist": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", - "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", - "dev": true - }, - "d": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", - "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", - "dev": true, - "requires": { - "es5-ext": "^0.10.9" - } - }, - "d3": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/d3/-/d3-5.7.0.tgz", - "integrity": "sha512-8KEIfx+dFm8PlbJN9PI0suazrZ41QcaAufsKE9PRcqYPWLngHIyWJZX96n6IQKePGgeSu0l7rtlueSSNq8Zc3g==", - "requires": { - "d3-array": "1", - "d3-axis": "1", - "d3-brush": "1", - "d3-chord": "1", - "d3-collection": "1", - "d3-color": "1", - "d3-contour": "1", - "d3-dispatch": "1", - "d3-drag": "1", - "d3-dsv": "1", - "d3-ease": "1", - "d3-fetch": "1", - "d3-force": "1", - "d3-format": "1", - "d3-geo": "1", - "d3-hierarchy": "1", - "d3-interpolate": "1", - "d3-path": "1", - "d3-polygon": "1", - "d3-quadtree": "1", - "d3-random": "1", - "d3-scale": "2", - "d3-scale-chromatic": "1", - "d3-selection": "1", - "d3-shape": "1", - "d3-time": "1", - "d3-time-format": "2", - "d3-timer": "1", - "d3-transition": "1", - "d3-voronoi": "1", - "d3-zoom": "1" - } - }, - "d3-array": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", - "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" - }, - "d3-axis": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.12.tgz", - "integrity": "sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ==" - }, - "d3-brush": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.0.6.tgz", - "integrity": "sha512-lGSiF5SoSqO5/mYGD5FAeGKKS62JdA1EV7HPrU2b5rTX4qEJJtpjaGLJngjnkewQy7UnGstnFd3168wpf5z76w==", - "requires": { - "d3-dispatch": "1", - "d3-drag": "1", - "d3-interpolate": "1", - "d3-selection": "1", - "d3-transition": "1" - } - }, - "d3-chord": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.6.tgz", - "integrity": "sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA==", - "requires": { - "d3-array": "1", - "d3-path": "1" - } - }, - "d3-collection": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz", - "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==" - }, - "d3-color": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.2.3.tgz", - "integrity": "sha512-x37qq3ChOTLd26hnps36lexMRhNXEtVxZ4B25rL0DVdDsGQIJGB18S7y9XDwlDD6MD/ZBzITCf4JjGMM10TZkw==" - }, - "d3-contour": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-1.3.2.tgz", - "integrity": "sha512-hoPp4K/rJCu0ladiH6zmJUEz6+u3lgR+GSm/QdM2BBvDraU39Vr7YdDCicJcxP1z8i9B/2dJLgDC1NcvlF8WCg==", - "requires": { - "d3-array": "^1.1.1" - } - }, - "d3-dispatch": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.5.tgz", - "integrity": "sha512-vwKx+lAqB1UuCeklr6Jh1bvC4SZgbSqbkGBLClItFBIYH4vqDJCA7qfoy14lXmJdnBOdxndAMxjCbImJYW7e6g==" - }, - "d3-drag": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.3.tgz", - "integrity": "sha512-8S3HWCAg+ilzjJsNtWW1Mutl74Nmzhb9yU6igspilaJzeZVFktmY6oO9xOh5TDk+BM2KrNFjttZNoJJmDnkjkg==", - "requires": { - "d3-dispatch": "1", - "d3-selection": "1" - } - }, - "d3-dsv": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.0.10.tgz", - "integrity": "sha512-vqklfpxmtO2ZER3fq/B33R/BIz3A1PV0FaZRuFM8w6jLo7sUX1BZDh73fPlr0s327rzq4H6EN1q9U+eCBCSN8g==", - "requires": { - "commander": "2", - "iconv-lite": "0.4", - "rw": "1" - } - }, - "d3-ease": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.5.tgz", - "integrity": "sha512-Ct1O//ly5y5lFM9YTdu+ygq7LleSgSE4oj7vUt9tPLHUi8VCV7QoizGpdWRWAwCO9LdYzIrQDg97+hGVdsSGPQ==" - }, - "d3-fetch": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-1.1.2.tgz", - "integrity": "sha512-S2loaQCV/ZeyTyIF2oP8D1K9Z4QizUzW7cWeAOAS4U88qOt3Ucf6GsmgthuYSdyB2HyEm4CeGvkQxWsmInsIVA==", - "requires": { - "d3-dsv": "1" - } - }, - "d3-force": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.1.2.tgz", - "integrity": "sha512-p1vcHAUF1qH7yR+e8ip7Bs61AHjLeKkIn8Z2gzwU2lwEf2wkSpWdjXG0axudTHsVFnYGlMkFaEsVy2l8tAg1Gw==", - "requires": { - "d3-collection": "1", - "d3-dispatch": "1", - "d3-quadtree": "1", - "d3-timer": "1" - } - }, - "d3-format": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.3.2.tgz", - "integrity": "sha512-Z18Dprj96ExragQ0DeGi+SYPQ7pPfRMtUXtsg/ChVIKNBCzjO8XYJvRTC1usblx52lqge56V5ect+frYTQc8WQ==" - }, - "d3-geo": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.11.1.tgz", - "integrity": "sha512-GsG7x9G9sykseLviOVSJ3h5yjw0ItLopOtuDQKUt1TRklEegCw5WAmnIpYYiCkSH/QgUMleAeE2xZK38Qb+1+Q==", - "requires": { - "d3-array": "1" - } - }, - "d3-hierarchy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.8.tgz", - "integrity": "sha512-L+GHMSZNwTpiq4rt9GEsNcpLa4M96lXMR8M/nMG9p5hBE0jy6C+3hWtyZMenPQdwla249iJy7Nx0uKt3n+u9+w==" - }, - "d3-interpolate": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.3.2.tgz", - "integrity": "sha512-NlNKGopqaz9qM1PXh9gBF1KSCVh+jSFErrSlD/4hybwoNX/gt1d8CDbDW+3i+5UOHhjC6s6nMvRxcuoMVNgL2w==", - "requires": { - "d3-color": "1" - } - }, - "d3-path": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.7.tgz", - "integrity": "sha512-q0cW1RpvA5c5ma2rch62mX8AYaiLX0+bdaSM2wxSU9tXjU4DNvkx9qiUvjkuWCj3p22UO/hlPivujqMiR9PDzA==" - }, - "d3-polygon": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.5.tgz", - "integrity": "sha512-RHhh1ZUJZfhgoqzWWuRhzQJvO7LavchhitSTHGu9oj6uuLFzYZVeBzaWTQ2qSO6bz2w55RMoOCf0MsLCDB6e0w==" - }, - "d3-quadtree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.5.tgz", - "integrity": "sha512-U2tjwDFbZ75JRAg8A+cqMvqPg1G3BE7UTJn3h8DHjY/pnsAfWdbJKgyfcy7zKjqGtLAmI0q8aDSeG1TVIKRaHQ==" - }, - "d3-random": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.2.tgz", - "integrity": "sha512-6AK5BNpIFqP+cx/sreKzNjWbwZQCSUatxq+pPRmFIQaWuoD+NrbVWw7YWpHiXpCQ/NanKdtGDuB+VQcZDaEmYQ==" - }, - "d3-scale": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.1.2.tgz", - "integrity": "sha512-bESpd64ylaKzCDzvULcmHKZTlzA/6DGSVwx7QSDj/EnX9cpSevsdiwdHFYI9ouo9tNBbV3v5xztHS2uFeOzh8Q==", - "requires": { - "d3-array": "^1.2.0", - "d3-collection": "1", - "d3-format": "1", - "d3-interpolate": "1", - "d3-time": "1", - "d3-time-format": "2" - } - }, - "d3-scale-chromatic": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-1.3.3.tgz", - "integrity": "sha512-BWTipif1CimXcYfT02LKjAyItX5gKiwxuPRgr4xM58JwlLocWbjPLI7aMEjkcoOQXMkYsmNsvv3d2yl/OKuHHw==", - "requires": { - "d3-color": "1", - "d3-interpolate": "1" - } - }, - "d3-selection": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.3.2.tgz", - "integrity": "sha512-OoXdv1nZ7h2aKMVg3kaUFbLLK5jXUFAMLD/Tu5JA96mjf8f2a9ZUESGY+C36t8R1WFeWk/e55hy54Ml2I62CRQ==" - }, - "d3-shape": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.2.2.tgz", - "integrity": "sha512-hUGEozlKecFZ2bOSNt7ENex+4Tk9uc/m0TtTEHBvitCBxUNjhzm5hS2GrrVRD/ae4IylSmxGeqX5tWC2rASMlQ==", - "requires": { - "d3-path": "1" - } - }, - "d3-time": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.0.10.tgz", - "integrity": "sha512-hF+NTLCaJHF/JqHN5hE8HVGAXPStEq6/omumPE/SxyHVrR7/qQxusFDo0t0c/44+sCGHthC7yNGFZIEgju0P8g==" - }, - "d3-time-format": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.1.3.tgz", - "integrity": "sha512-6k0a2rZryzGm5Ihx+aFMuO1GgelgIz+7HhB4PH4OEndD5q2zGn1mDfRdNrulspOfR6JXkb2sThhDK41CSK85QA==", - "requires": { - "d3-time": "1" - } - }, - "d3-timer": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.9.tgz", - "integrity": "sha512-rT34J5HnQUHhcLvhSB9GjCkN0Ddd5Y8nCwDBG2u6wQEeYxT/Lf51fTFFkldeib/sE/J0clIe0pnCfs6g/lRbyg==" - }, - "d3-transition": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.1.3.tgz", - "integrity": "sha512-tEvo3qOXL6pZ1EzcXxFcPNxC/Ygivu5NoBY6mbzidATAeML86da+JfVIUzon3dNM6UX6zjDx+xbYDmMVtTSjuA==", - "requires": { - "d3-color": "1", - "d3-dispatch": "1", - "d3-ease": "1", - "d3-interpolate": "1", - "d3-selection": "^1.1.0", - "d3-timer": "1" - } - }, - "d3-voronoi": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz", - "integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==" - }, - "d3-zoom": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.7.3.tgz", - "integrity": "sha512-xEBSwFx5Z9T3/VrwDkMt+mr0HCzv7XjpGURJ8lWmIC8wxe32L39eWHIasEe/e7Ox8MPU4p1hvH8PKN2olLzIBg==", - "requires": { - "d3-dispatch": "1", - "d3-drag": "1", - "d3-interpolate": "1", - "d3-selection": "1", - "d3-transition": "1" - } - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "data-uri-to-buffer": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz", - "integrity": "sha512-vKQ9DTQPN1FLYiiEEOQ6IBGFqvjCa5rSK3cWMy/Nespm5d/x3dGFT9UBZnkLxCwua/IXBi2TYnwTEpsOvhC4UQ==", - "dev": true - }, - "data-urls": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.0.1.tgz", - "integrity": "sha512-0HdcMZzK6ubMUnsMmQmG0AcLQPvbvb47R0+7CCZQCYgcd8OUWG91CG7sM6GoXgjz+WLl4ArFzHtBMy/QqSF4eg==", - "dev": true, - "requires": { - "abab": "^2.0.0", - "whatwg-mimetype": "^2.1.0", - "whatwg-url": "^7.0.0" - }, - "dependencies": { - "whatwg-url": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", - "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", - "dev": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - } - } - }, - "date-now": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", - "dev": true - }, - "de-indent": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", - "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true - }, - "deep-eql": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", - "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", - "dev": true, - "requires": { - "type-detect": "0.1.1" - } - }, - "deep-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", - "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", - "dev": true - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "default-require-extensions": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-1.0.0.tgz", - "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=", - "dev": true, - "requires": { - "strip-bom": "^2.0.0" - } - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - } - } - }, - "defined": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", - "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", - "dev": true - }, - "degenerator": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-1.0.4.tgz", - "integrity": "sha1-/PSQo37OJmRk2cxDGrmMWBnO0JU=", - "dev": true, - "requires": { - "ast-types": "0.x.x", - "escodegen": "1.x.x", - "esprima": "3.x.x" - }, - "dependencies": { - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "dev": true - } - } - }, - "del": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", - "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", - "dev": true, - "requires": { - "globby": "^6.1.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "p-map": "^1.1.1", - "pify": "^3.0.0", - "rimraf": "^2.2.8" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true - }, - "des.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", - "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", - "dev": true - }, - "detect-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", - "dev": true, - "requires": { - "repeating": "^2.0.0" - } - }, - "detect-newline": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", - "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", - "dev": true - }, - "detect-node": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", - "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", - "dev": true - }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" - } - }, - "dir-glob": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz", - "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==", - "dev": true, - "requires": { - "arrify": "^1.0.1", - "path-type": "^3.0.0" - }, - "dependencies": { - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", - "dev": true - }, - "dns-packet": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", - "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", - "dev": true, - "requires": { - "ip": "^1.1.0", - "safe-buffer": "^5.0.1" - }, - "dependencies": { - "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", - "dev": true - } - } - }, - "dns-txt": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", - "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", - "dev": true, - "requires": { - "buffer-indexof": "^1.0.0" - } - }, - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "dom-converter": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.1.4.tgz", - "integrity": "sha1-pF71cnuJDJv/5tfIduexnLDhfzs=", - "dev": true, - "requires": { - "utila": "~0.3" - }, - "dependencies": { - "utila": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz", - "integrity": "sha1-1+jn1+MJEHCSsF+NloiCTWM6QiY=", - "dev": true - } - } - }, - "dom-serializer": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", - "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", - "dev": true, - "requires": { - "domelementtype": "~1.1.1", - "entities": "~1.1.1" - }, - "dependencies": { - "domelementtype": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", - "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", - "dev": true - } - } - }, - "domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", - "dev": true - }, - "domelementtype": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", - "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=", - "dev": true - }, - "domexception": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", - "dev": true, - "requires": { - "webidl-conversions": "^4.0.2" - } - }, - "domhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.1.0.tgz", - "integrity": "sha1-0mRvXlf2w7qxHPbLBdPArPdBJZQ=", - "dev": true, - "requires": { - "domelementtype": "1" - } - }, - "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "dev": true, - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "duplexer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", - "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", - "dev": true - }, - "duplexify": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz", - "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==", - "dev": true, - "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - } - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "optional": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "editorconfig": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.0.tgz", - "integrity": "sha512-j7JBoj/bpNzvoTQylfRZSc85MlLNKWQiq5y6gwKhmqD2h1eZ+tH4AXbkhEJD468gjDna/XMx2YtSkCxBRX9OGg==", - "dev": true, - "requires": { - "@types/commander": "^2.11.0", - "@types/semver": "^5.4.0", - "commander": "^2.11.0", - "lru-cache": "^4.1.1", - "semver": "^5.4.1", - "sigmund": "^1.0.1" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", - "dev": true - }, - "ejs": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.5.7.tgz", - "integrity": "sha1-zIcsFoiArjxxiXYv1f/ACJbJUYo=", - "dev": true - }, - "electron-to-chromium": { - "version": "1.3.63", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.63.tgz", - "integrity": "sha512-Ec35NNY040HKuSxMAzBMgz/uUI78amSWpBUD9x2gN7R7gkb/wgAcClngWklcLP0/lm/g0UUYHnC/tUIlZj8UvQ==", - "dev": true - }, - "elliptic": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", - "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", - "dev": true, - "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" - } - }, - "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", - "dev": true - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "dev": true - }, - "encoding": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", - "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", - "requires": { - "iconv-lite": "~0.4.13" - } - }, - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "enhanced-resolve": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz", - "integrity": "sha1-TW5omzcl+GCQknzMhs2fFjW4ni4=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.2.0", - "tapable": "^0.1.8" - } - }, - "entities": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", - "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=", - "dev": true - }, - "errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "dev": true, - "requires": { - "prr": "~1.0.1" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "error-stack-parser": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.2.tgz", - "integrity": "sha512-E1fPutRDdIj/hohG0UpT5mayXNCxXP9d+snxFsPU9X0XgccOumKraa3juDMwTUyi7+Bu5+mCGagjg4IYeNbOdw==", - "dev": true, - "requires": { - "stackframe": "^1.0.4" - } - }, - "es-abstract": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz", - "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", - "dev": true, - "requires": { - "es-to-primitive": "^1.1.1", - "function-bind": "^1.1.1", - "has": "^1.0.1", - "is-callable": "^1.1.3", - "is-regex": "^1.0.4" - } - }, - "es-to-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", - "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", - "dev": true, - "requires": { - "is-callable": "^1.1.1", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.1" - } - }, - "es5-ext": { - "version": "0.10.46", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.46.tgz", - "integrity": "sha512-24XxRvJXNFwEMpJb3nOkiRJKRoupmjYmOPVlI65Qy2SrtxwOTB+g6ODjBKOtwEHbYrhWRty9xxOWLNdClT2djw==", - "dev": true, - "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.1", - "next-tick": "1" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "es6-map": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", - "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", - "es6-set": "~0.1.5", - "es6-symbol": "~3.1.1", - "event-emitter": "~0.3.5" - } - }, - "es6-set": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", - "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", - "es6-symbol": "3.1.1", - "event-emitter": "~0.3.5" - } - }, - "es6-symbol": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", - "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, - "es6-weak-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", - "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "^0.10.14", - "es6-iterator": "^2.0.1", - "es6-symbol": "^3.1.1" - } - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "escodegen": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.0.tgz", - "integrity": "sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw==", - "dev": true, - "requires": { - "esprima": "^3.1.3", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - }, - "dependencies": { - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "dev": true - } - } - }, - "escope": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", - "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", - "dev": true, - "requires": { - "es6-map": "^0.1.3", - "es6-weak-map": "^2.0.1", - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "eslint": { - "version": "4.19.1", - "resolved": "http://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz", - "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==", - "dev": true, - "requires": { - "ajv": "^5.3.0", - "babel-code-frame": "^6.22.0", - "chalk": "^2.1.0", - "concat-stream": "^1.6.0", - "cross-spawn": "^5.1.0", - "debug": "^3.1.0", - "doctrine": "^2.1.0", - "eslint-scope": "^3.7.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^3.5.4", - "esquery": "^1.0.0", - "esutils": "^2.0.2", - "file-entry-cache": "^2.0.0", - "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.0.1", - "ignore": "^3.3.3", - "imurmurhash": "^0.1.4", - "inquirer": "^3.0.6", - "is-resolvable": "^1.0.0", - "js-yaml": "^3.9.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.4", - "minimatch": "^3.0.2", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "pluralize": "^7.0.0", - "progress": "^2.0.0", - "regexpp": "^1.0.1", - "require-uncached": "^1.0.3", - "semver": "^5.3.0", - "strip-ansi": "^4.0.0", - "strip-json-comments": "~2.0.1", - "table": "4.0.2", - "text-table": "~0.2.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "globals": { - "version": "11.7.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.7.0.tgz", - "integrity": "sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg==", - "dev": true - }, - "js-yaml": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", - "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "eslint-config-airbnb-base": { - "version": "11.3.2", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-11.3.2.tgz", - "integrity": "sha512-/fhjt/VqzBA2SRsx7ErDtv6Ayf+XLw9LIOqmpBuHFCVwyJo2EtzGWMB9fYRFBoWWQLxmNmCpenNiH0RxyeS41w==", - "dev": true, - "requires": { - "eslint-restricted-globals": "^0.1.1" - } - }, - "eslint-friendly-formatter": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-friendly-formatter/-/eslint-friendly-formatter-3.0.0.tgz", - "integrity": "sha1-J4h0Q1psRuwdlPoLH/SU4w7wQpA=", - "dev": true, - "requires": { - "chalk": "^1.0.0", - "coalescy": "1.0.0", - "extend": "^3.0.0", - "minimist": "^1.2.0", - "text-table": "^0.2.0" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "eslint-import-resolver-node": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", - "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==", - "dev": true, - "requires": { - "debug": "^2.6.9", - "resolve": "^1.5.0" - } - }, - "eslint-import-resolver-webpack": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.8.4.tgz", - "integrity": "sha512-b6JxR57ruiMxq2tIu4T/SrYED5RKJfeBEs8u3+JWF+O2RxDmFpUH84c5uS1T5qiP0K4r0SL7CXhvd41hXdDlAg==", - "dev": true, - "requires": { - "array-find": "^1.0.0", - "debug": "^2.6.8", - "enhanced-resolve": "~0.9.0", - "find-root": "^0.1.1", - "has": "^1.0.1", - "interpret": "^1.0.0", - "is-absolute": "^0.2.3", - "lodash.get": "^3.7.0", - "node-libs-browser": "^1.0.0 || ^2.0.0", - "resolve": "^1.2.0", - "semver": "^5.3.0" - } - }, - "eslint-loader": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-1.9.0.tgz", - "integrity": "sha512-40aN976qSNPyb9ejTqjEthZITpls1SVKtwguahmH1dzGCwQU/vySE+xX33VZmD8csU0ahVNCtFlsPgKqRBiqgg==", - "dev": true, - "requires": { - "loader-fs-cache": "^1.0.0", - "loader-utils": "^1.0.2", - "object-assign": "^4.0.1", - "object-hash": "^1.1.4", - "rimraf": "^2.6.1" - } - }, - "eslint-module-utils": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.2.0.tgz", - "integrity": "sha1-snA2LNiLGkitMIl2zn+lTphBF0Y=", - "dev": true, - "requires": { - "debug": "^2.6.8", - "pkg-dir": "^1.0.0" - }, - "dependencies": { - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "^2.0.0" - } - }, - "pkg-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", - "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", - "dev": true, - "requires": { - "find-up": "^1.0.0" - } - } - } - }, - "eslint-plugin-import": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.14.0.tgz", - "integrity": "sha512-FpuRtniD/AY6sXByma2Wr0TXvXJ4nA/2/04VPlfpmUDPOpOY264x+ILiwnrk/k4RINgDAyFZByxqPUbSQ5YE7g==", - "dev": true, - "requires": { - "contains-path": "^0.1.0", - "debug": "^2.6.8", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.1", - "eslint-module-utils": "^2.2.0", - "has": "^1.0.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.3", - "read-pkg-up": "^2.0.0", - "resolve": "^1.6.0" - }, - "dependencies": { - "doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" - } - }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - } - }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "^2.0.0" - } - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } - } - }, - "eslint-plugin-vue": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-4.7.1.tgz", - "integrity": "sha512-esETKhVMI7Vdli70Wt4bvAwnZBJeM0pxVX9Yb0wWKxdCJc2EADalVYK/q2FzMw8oKN0wPMdqVCKS8kmR89recA==", - "dev": true, - "requires": { - "vue-eslint-parser": "^2.0.3" - } - }, - "eslint-restricted-globals": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz", - "integrity": "sha1-NfDVy8ZMLj7WLpO0saevBbp+1Nc=", - "dev": true - }, - "eslint-scope": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", - "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "eslint-visitor-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", - "dev": true - }, - "espree": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", - "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", - "dev": true, - "requires": { - "acorn": "^5.5.0", - "acorn-jsx": "^3.0.0" - } - }, - "esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", - "dev": true - }, - "esquery": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", - "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", - "dev": true, - "requires": { - "estraverse": "^4.0.0" - } - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "dev": true, - "requires": { - "estraverse": "^4.1.0" - } - }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", - "dev": true - }, - "event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, - "eventemitter3": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", - "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==", - "dev": true - }, - "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", - "dev": true - }, - "eventsource": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-0.1.6.tgz", - "integrity": "sha1-Cs7ehJ7X3RzMMsgRuxG5RNTykjI=", - "dev": true, - "requires": { - "original": ">=0.0.5" - } - }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, - "exec-sh": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.2.2.tgz", - "integrity": "sha512-FIUCJz1RbuS0FKTdaAafAByGS0CPvU3R0MeHxgtl+djzCc//F8HakL8GzmVNZanasTbTAY/3DRFA0KpVqj/eAw==", - "dev": true, - "requires": { - "merge": "^1.2.0" - } - }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "dev": true, - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true - }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "dev": true, - "requires": { - "is-posix-bracket": "^0.1.0" - } - }, - "expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", - "dev": true, - "requires": { - "fill-range": "^2.1.0" - } - }, - "expect": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/expect/-/expect-22.4.3.tgz", - "integrity": "sha512-XcNXEPehqn8b/jm8FYotdX0YrXn36qp4HWlrVT4ktwQas1l1LPxiVWncYnnL2eyMtKAmVIaG0XAp0QlrqJaxaA==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "jest-diff": "^22.4.3", - "jest-get-type": "^22.4.3", - "jest-matcher-utils": "^22.4.3", - "jest-message-util": "^22.4.3", - "jest-regex-util": "^22.4.3" - } - }, - "express": { - "version": "4.16.3", - "resolved": "http://registry.npmjs.org/express/-/express-4.16.3.tgz", - "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", - "dev": true, - "requires": { - "accepts": "~1.3.5", - "array-flatten": "1.1.1", - "body-parser": "1.18.2", - "content-disposition": "0.5.2", - "content-type": "~1.0.4", - "cookie": "0.3.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.1.1", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.3", - "qs": "6.5.1", - "range-parser": "~1.2.0", - "safe-buffer": "5.1.1", - "send": "0.16.2", - "serve-static": "1.13.2", - "setprototypeof": "1.1.0", - "statuses": "~1.4.0", - "type-is": "~1.6.16", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", - "dev": true - }, - "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", - "dev": true - }, - "statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", - "dev": true - } - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "external-editor": { - "version": "2.2.0", - "resolved": "http://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", - "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", - "requires": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", - "tmp": "^0.0.33" - } - }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "dev": true, - "requires": { - "is-extglob": "^1.0.0" - } - }, - "extract-text-webpack-plugin": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extract-text-webpack-plugin/-/extract-text-webpack-plugin-3.0.2.tgz", - "integrity": "sha512-bt/LZ4m5Rqt/Crl2HiKuAl/oqg0psx1tsTLkvWbJen1CtD+fftkZhMaQ9HOtY2gWsl2Wq+sABmMVi9z3DhKWQQ==", - "dev": true, - "requires": { - "async": "^2.4.1", - "loader-utils": "^1.1.0", - "schema-utils": "^0.3.0", - "webpack-sources": "^1.0.1" - } - }, - "extract-zip": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz", - "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=", - "dev": true, - "requires": { - "concat-stream": "1.6.2", - "debug": "2.6.9", - "mkdirp": "0.5.1", - "yauzl": "2.4.1" - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "fastparse": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz", - "integrity": "sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg=", - "dev": true - }, - "faye-websocket": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", - "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", - "dev": true, - "requires": { - "websocket-driver": ">=0.5.1" - } - }, - "fb-watchman": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.0.tgz", - "integrity": "sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=", - "dev": true, - "requires": { - "bser": "^2.0.0" - } - }, - "fd-slicer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", - "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", - "dev": true, - "requires": { - "pend": "~1.2.0" - } - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-entry-cache": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", - "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", - "dev": true, - "requires": { - "flat-cache": "^1.2.1", - "object-assign": "^4.0.1" - } - }, - "file-loader": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", - "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==", - "dev": true, - "requires": { - "loader-utils": "^1.0.2", - "schema-utils": "^0.4.5" - }, - "dependencies": { - "ajv": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz", - "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", - "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", - "dev": true - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "schema-utils": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", - "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0" - } - } - } - }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true - }, - "filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", - "dev": true - }, - "fileset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", - "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", - "dev": true, - "requires": { - "glob": "^7.0.3", - "minimatch": "^3.0.3" - } - }, - "filesize": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz", - "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==", - "dev": true - }, - "fill-range": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", - "dev": true, - "requires": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^3.0.0", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" - } - }, - "finalhandler": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", - "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", - "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "statuses": "~1.4.0", - "unpipe": "~1.0.0" - }, - "dependencies": { - "statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", - "dev": true - } - } - }, - "find-babel-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-babel-config/-/find-babel-config-1.1.0.tgz", - "integrity": "sha1-rMAQQ6Z0n+w0Qpvmtk9ULrtdY1U=", - "dev": true, - "requires": { - "json5": "^0.5.1", - "path-exists": "^3.0.0" - } - }, - "find-cache-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", - "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^1.0.0", - "pkg-dir": "^2.0.0" - } - }, - "find-root": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-0.1.2.tgz", - "integrity": "sha1-mNImfP8ZFsyvJ0OzoO6oHXnX3NE=", - "dev": true - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "flat-cache": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", - "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", - "dev": true, - "requires": { - "circular-json": "^0.3.1", - "del": "^2.0.2", - "graceful-fs": "^4.1.2", - "write": "^0.2.1" - }, - "dependencies": { - "del": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", - "dev": true, - "requires": { - "globby": "^5.0.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "rimraf": "^2.2.8" - } - }, - "globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - } - } - }, - "flatten": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", - "integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=", - "dev": true - }, - "flush-write-stream": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz", - "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.4" - } - }, - "follow-redirects": { - "version": "1.5.7", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.7.tgz", - "integrity": "sha512-NONJVIFiX7Z8k2WxfqBjtwqMifx7X42ORLFrOZ2LTKGj71G3C0kfdyTqGqr8fx5zSX6Foo/D95dgGWbPUiwnew==", - "requires": { - "debug": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - } - } - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, - "for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", - "dev": true, - "requires": { - "for-in": "^1.0.1" - } - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", - "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "1.0.6", - "mime-types": "^2.1.12" - } - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", - "dev": true - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "requires": { - "map-cache": "^0.2.2" - } - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "dev": true - }, - "friendly-errors-webpack-plugin": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.7.0.tgz", - "integrity": "sha512-K27M3VK30wVoOarP651zDmb93R9zF28usW4ocaK3mfQeIEI5BPht/EzZs5E8QLLwbLRJQMwscAjDxYPb1FuNiw==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "error-stack-parser": "^2.0.0", - "string-width": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } - }, - "fs-write-stream-atomic": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", - "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", - "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", - "dev": true, - "optional": true, - "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "debug": { - "version": "2.6.9", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.21", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safer-buffer": "^2.1.0" - } - }, - "ignore-walk": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "dev": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true - }, - "minipass": { - "version": "2.2.4", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "^5.1.1", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "needle": { - "version": "2.2.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "debug": "^2.1.2", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.10.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.0", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.1.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "npm-packlist": { - "version": "1.1.10", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.7", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "deep-extend": "^0.5.1", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.6.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "glob": "^7.0.5" - } - }, - "safe-buffer": { - "version": "5.1.1", - "bundled": true, - "dev": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "bundled": true, - "dev": true, - "optional": true - }, - "semver": { - "version": "5.5.0", - "bundled": true, - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "tar": { - "version": "4.4.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "chownr": "^1.0.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.2.4", - "minizlib": "^1.1.0", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.1", - "yallist": "^3.0.2" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "wide-align": { - "version": "1.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "string-width": "^1.0.2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "yallist": { - "version": "3.0.2", - "bundled": true, - "dev": true - } - } - }, - "ftp": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz", - "integrity": "sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0=", - "dev": true, - "requires": { - "readable-stream": "1.1.x", - "xregexp": "2.0.0" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", - "dev": true - }, - "get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - }, - "get-uri": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-2.0.2.tgz", - "integrity": "sha512-ZD325dMZOgerGqF/rF6vZXyFGTAay62svjQIT+X/oU2PtxYpFxvSkbsdi+oxIrsNxlZVd4y8wUDqkaExWTI/Cw==", - "dev": true, - "requires": { - "data-uri-to-buffer": "1", - "debug": "2", - "extend": "3", - "file-uri-to-path": "1", - "ftp": "~0.3.10", - "readable-stream": "2" - } - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", - "dev": true, - "requires": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" - } - }, - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dev": true, - "requires": { - "is-glob": "^2.0.0" - } - }, - "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", - "dev": true - }, - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - }, - "graceful-readlink": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", - "dev": true - }, - "growl": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", - "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=", - "dev": true - }, - "growly": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", - "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", - "dev": true - }, - "gzip-size": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-4.1.0.tgz", - "integrity": "sha1-iuCWJX6r59acRb4rZ8RIEk/7UXw=", - "dev": true, - "requires": { - "duplexer": "^0.1.1", - "pify": "^3.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "handle-thing": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", - "integrity": "sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ=", - "dev": true - }, - "handlebars": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.12.tgz", - "integrity": "sha512-RhmTekP+FZL+XNhwS1Wf+bTTZpdLougwt5pcgA1tuz6Jcx0fpH/7z0qd71RKnZHBCxIRBHfBOnio4gViPemNzA==", - "dev": true, - "requires": { - "async": "^2.5.0", - "optimist": "^0.6.1", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4" - } - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz", - "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==", - "dev": true, - "requires": { - "ajv": "^5.3.0", - "har-schema": "^2.0.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "hash-sum": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", - "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=", - "dev": true - }, - "hash.js": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.5.tgz", - "integrity": "sha512-eWI5HG9Np+eHV1KQhisXWwM+4EPPYe5dFX1UZZH7k/E3JzDEazVH+VGlZi6R94ZqImq+A3D1mCEtrFIfg/E7sA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "home-or-tmp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", - "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", - "dev": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.1" - } - }, - "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", - "dev": true - }, - "hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - } - }, - "html-comment-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.1.tgz", - "integrity": "sha1-ZouTd26q5V696POtRkswekljYl4=", - "dev": true - }, - "html-encoding-sniffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", - "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", - "dev": true, - "requires": { - "whatwg-encoding": "^1.0.1" - } - }, - "html-entities": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", - "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=", - "dev": true - }, - "html-minifier": { - "version": "3.5.20", - "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.20.tgz", - "integrity": "sha512-ZmgNLaTp54+HFKkONyLFEfs5dd/ZOtlquKaTnqIWFmx3Av5zG6ZPcV2d0o9XM2fXOTxxIf6eDcwzFFotke/5zA==", - "dev": true, - "requires": { - "camel-case": "3.0.x", - "clean-css": "4.2.x", - "commander": "2.17.x", - "he": "1.1.x", - "param-case": "2.1.x", - "relateurl": "0.2.x", - "uglify-js": "3.4.x" - } - }, - "html-tags": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-2.0.0.tgz", - "integrity": "sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos=", - "dev": true - }, - "html-webpack-plugin": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-2.30.1.tgz", - "integrity": "sha1-f5xCG36pHsRg9WUn1430hO51N9U=", - "dev": true, - "requires": { - "bluebird": "^3.4.7", - "html-minifier": "^3.2.3", - "loader-utils": "^0.2.16", - "lodash": "^4.17.3", - "pretty-error": "^2.0.2", - "toposort": "^1.0.0" - }, - "dependencies": { - "loader-utils": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", - "dev": true, - "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0", - "object-assign": "^4.0.1" - } - } - } - }, - "htmlparser2": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", - "integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=", - "dev": true, - "requires": { - "domelementtype": "1", - "domhandler": "2.1", - "domutils": "1.1", - "readable-stream": "1.0" - }, - "dependencies": { - "domutils": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.1.6.tgz", - "integrity": "sha1-vdw94Jm5ou+sxRxiPyj0FuzFdIU=", - "dev": true, - "requires": { - "domelementtype": "1" - } - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } - } - }, - "http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", - "dev": true - }, - "http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - } - }, - "http-parser-js": { - "version": "0.4.13", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.13.tgz", - "integrity": "sha1-O9bW/ebjFyyTNMOzO2wZPYD+ETc=", - "dev": true - }, - "http-proxy": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", - "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", - "dev": true, - "requires": { - "eventemitter3": "^3.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - } - }, - "http-proxy-agent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-1.0.0.tgz", - "integrity": "sha1-zBzjjkU7+YSg93AtLdWcc9CBKEo=", - "dev": true, - "requires": { - "agent-base": "2", - "debug": "2", - "extend": "3" - } - }, - "http-proxy-middleware": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.17.4.tgz", - "integrity": "sha1-ZC6ISIUdZvCdTxJJEoRtuutBuDM=", - "dev": true, - "requires": { - "http-proxy": "^1.16.2", - "is-glob": "^3.1.0", - "lodash": "^4.17.2", - "micromatch": "^2.3.11" - }, - "dependencies": { - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", - "dev": true - }, - "https-proxy-agent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz", - "integrity": "sha1-NffabEjOTdv6JkiRrFk+5f+GceY=", - "dev": true, - "requires": { - "agent-base": "2", - "debug": "2", - "extend": "3" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "icss-replace-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", - "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", - "dev": true - }, - "icss-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz", - "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=", - "dev": true, - "requires": { - "postcss": "^6.0.1" - } - }, - "ieee754": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", - "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==", - "dev": true - }, - "iferr": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", - "dev": true - }, - "ignore": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", - "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", - "dev": true - }, - "import-cwd": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", - "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=", - "dev": true, - "requires": { - "import-from": "^2.1.0" - } - }, - "import-from": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", - "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", - "dev": true, - "requires": { - "resolve-from": "^3.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", - "dev": true - } - } - }, - "import-local": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz", - "integrity": "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==", - "dev": true, - "requires": { - "pkg-dir": "^2.0.0", - "resolve-cwd": "^2.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "indent-string": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", - "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", - "dev": true, - "requires": { - "repeating": "^2.0.0" - } - }, - "indexes-of": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", - "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", - "dev": true - }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true - }, - "inquirer": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", - "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", - "dev": true, - "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^2.0.4", - "figures": "^2.0.0", - "lodash": "^4.3.0", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rx-lite": "^4.0.8", - "rx-lite-aggregates": "^4.0.8", - "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "internal-ip": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-1.2.0.tgz", - "integrity": "sha1-rp+/k7mEh4eF1QqN4bNWlWBYz1w=", - "dev": true, - "requires": { - "meow": "^3.3.0" - } - }, - "interpret": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", - "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", - "dev": true - }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, - "requires": { - "loose-envify": "^1.0.0" - } - }, - "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", - "dev": true - }, - "ip": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.0.1.tgz", - "integrity": "sha1-x+NWzeoiWucbNtcPLnGpK6TkJZA=", - "dev": true - }, - "ipaddr.js": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", - "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=", - "dev": true - }, - "is-absolute": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.2.6.tgz", - "integrity": "sha1-IN5p89uULvLYe5wto28XIjWxtes=", - "dev": true, - "requires": { - "is-relative": "^0.2.1", - "is-windows": "^0.2.0" - } - }, - "is-absolute-url": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", - "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", - "dev": true - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, - "requires": { - "binary-extensions": "^1.0.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "is-builtin-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", - "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", - "dev": true, - "requires": { - "builtin-modules": "^1.0.0" - } - }, - "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", - "dev": true - }, - "is-ci": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.0.tgz", - "integrity": "sha512-plgvKjQtalH2P3Gytb7L61Lmz95g2DlpzFiQyRSFew8WoJKxtKRzrZMeyRN2supblm3Psc8OQGy7Xjb6XG11jw==", - "dev": true, - "requires": { - "ci-info": "^1.3.0" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", - "dev": true - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "is-directory": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", - "dev": true - }, - "is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", - "dev": true - }, - "is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", - "dev": true, - "requires": { - "is-primitive": "^2.0.0" - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-finite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "is-generator-fn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-1.0.0.tgz", - "integrity": "sha1-lp1J4bszKfa7fwkIm+JleLLd1Go=", - "dev": true - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "^1.0.0" - } - }, - "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", - "dev": true - }, - "is-path-in-cwd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", - "dev": true, - "requires": { - "is-path-inside": "^1.0.0" - } - }, - "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "dev": true, - "requires": { - "path-is-inside": "^1.0.1" - } - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", - "dev": true - }, - "is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", - "dev": true - }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" - }, - "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "dev": true, - "requires": { - "has": "^1.0.1" - } - }, - "is-relative": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.2.1.tgz", - "integrity": "sha1-0n9MfVFtF1+2ENuEu+7yPDvJeqU=", - "dev": true, - "requires": { - "is-unc-path": "^0.1.1" - } - }, - "is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", - "dev": true - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, - "is-svg": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz", - "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=", - "dev": true, - "requires": { - "html-comment-regex": "^1.1.0" - } - }, - "is-symbol": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", - "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", - "dev": true - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-unc-path": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-0.1.2.tgz", - "integrity": "sha1-arBTpyVzwQJQ/0FqOBTDUXivObk=", - "dev": true, - "requires": { - "unc-path-regex": "^0.1.0" - } - }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true - }, - "is-whitespace": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-whitespace/-/is-whitespace-0.3.0.tgz", - "integrity": "sha1-Fjnssb4DauxppUy7QBz77XEUq38=", - "dev": true - }, - "is-windows": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz", - "integrity": "sha1-3hqm1j6indJIc3tp8f+LgALSEIw=", - "dev": true - }, - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "istanbul-api": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-1.3.7.tgz", - "integrity": "sha512-4/ApBnMVeEPG3EkSzcw25wDe4N66wxwn+KKn6b47vyek8Xb3NBAcg4xfuQbS7BqcZuTX4wxfD5lVagdggR3gyA==", - "dev": true, - "requires": { - "async": "^2.1.4", - "fileset": "^2.0.2", - "istanbul-lib-coverage": "^1.2.1", - "istanbul-lib-hook": "^1.2.2", - "istanbul-lib-instrument": "^1.10.2", - "istanbul-lib-report": "^1.1.5", - "istanbul-lib-source-maps": "^1.2.6", - "istanbul-reports": "^1.5.1", - "js-yaml": "^3.7.0", - "mkdirp": "^0.5.1", - "once": "^1.4.0" - } - }, - "istanbul-lib-coverage": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz", - "integrity": "sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ==", - "dev": true - }, - "istanbul-lib-hook": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-1.2.2.tgz", - "integrity": "sha512-/Jmq7Y1VeHnZEQ3TL10VHyb564mn6VrQXHchON9Jf/AEcmQ3ZIiyD1BVzNOKTZf/G3gE+kiGK6SmpF9y3qGPLw==", - "dev": true, - "requires": { - "append-transform": "^0.4.0" - } - }, - "istanbul-lib-instrument": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.2.tgz", - "integrity": "sha512-aWHxfxDqvh/ZlxR8BBaEPVSWDPUkGD63VjGQn3jcw8jCp7sHEMKcrj4xfJn/ABzdMEHiQNyvDQhqm5o8+SQg7A==", - "dev": true, - "requires": { - "babel-generator": "^6.18.0", - "babel-template": "^6.16.0", - "babel-traverse": "^6.18.0", - "babel-types": "^6.18.0", - "babylon": "^6.18.0", - "istanbul-lib-coverage": "^1.2.1", - "semver": "^5.3.0" - } - }, - "istanbul-lib-report": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-1.1.5.tgz", - "integrity": "sha512-UsYfRMoi6QO/doUshYNqcKJqVmFe9w51GZz8BS3WB0lYxAllQYklka2wP9+dGZeHYaWIdcXUx8JGdbqaoXRXzw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^1.2.1", - "mkdirp": "^0.5.1", - "path-parse": "^1.0.5", - "supports-color": "^3.1.2" - }, - "dependencies": { - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.6.tgz", - "integrity": "sha512-TtbsY5GIHgbMsMiRw35YBHGpZ1DVFEO19vxxeiDMYaeOFOCzfnYVxvl6pOUIZR4dtPhAGpSMup8OyF8ubsaqEg==", - "dev": true, - "requires": { - "debug": "^3.1.0", - "istanbul-lib-coverage": "^1.2.1", - "mkdirp": "^0.5.1", - "rimraf": "^2.6.1", - "source-map": "^0.5.3" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "istanbul-reports": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-1.5.1.tgz", - "integrity": "sha512-+cfoZ0UXzWjhAdzosCPP3AN8vvef8XDkWtTfgaN+7L3YTpNYITnCaEkceo5SEYy644VkHka/P1FvkWvrG/rrJw==", - "dev": true, - "requires": { - "handlebars": "^4.0.3" - } - }, - "jest": { - "version": "22.4.4", - "resolved": "https://registry.npmjs.org/jest/-/jest-22.4.4.tgz", - "integrity": "sha512-eBhhW8OS/UuX3HxgzNBSVEVhSuRDh39Z1kdYkQVWna+scpgsrD7vSeBI7tmEvsguPDMnfJodW28YBnhv/BzSew==", - "dev": true, - "requires": { - "import-local": "^1.0.0", - "jest-cli": "^22.4.4" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "jest-cli": { - "version": "22.4.4", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-22.4.4.tgz", - "integrity": "sha512-I9dsgkeyjVEEZj9wrGrqlH+8OlNob9Iptyl+6L5+ToOLJmHm4JwOPatin1b2Bzp5R5YRQJ+oiedx7o1H7wJzhA==", - "dev": true, - "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.1.11", - "import-local": "^1.0.0", - "is-ci": "^1.0.10", - "istanbul-api": "^1.1.14", - "istanbul-lib-coverage": "^1.1.1", - "istanbul-lib-instrument": "^1.8.0", - "istanbul-lib-source-maps": "^1.2.1", - "jest-changed-files": "^22.2.0", - "jest-config": "^22.4.4", - "jest-environment-jsdom": "^22.4.1", - "jest-get-type": "^22.1.0", - "jest-haste-map": "^22.4.2", - "jest-message-util": "^22.4.0", - "jest-regex-util": "^22.1.0", - "jest-resolve-dependencies": "^22.1.0", - "jest-runner": "^22.4.4", - "jest-runtime": "^22.4.4", - "jest-snapshot": "^22.4.0", - "jest-util": "^22.4.1", - "jest-validate": "^22.4.4", - "jest-worker": "^22.2.2", - "micromatch": "^2.3.11", - "node-notifier": "^5.2.1", - "realpath-native": "^1.0.0", - "rimraf": "^2.5.4", - "slash": "^1.0.0", - "string-length": "^2.0.0", - "strip-ansi": "^4.0.0", - "which": "^1.2.12", - "yargs": "^10.0.3" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "jest-changed-files": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-22.4.3.tgz", - "integrity": "sha512-83Dh0w1aSkUNFhy5d2dvqWxi/y6weDwVVLU6vmK0cV9VpRxPzhTeGimbsbRDSnEoszhF937M4sDLLeS7Cu/Tmw==", - "dev": true, - "requires": { - "throat": "^4.0.0" - } - }, - "jest-config": { - "version": "22.4.4", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-22.4.4.tgz", - "integrity": "sha512-9CKfo1GC4zrXSoMLcNeDvQBfgtqGTB1uP8iDIZ97oB26RCUb886KkKWhVcpyxVDOUxbhN+uzcBCeFe7w+Iem4A==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "glob": "^7.1.1", - "jest-environment-jsdom": "^22.4.1", - "jest-environment-node": "^22.4.1", - "jest-get-type": "^22.1.0", - "jest-jasmine2": "^22.4.4", - "jest-regex-util": "^22.1.0", - "jest-resolve": "^22.4.2", - "jest-util": "^22.4.1", - "jest-validate": "^22.4.4", - "pretty-format": "^22.4.0" - } - }, - "jest-diff": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-22.4.3.tgz", - "integrity": "sha512-/QqGvCDP5oZOF6PebDuLwrB2BMD8ffJv6TAGAdEVuDx1+uEgrHpSFrfrOiMRx2eJ1hgNjlQrOQEHetVwij90KA==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "diff": "^3.2.0", - "jest-get-type": "^22.4.3", - "pretty-format": "^22.4.3" - } - }, - "jest-docblock": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-22.4.3.tgz", - "integrity": "sha512-uPKBEAw7YrEMcXueMKZXn/rbMxBiSv48fSqy3uEnmgOlQhSX+lthBqHb1fKWNVmFqAp9E/RsSdBfiV31LbzaOg==", - "dev": true, - "requires": { - "detect-newline": "^2.1.0" - } - }, - "jest-environment-jsdom": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-22.4.3.tgz", - "integrity": "sha512-FviwfR+VyT3Datf13+ULjIMO5CSeajlayhhYQwpzgunswoaLIPutdbrnfUHEMyJCwvqQFaVtTmn9+Y8WCt6n1w==", - "dev": true, - "requires": { - "jest-mock": "^22.4.3", - "jest-util": "^22.4.3", - "jsdom": "^11.5.1" - } - }, - "jest-environment-node": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-22.4.3.tgz", - "integrity": "sha512-reZl8XF6t/lMEuPWwo9OLfttyC26A5AMgDyEQ6DBgZuyfyeNUzYT8BFo6uxCCP/Av/b7eb9fTi3sIHFPBzmlRA==", - "dev": true, - "requires": { - "jest-mock": "^22.4.3", - "jest-util": "^22.4.3" - } - }, - "jest-get-type": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz", - "integrity": "sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==", - "dev": true - }, - "jest-haste-map": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-22.4.3.tgz", - "integrity": "sha512-4Q9fjzuPVwnaqGKDpIsCSoTSnG3cteyk2oNVjBX12HHOaF1oxql+uUiqZb5Ndu7g/vTZfdNwwy4WwYogLh29DQ==", - "dev": true, - "requires": { - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.1.11", - "jest-docblock": "^22.4.3", - "jest-serializer": "^22.4.3", - "jest-worker": "^22.4.3", - "micromatch": "^2.3.11", - "sane": "^2.0.0" - } - }, - "jest-jasmine2": { - "version": "22.4.4", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-22.4.4.tgz", - "integrity": "sha512-nK3vdUl50MuH7vj/8at7EQVjPGWCi3d5+6aCi7Gxy/XMWdOdbH1qtO/LjKbqD8+8dUAEH+BVVh7HkjpCWC1CSw==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "co": "^4.6.0", - "expect": "^22.4.0", - "graceful-fs": "^4.1.11", - "is-generator-fn": "^1.0.0", - "jest-diff": "^22.4.0", - "jest-matcher-utils": "^22.4.0", - "jest-message-util": "^22.4.0", - "jest-snapshot": "^22.4.0", - "jest-util": "^22.4.1", - "source-map-support": "^0.5.0" - }, - "dependencies": { - "source-map-support": { - "version": "0.5.9", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz", - "integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - } - } - }, - "jest-leak-detector": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-22.4.3.tgz", - "integrity": "sha512-NZpR/Ls7+ndO57LuXROdgCGz2RmUdC541tTImL9bdUtU3WadgFGm0yV+Ok4Fuia/1rLAn5KaJ+i76L6e3zGJYQ==", - "dev": true, - "requires": { - "pretty-format": "^22.4.3" - } - }, - "jest-matcher-utils": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-22.4.3.tgz", - "integrity": "sha512-lsEHVaTnKzdAPR5t4B6OcxXo9Vy4K+kRRbG5gtddY8lBEC+Mlpvm1CJcsMESRjzUhzkz568exMV1hTB76nAKbA==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "jest-get-type": "^22.4.3", - "pretty-format": "^22.4.3" - } - }, - "jest-message-util": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-22.4.3.tgz", - "integrity": "sha512-iAMeKxhB3Se5xkSjU0NndLLCHtP4n+GtCqV0bISKA5dmOXQfEbdEmYiu2qpnWBDCQdEafNDDU6Q+l6oBMd/+BA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0-beta.35", - "chalk": "^2.0.1", - "micromatch": "^2.3.11", - "slash": "^1.0.0", - "stack-utils": "^1.0.1" - } - }, - "jest-mock": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-22.4.3.tgz", - "integrity": "sha512-+4R6mH5M1G4NK16CKg9N1DtCaFmuxhcIqF4lQK/Q1CIotqMs/XBemfpDPeVZBFow6iyUNu6EBT9ugdNOTT5o5Q==", - "dev": true - }, - "jest-regex-util": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-22.4.3.tgz", - "integrity": "sha512-LFg1gWr3QinIjb8j833bq7jtQopiwdAs67OGfkPrvy7uNUbVMfTXXcOKXJaeY5GgjobELkKvKENqq1xrUectWg==", - "dev": true - }, - "jest-resolve": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-22.4.3.tgz", - "integrity": "sha512-u3BkD/MQBmwrOJDzDIaxpyqTxYH+XqAXzVJP51gt29H8jpj3QgKof5GGO2uPGKGeA1yTMlpbMs1gIQ6U4vcRhw==", - "dev": true, - "requires": { - "browser-resolve": "^1.11.2", - "chalk": "^2.0.1" - } - }, - "jest-resolve-dependencies": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-22.4.3.tgz", - "integrity": "sha512-06czCMVToSN8F2U4EvgSB1Bv/56gc7MpCftZ9z9fBgUQM7dzHGCMBsyfVA6dZTx8v0FDcnALf7hupeQxaBCvpA==", - "dev": true, - "requires": { - "jest-regex-util": "^22.4.3" - } - }, - "jest-runner": { - "version": "22.4.4", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-22.4.4.tgz", - "integrity": "sha512-5S/OpB51igQW9xnkM5Tgd/7ZjiAuIoiJAVtvVTBcEBiXBIFzWM3BAMPBM19FX68gRV0KWyFuGKj0EY3M3aceeQ==", - "dev": true, - "requires": { - "exit": "^0.1.2", - "jest-config": "^22.4.4", - "jest-docblock": "^22.4.0", - "jest-haste-map": "^22.4.2", - "jest-jasmine2": "^22.4.4", - "jest-leak-detector": "^22.4.0", - "jest-message-util": "^22.4.0", - "jest-runtime": "^22.4.4", - "jest-util": "^22.4.1", - "jest-worker": "^22.2.2", - "throat": "^4.0.0" - } - }, - "jest-runtime": { - "version": "22.4.4", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-22.4.4.tgz", - "integrity": "sha512-WRTj9m///npte1YjuphCYX7GRY/c2YvJImU9t7qOwFcqHr4YMzmX6evP/3Sehz5DKW2Vi8ONYPCFWe36JVXxfw==", - "dev": true, - "requires": { - "babel-core": "^6.0.0", - "babel-jest": "^22.4.4", - "babel-plugin-istanbul": "^4.1.5", - "chalk": "^2.0.1", - "convert-source-map": "^1.4.0", - "exit": "^0.1.2", - "graceful-fs": "^4.1.11", - "jest-config": "^22.4.4", - "jest-haste-map": "^22.4.2", - "jest-regex-util": "^22.1.0", - "jest-resolve": "^22.4.2", - "jest-util": "^22.4.1", - "jest-validate": "^22.4.4", - "json-stable-stringify": "^1.0.1", - "micromatch": "^2.3.11", - "realpath-native": "^1.0.0", - "slash": "^1.0.0", - "strip-bom": "3.0.0", - "write-file-atomic": "^2.1.0", - "yargs": "^10.0.3" - }, - "dependencies": { - "babel-jest": { - "version": "22.4.4", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-22.4.4.tgz", - "integrity": "sha512-A9NB6/lZhYyypR9ATryOSDcqBaqNdzq4U+CN+/wcMsLcmKkPxQEoTKLajGfd3IkxNyVBT8NewUK2nWyGbSzHEQ==", - "dev": true, - "requires": { - "babel-plugin-istanbul": "^4.1.5", - "babel-preset-jest": "^22.4.4" - } - }, - "babel-plugin-jest-hoist": { - "version": "22.4.4", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-22.4.4.tgz", - "integrity": "sha512-DUvGfYaAIlkdnygVIEl0O4Av69NtuQWcrjMOv6DODPuhuGLDnbsARz3AwiiI/EkIMMlxQDUcrZ9yoyJvTNjcVQ==", - "dev": true - }, - "babel-preset-jest": { - "version": "22.4.4", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-22.4.4.tgz", - "integrity": "sha512-+dxMtOFwnSYWfum0NaEc0O03oSdwBsjx4tMSChRDPGwu/4wSY6Q6ANW3wkjKpJzzguaovRs/DODcT4hbSN8yiA==", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^22.4.4", - "babel-plugin-syntax-object-rest-spread": "^6.13.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } - } - }, - "jest-serializer": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-22.4.3.tgz", - "integrity": "sha512-uPaUAppx4VUfJ0QDerpNdF43F68eqKWCzzhUlKNDsUPhjOon7ZehR4C809GCqh765FoMRtTVUVnGvIoskkYHiw==", - "dev": true - }, - "jest-serializer-vue": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/jest-serializer-vue/-/jest-serializer-vue-0.3.0.tgz", - "integrity": "sha512-Id1x3XabYu2r6BnmTfGk2tY172BEqR+vAzSvPk4VF8HyVqwebxZQbqiZ/giAtCnRSqi6lzxuyvzQbwQ6bo6Hbg==", - "dev": true, - "requires": { - "pretty": "2.0.0" - } - }, - "jest-snapshot": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-22.4.3.tgz", - "integrity": "sha512-JXA0gVs5YL0HtLDCGa9YxcmmV2LZbwJ+0MfyXBBc5qpgkEYITQFJP7XNhcHFbUvRiniRpRbGVfJrOoYhhGE0RQ==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "jest-diff": "^22.4.3", - "jest-matcher-utils": "^22.4.3", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "pretty-format": "^22.4.3" - } - }, - "jest-util": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-22.4.3.tgz", - "integrity": "sha512-rfDfG8wyC5pDPNdcnAlZgwKnzHvZDu8Td2NJI/jAGKEGxJPYiE4F0ss/gSAkG4778Y23Hvbz+0GMrDJTeo7RjQ==", - "dev": true, - "requires": { - "callsites": "^2.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.11", - "is-ci": "^1.0.10", - "jest-message-util": "^22.4.3", - "mkdirp": "^0.5.1", - "source-map": "^0.6.0" - }, - "dependencies": { - "callsites": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", - "dev": true - } - } - }, - "jest-validate": { - "version": "22.4.4", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-22.4.4.tgz", - "integrity": "sha512-dmlf4CIZRGvkaVg3fa0uetepcua44DHtktHm6rcoNVtYlpwe6fEJRkMFsaUVcFHLzbuBJ2cPw9Gl9TKfnzMVwg==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "jest-config": "^22.4.4", - "jest-get-type": "^22.1.0", - "leven": "^2.1.0", - "pretty-format": "^22.4.0" - } - }, - "jest-worker": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-22.4.3.tgz", - "integrity": "sha512-B1ucW4fI8qVAuZmicFxI1R3kr2fNeYJyvIQ1rKcuLYnenFV5K5aMbxFj6J0i00Ju83S8jP2d7Dz14+AvbIHRYQ==", - "dev": true, - "requires": { - "merge-stream": "^1.0.1" - } - }, - "js-base64": { - "version": "2.4.9", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.9.tgz", - "integrity": "sha512-xcinL3AuDJk7VSzsHgb9DvvIXayBbadtMZ4HFPx8rUszbW1MuNMlwYVC4zzCZ6e1sqZpnNS5ZFYOhXqA39T7LQ==", - "dev": true - }, - "js-beautify": { - "version": "1.8.4", - "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.8.4.tgz", - "integrity": "sha512-mlWlABWrhXiTz0Snh1dAjIcll2kWJv4Fw+RP7dY0QhaN5ftH8PQsS4BJ1Uw+coVcVblzxLvC97MYpIgSM4TcyA==", - "dev": true, - "requires": { - "config-chain": "~1.1.5", - "editorconfig": "^0.15.0", - "mkdirp": "~0.5.0", - "nopt": "~4.0.1" - } - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true - }, - "js-yaml": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", - "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^2.6.0" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true, - "optional": true - }, - "jsdom": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", - "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", - "dev": true, - "requires": { - "abab": "^2.0.0", - "acorn": "^5.5.3", - "acorn-globals": "^4.1.0", - "array-equal": "^1.0.0", - "cssom": ">= 0.3.2 < 0.4.0", - "cssstyle": "^1.0.0", - "data-urls": "^1.0.0", - "domexception": "^1.0.1", - "escodegen": "^1.9.1", - "html-encoding-sniffer": "^1.0.2", - "left-pad": "^1.3.0", - "nwsapi": "^2.0.7", - "parse5": "4.0.0", - "pn": "^1.1.0", - "request": "^2.87.0", - "request-promise-native": "^1.0.5", - "sax": "^1.2.4", - "symbol-tree": "^3.2.2", - "tough-cookie": "^2.3.4", - "w3c-hr-time": "^1.0.1", - "webidl-conversions": "^4.0.2", - "whatwg-encoding": "^1.0.3", - "whatwg-mimetype": "^2.1.0", - "whatwg-url": "^6.4.1", - "ws": "^5.2.0", - "xml-name-validator": "^3.0.0" - } - }, - "jsesc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", - "dev": true - }, - "json-loader": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz", - "integrity": "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==", - "dev": true - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - }, - "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", - "dev": true, - "requires": { - "jsonify": "~0.0.0" - } - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "json3": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", - "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", - "dev": true - }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true - }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", - "dev": true - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "kew": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz", - "integrity": "sha1-edk9LTM2PW/dKXCzNdkUGtWR15s=", - "dev": true - }, - "killable": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.0.tgz", - "integrity": "sha1-2ouEvUfeU5WHj5XWTQLyRJ/gXms=", - "dev": true - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - }, - "last-call-webpack-plugin": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/last-call-webpack-plugin/-/last-call-webpack-plugin-2.1.2.tgz", - "integrity": "sha512-CZc+m2xZm51J8qSwdODeiiNeqh8CYkKEq6Rw8IkE4i/4yqf2cJhjQPsA6BtAV970ePRNhwEOXhy2U5xc5Jwh9Q==", - "dev": true, - "requires": { - "lodash": "^4.17.4", - "webpack-sources": "^1.0.1" - } - }, - "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", - "dev": true - }, - "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "dev": true, - "requires": { - "invert-kv": "^1.0.0" - } - }, - "left-pad": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", - "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", - "dev": true - }, - "leven": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", - "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", - "dev": true - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" - } - }, - "loader-fs-cache": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.1.tgz", - "integrity": "sha1-VuC/CL2XCLJqdltoUJhAyN7J/bw=", - "dev": true, - "requires": { - "find-cache-dir": "^0.1.1", - "mkdirp": "0.5.1" - }, - "dependencies": { - "find-cache-dir": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz", - "integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "mkdirp": "^0.5.1", - "pkg-dir": "^1.0.0" - } - }, - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "^2.0.0" - } - }, - "pkg-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", - "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", - "dev": true, - "requires": { - "find-up": "^1.0.0" - } - } - } - }, - "loader-runner": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz", - "integrity": "sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI=", - "dev": true - }, - "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", - "dev": true, - "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" - }, - "lodash._arraycopy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._arraycopy/-/lodash._arraycopy-3.0.0.tgz", - "integrity": "sha1-due3wfH7klRzdIeKVi7Qaj5Q9uE=", - "dev": true - }, - "lodash._arrayeach": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._arrayeach/-/lodash._arrayeach-3.0.0.tgz", - "integrity": "sha1-urFWsqkNPxu9XGU0AzSeXlkz754=", - "dev": true - }, - "lodash._baseassign": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", - "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", - "dev": true, - "requires": { - "lodash._basecopy": "^3.0.0", - "lodash.keys": "^3.0.0" - } - }, - "lodash._baseclone": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lodash._baseclone/-/lodash._baseclone-3.3.0.tgz", - "integrity": "sha1-MDUZv2OT/n5C802LYw73eU41Qrc=", - "dev": true, - "requires": { - "lodash._arraycopy": "^3.0.0", - "lodash._arrayeach": "^3.0.0", - "lodash._baseassign": "^3.0.0", - "lodash._basefor": "^3.0.0", - "lodash.isarray": "^3.0.0", - "lodash.keys": "^3.0.0" - } - }, - "lodash._basecopy": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", - "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", - "dev": true - }, - "lodash._basecreate": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz", - "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=", - "dev": true - }, - "lodash._basefor": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash._basefor/-/lodash._basefor-3.0.3.tgz", - "integrity": "sha1-dVC06SGO8J+tJDQ7YSAhx5tMIMI=", - "dev": true - }, - "lodash._baseget": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/lodash._baseget/-/lodash._baseget-3.7.2.tgz", - "integrity": "sha1-G2rh1frPPCVTI1ChPBGXy4u2dPQ=", - "dev": true - }, - "lodash._bindcallback": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz", - "integrity": "sha1-5THCdkTPi1epnhftlbNcdIeJOS4=", - "dev": true - }, - "lodash._getnative": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", - "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", - "dev": true - }, - "lodash._isiterateecall": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", - "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", - "dev": true - }, - "lodash._stack": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/lodash._stack/-/lodash._stack-4.1.3.tgz", - "integrity": "sha1-dRqnbBuWSwR+dtFPxyoJP8teLdA=", - "dev": true - }, - "lodash._topath": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/lodash._topath/-/lodash._topath-3.8.1.tgz", - "integrity": "sha1-PsXiYGAU9MuX91X+aRTt2L/ADqw=", - "dev": true, - "requires": { - "lodash.isarray": "^3.0.0" - } - }, - "lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", - "dev": true - }, - "lodash.clone": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-3.0.3.tgz", - "integrity": "sha1-hGiMc9MrWpDKJWFpY/GJJSqZcEM=", - "dev": true, - "requires": { - "lodash._baseclone": "^3.0.0", - "lodash._bindcallback": "^3.0.0", - "lodash._isiterateecall": "^3.0.0" - } - }, - "lodash.create": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", - "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=", - "dev": true, - "requires": { - "lodash._baseassign": "^3.0.0", - "lodash._basecreate": "^3.0.0", - "lodash._isiterateecall": "^3.0.0" - } - }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", - "dev": true - }, - "lodash.defaultsdeep": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.3.2.tgz", - "integrity": "sha1-bBpYbmxWR7DmTi15gUG4g2FYvoo=", - "dev": true, - "requires": { - "lodash._baseclone": "^4.0.0", - "lodash._stack": "^4.0.0", - "lodash.isplainobject": "^4.0.0", - "lodash.keysin": "^4.0.0", - "lodash.mergewith": "^4.0.0", - "lodash.rest": "^4.0.0" - }, - "dependencies": { - "lodash._baseclone": { - "version": "4.5.7", - "resolved": "https://registry.npmjs.org/lodash._baseclone/-/lodash._baseclone-4.5.7.tgz", - "integrity": "sha1-zkKt4IOE711i+nfDD2GkbmhvhDQ=", - "dev": true - } - } - }, - "lodash.get": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-3.7.0.tgz", - "integrity": "sha1-POaK4skWg7KBzFOUEoMDy/deaR8=", - "dev": true, - "requires": { - "lodash._baseget": "^3.0.0", - "lodash._topath": "^3.0.0" - } - }, - "lodash.isarguments": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", - "dev": true - }, - "lodash.isarray": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", - "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", - "dev": true - }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", - "dev": true - }, - "lodash.keys": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", - "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", - "dev": true, - "requires": { - "lodash._getnative": "^3.0.0", - "lodash.isarguments": "^3.0.0", - "lodash.isarray": "^3.0.0" - } - }, - "lodash.keysin": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.keysin/-/lodash.keysin-4.2.0.tgz", - "integrity": "sha1-jMP7NcLZSsxEOhhj4C+kB5nqbyg=", - "dev": true - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", - "dev": true - }, - "lodash.mergewith": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz", - "integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ==", - "dev": true - }, - "lodash.rest": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/lodash.rest/-/lodash.rest-4.0.5.tgz", - "integrity": "sha1-lU73UEkmIDjJbR/Jiyj9r58Hcqo=", - "dev": true - }, - "lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", - "dev": true - }, - "lodash.startcase": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", - "integrity": "sha1-lDbjTtJgk+1/+uGTYUQ1CRXZrdg=" - }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", - "dev": true - }, - "log-symbols": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", - "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", - "dev": true, - "requires": { - "chalk": "^2.0.1" - } - }, - "loglevel": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.1.tgz", - "integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=", - "dev": true - }, - "longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "dev": true - }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", - "dev": true, - "requires": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" - } - }, - "lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", - "dev": true - }, - "lru-cache": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", - "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", - "dev": true, - "requires": { - "pify": "^3.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "makeerror": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", - "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", - "dev": true, - "requires": { - "tmpl": "1.0.x" - } - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true - }, - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "requires": { - "object-visit": "^1.0.0" - } - }, - "math-expression-evaluator": { - "version": "1.2.17", - "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz", - "integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw=", - "dev": true - }, - "math-random": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz", - "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=", - "dev": true - }, - "md5.js": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", - "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "dev": true - }, - "mem": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", - "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "memory-fs": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.2.0.tgz", - "integrity": "sha1-8rslNovBIeORwlIN6Slpyu4KApA=", - "dev": true - }, - "meow": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", - "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", - "dev": true, - "requires": { - "camelcase-keys": "^2.0.0", - "decamelize": "^1.1.2", - "loud-rejection": "^1.0.0", - "map-obj": "^1.0.1", - "minimist": "^1.1.3", - "normalize-package-data": "^2.3.4", - "object-assign": "^4.0.1", - "read-pkg-up": "^1.0.1", - "redent": "^1.0.0", - "trim-newlines": "^1.0.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "merge": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.0.tgz", - "integrity": "sha1-dTHjnUlJwoGma4xabgJl6LBYlNo=", - "dev": true - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", - "dev": true - }, - "merge-stream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", - "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", - "dev": true, - "requires": { - "readable-stream": "^2.0.1" - } - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "dev": true - }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true, - "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" - } - }, - "miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - } - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true - }, - "mime-db": { - "version": "1.36.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.36.0.tgz", - "integrity": "sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw==", - "dev": true - }, - "mime-types": { - "version": "2.1.20", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.20.tgz", - "integrity": "sha512-HrkrPaP9vGuWbLK1B1FfgAkbqNjIuy4eHlIYnFi7kamZyLLrGlo2mpcx0bBmNpKqBtYtAfGbodDddIgddSJC2A==", - "dev": true, - "requires": { - "mime-db": "~1.36.0" - } - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "mississippi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", - "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", - "dev": true, - "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^2.0.1", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - } - }, - "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", - "dev": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "mkpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mkpath/-/mkpath-1.0.0.tgz", - "integrity": "sha1-67Opd+evHGg65v2hK1Raa6bFhT0=", - "dev": true - }, - "mocha-nightwatch": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/mocha-nightwatch/-/mocha-nightwatch-3.2.2.tgz", - "integrity": "sha1-kby5s73gV912d8eBJeSR5Y1mZHw=", - "dev": true, - "requires": { - "browser-stdout": "1.3.0", - "commander": "2.9.0", - "debug": "2.2.0", - "diff": "1.4.0", - "escape-string-regexp": "1.0.5", - "glob": "7.0.5", - "growl": "1.9.2", - "json3": "3.3.2", - "lodash.create": "3.1.1", - "mkdirp": "0.5.1", - "supports-color": "3.1.2" - }, - "dependencies": { - "commander": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", - "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", - "dev": true, - "requires": { - "graceful-readlink": ">= 1.0.0" - } - }, - "debug": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", - "dev": true, - "requires": { - "ms": "0.7.1" - } - }, - "diff": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz", - "integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=", - "dev": true - }, - "glob": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.5.tgz", - "integrity": "sha1-tCAqaQmbu00pKnwblbZoK2fr3JU=", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.2", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "ms": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", - "dev": true - }, - "supports-color": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", - "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "move-concurrently": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", - "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", - "dev": true, - "requires": { - "aproba": "^1.1.1", - "copy-concurrently": "^1.0.0", - "fs-write-stream-atomic": "^1.0.8", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.3" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "multicast-dns": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", - "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", - "dev": true, - "requires": { - "dns-packet": "^1.3.1", - "thunky": "^1.0.2" - } - }, - "multicast-dns-service-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", - "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", - "dev": true - }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" - }, - "nan": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.0.tgz", - "integrity": "sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw==", - "dev": true, - "optional": true - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - } - } - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", - "dev": true - }, - "neo-async": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.5.2.tgz", - "integrity": "sha512-vdqTKI9GBIYcAEbFAcpKPErKINfPF5zIuz3/niBfq8WUZjpT2tytLlFVrBgWdOtqI4uaA/Rb6No0hux39XXDuw==", - "dev": true - }, - "netmask": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-1.0.6.tgz", - "integrity": "sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU=", - "dev": true - }, - "next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", - "dev": true - }, - "nightwatch": { - "version": "0.9.21", - "resolved": "https://registry.npmjs.org/nightwatch/-/nightwatch-0.9.21.tgz", - "integrity": "sha1-nnlKdRS0/V9GYC02jlBRUjKrnpA=", - "dev": true, - "requires": { - "chai-nightwatch": "~0.1.x", - "ejs": "2.5.7", - "lodash.clone": "3.0.3", - "lodash.defaultsdeep": "4.3.2", - "minimatch": "3.0.3", - "mkpath": "1.0.0", - "mocha-nightwatch": "3.2.2", - "optimist": "0.6.1", - "proxy-agent": "2.0.0", - "q": "1.4.1" - }, - "dependencies": { - "minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q=", - "dev": true, - "requires": { - "brace-expansion": "^1.0.0" - } - }, - "q": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", - "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", - "dev": true - } - } - }, - "no-case": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", - "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", - "dev": true, - "requires": { - "lower-case": "^1.1.1" - } - }, - "node-cache": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-4.2.0.tgz", - "integrity": "sha512-obRu6/f7S024ysheAjoYFEEBqqDWv4LOMNJEuO8vMeEw2AT4z+NCzO4hlc2lhI4vATzbCQv6kke9FVdx0RbCOw==", - "dev": true, - "requires": { - "clone": "2.x", - "lodash": "4.x" - }, - "dependencies": { - "clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", - "dev": true - } - } - }, - "node-fetch": { - "version": "1.6.3", - "resolved": "http://registry.npmjs.org/node-fetch/-/node-fetch-1.6.3.tgz", - "integrity": "sha1-3CNO3WSJmC1Y6PDbT2lQKavNjAQ=", - "requires": { - "encoding": "^0.1.11", - "is-stream": "^1.0.1" - } - }, - "node-forge": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz", - "integrity": "sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ==", - "dev": true - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", - "dev": true - }, - "node-libs-browser": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz", - "integrity": "sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg==", - "dev": true, - "requires": { - "assert": "^1.1.1", - "browserify-zlib": "^0.2.0", - "buffer": "^4.3.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.11.0", - "domain-browser": "^1.1.1", - "events": "^1.0.0", - "https-browserify": "^1.0.0", - "os-browserify": "^0.3.0", - "path-browserify": "0.0.0", - "process": "^0.11.10", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.3.3", - "stream-browserify": "^2.0.1", - "stream-http": "^2.7.2", - "string_decoder": "^1.0.0", - "timers-browserify": "^2.0.4", - "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.10.3", - "vm-browserify": "0.0.4" - } - }, - "node-notifier": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.2.1.tgz", - "integrity": "sha512-MIBs+AAd6dJ2SklbbE8RUDRlIVhU8MaNLh1A9SUZDUHPiZkWLFde6UNwG41yQHZEToHgJMXqyVZ9UcS/ReOVTg==", - "dev": true, - "requires": { - "growly": "^1.3.0", - "semver": "^5.4.1", - "shellwords": "^0.1.1", - "which": "^1.3.0" - } - }, - "nopt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", - "dev": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "is-builtin-module": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", - "dev": true - }, - "normalize-url": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", - "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", - "dev": true, - "requires": { - "object-assign": "^4.0.1", - "prepend-http": "^1.0.0", - "query-string": "^4.1.0", - "sort-keys": "^1.0.0" - } - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.0" - } - }, - "nth-check": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz", - "integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=", - "dev": true, - "requires": { - "boolbase": "~1.0.0" - } - }, - "num2fraction": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", - "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", - "dev": true - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "nwsapi": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.0.9.tgz", - "integrity": "sha512-nlWFSCTYQcHk/6A9FFnfhKc14c3aFhfdNBXgo8Qgi9QTBu/qg3Ww+Uiz9wMzXd1T8GFxPc2QIHB6Qtf2XFryFQ==", - "dev": true - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "object-hash": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.0.tgz", - "integrity": "sha512-05KzQ70lSeGSrZJQXE5wNDiTkBJDlUT/myi6RX9dVIvz7a7Qh4oH93BQdiPMn27nldYvVQCKMUaM83AfizZlsQ==", - "dev": true - }, - "object-keys": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", - "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==", - "dev": true - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "requires": { - "isobject": "^3.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" - } - }, - "object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", - "dev": true, - "requires": { - "for-own": "^0.1.4", - "is-extendable": "^0.1.1" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "requires": { - "isobject": "^3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } - }, - "on-headers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", - "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "opencollective": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/opencollective/-/opencollective-1.0.3.tgz", - "integrity": "sha1-ruY3K8KBRFg2kMPKja7PwSDdDvE=", - "requires": { - "babel-polyfill": "6.23.0", - "chalk": "1.1.3", - "inquirer": "3.0.6", - "minimist": "1.2.0", - "node-fetch": "1.6.3", - "opn": "4.0.2" - }, - "dependencies": { - "ansi-escapes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", - "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=" - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "inquirer": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.0.6.tgz", - "integrity": "sha1-4EqqnQW3o8ubD0B9BDdfBEcZA0c=", - "requires": { - "ansi-escapes": "^1.1.0", - "chalk": "^1.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^2.0.1", - "figures": "^2.0.0", - "lodash": "^4.3.0", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rx": "^4.1.0", - "string-width": "^2.0.0", - "strip-ansi": "^3.0.0", - "through": "^2.3.6" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - }, - "opn": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/opn/-/opn-4.0.2.tgz", - "integrity": "sha1-erwi5kTf9jsKltWrfyeQwPAavJU=", - "requires": { - "object-assign": "^4.0.1", - "pinkie-promise": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - } - } - }, - "opener": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.1.tgz", - "integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==", - "dev": true - }, - "opn": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz", - "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==", - "dev": true, - "requires": { - "is-wsl": "^1.1.0" - } - }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true, - "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" - }, - "dependencies": { - "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", - "dev": true - } - } - }, - "optimize-css-assets-webpack-plugin": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-3.2.0.tgz", - "integrity": "sha512-Fjn7wyyadPAriuH2DHamDQw5B8GohEWbroBkKoPeP+vSF2PIAPI7WDihi8WieMRb/At4q7Ea7zTKaMDuSoIAAg==", - "dev": true, - "requires": { - "cssnano": "^3.4.0", - "last-call-webpack-plugin": "^2.1.2" - } - }, - "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" - } - }, - "ora": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-1.4.0.tgz", - "integrity": "sha512-iMK1DOQxzzh2MBlVsU42G80mnrvUhqsMh74phHtDlrcTZPK0pH6o7l7DRshK+0YsxDyEuaOkziVdvM3T0QTzpw==", - "dev": true, - "requires": { - "chalk": "^2.1.0", - "cli-cursor": "^2.1.0", - "cli-spinners": "^1.0.1", - "log-symbols": "^2.1.0" - } - }, - "original": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", - "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", - "dev": true, - "requires": { - "url-parse": "^1.4.3" - } - }, - "os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", - "dev": true - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true - }, - "os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", - "dev": true, - "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "dev": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-map": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", - "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", - "dev": true - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "pac-proxy-agent": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-1.1.0.tgz", - "integrity": "sha512-QBELCWyLYPgE2Gj+4wUEiMscHrQ8nRPBzYItQNOHWavwBt25ohZHQC4qnd5IszdVVrFbLsQ+dPkm6eqdjJAmwQ==", - "dev": true, - "requires": { - "agent-base": "2", - "debug": "2", - "extend": "3", - "get-uri": "2", - "http-proxy-agent": "1", - "https-proxy-agent": "1", - "pac-resolver": "~2.0.0", - "raw-body": "2", - "socks-proxy-agent": "2" - } - }, - "pac-resolver": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-2.0.0.tgz", - "integrity": "sha1-mbiNLxk/ve78HJpSnB8yYKtSd80=", - "dev": true, - "requires": { - "co": "~3.0.6", - "degenerator": "~1.0.2", - "ip": "1.0.1", - "netmask": "~1.0.4", - "thunkify": "~2.1.1" - }, - "dependencies": { - "co": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/co/-/co-3.0.6.tgz", - "integrity": "sha1-FEXyJsXrlWE45oyawwFn6n0ua9o=", - "dev": true - } - } - }, - "pako": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", - "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==", - "dev": true - }, - "parallel-transform": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", - "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", - "dev": true, - "requires": { - "cyclist": "~0.2.2", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" - } - }, - "param-case": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", - "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", - "dev": true, - "requires": { - "no-case": "^2.2.0" - } - }, - "parse-asn1": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", - "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==", - "dev": true, - "requires": { - "asn1.js": "^4.0.0", - "browserify-aes": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3" - } - }, - "parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", - "dev": true, - "requires": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" - } - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "^1.2.0" - } - }, - "parse5": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", - "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", - "dev": true - }, - "parseurl": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", - "dev": true - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true - }, - "path-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", - "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", - "dev": true - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", - "dev": true - }, - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "pbkdf2": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.16.tgz", - "integrity": "sha512-y4CXP3thSxqf7c0qmOF+9UeOTrifiVTIM+u7NWlq+PRsHbr7r7dpCmvzrZxa96JJUNi0Y5w9VqG5ZNeCVMoDcA==", - "dev": true, - "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", - "dev": true - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "requires": { - "pinkie": "^2.0.0" - } - }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - } - }, - "pluralize": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", - "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", - "dev": true - }, - "pn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", - "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", - "dev": true - }, - "popper.js": { - "version": "1.14.4", - "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.14.4.tgz", - "integrity": "sha1-juwdj/AqWjoVLdQ0FKFce3n9abY=" - }, - "portfinder": { - "version": "1.0.17", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.17.tgz", - "integrity": "sha512-syFcRIRzVI1BoEFOCaAiizwDolh1S1YXSodsVhncbhjzjZQulhczNRbqnUl9N31Q4dKGOXsNDqxC2BWBgSMqeQ==", - "dev": true, - "requires": { - "async": "^1.5.2", - "debug": "^2.2.0", - "mkdirp": "0.5.x" - }, - "dependencies": { - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true - } - } - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true - }, - "postcss": { - "version": "6.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "source-map": "^0.6.1", - "supports-color": "^5.4.0" - } - }, - "postcss-calc": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", - "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=", - "dev": true, - "requires": { - "postcss": "^5.0.2", - "postcss-message-helpers": "^2.0.0", - "reduce-css-calc": "^1.2.6" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "postcss-colormin": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz", - "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=", - "dev": true, - "requires": { - "colormin": "^1.0.5", - "postcss": "^5.0.13", - "postcss-value-parser": "^3.2.3" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "postcss-convert-values": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz", - "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=", - "dev": true, - "requires": { - "postcss": "^5.0.11", - "postcss-value-parser": "^3.1.2" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "postcss-discard-comments": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", - "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=", - "dev": true, - "requires": { - "postcss": "^5.0.14" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "postcss-discard-duplicates": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz", - "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=", - "dev": true, - "requires": { - "postcss": "^5.0.4" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "postcss-discard-empty": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", - "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=", - "dev": true, - "requires": { - "postcss": "^5.0.14" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "postcss-discard-overridden": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", - "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=", - "dev": true, - "requires": { - "postcss": "^5.0.16" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "postcss-discard-unused": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", - "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=", - "dev": true, - "requires": { - "postcss": "^5.0.14", - "uniqs": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "postcss-filter-plugins": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.3.tgz", - "integrity": "sha512-T53GVFsdinJhgwm7rg1BzbeBRomOg9y5MBVhGcsV0CxurUdVj1UlPdKtn7aqYA/c/QVkzKMjq2bSV5dKG5+AwQ==", - "dev": true, - "requires": { - "postcss": "^5.0.4" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "postcss-import": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-11.1.0.tgz", - "integrity": "sha512-5l327iI75POonjxkXgdRCUS+AlzAdBx4pOvMEhTKTCjb1p8IEeVR9yx3cPbmN7LIWJLbfnIXxAhoB4jpD0c/Cw==", - "dev": true, - "requires": { - "postcss": "^6.0.1", - "postcss-value-parser": "^3.2.3", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - } - }, - "postcss-load-config": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.0.0.tgz", - "integrity": "sha512-V5JBLzw406BB8UIfsAWSK2KSwIJ5yoEIVFb4gVkXci0QdKgA24jLmHZ/ghe/GgX0lJ0/D1uUK1ejhzEY94MChQ==", - "dev": true, - "requires": { - "cosmiconfig": "^4.0.0", - "import-cwd": "^2.0.0" - } - }, - "postcss-load-options": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-load-options/-/postcss-load-options-1.2.0.tgz", - "integrity": "sha1-sJixVZ3awt8EvAuzdfmaXP4rbYw=", - "dev": true, - "requires": { - "cosmiconfig": "^2.1.0", - "object-assign": "^4.1.0" - }, - "dependencies": { - "cosmiconfig": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", - "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", - "dev": true, - "requires": { - "is-directory": "^0.3.1", - "js-yaml": "^3.4.3", - "minimist": "^1.2.0", - "object-assign": "^4.1.0", - "os-homedir": "^1.0.1", - "parse-json": "^2.2.0", - "require-from-string": "^1.1.0" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "require-from-string": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz", - "integrity": "sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=", - "dev": true - } - } - }, - "postcss-load-plugins": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/postcss-load-plugins/-/postcss-load-plugins-2.3.0.tgz", - "integrity": "sha1-dFdoEWWZrKLwCfrUJrABdQSdjZI=", - "dev": true, - "requires": { - "cosmiconfig": "^2.1.1", - "object-assign": "^4.1.0" - }, - "dependencies": { - "cosmiconfig": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", - "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", - "dev": true, - "requires": { - "is-directory": "^0.3.1", - "js-yaml": "^3.4.3", - "minimist": "^1.2.0", - "object-assign": "^4.1.0", - "os-homedir": "^1.0.1", - "parse-json": "^2.2.0", - "require-from-string": "^1.1.0" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "require-from-string": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz", - "integrity": "sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=", - "dev": true - } - } - }, - "postcss-loader": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-2.1.6.tgz", - "integrity": "sha512-hgiWSc13xVQAq25cVw80CH0l49ZKlAnU1hKPOdRrNj89bokRr/bZF2nT+hebPPF9c9xs8c3gw3Fr2nxtmXYnNg==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "postcss": "^6.0.0", - "postcss-load-config": "^2.0.0", - "schema-utils": "^0.4.0" - }, - "dependencies": { - "ajv": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz", - "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", - "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", - "dev": true - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "schema-utils": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", - "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0" - } - } - } - }, - "postcss-merge-idents": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", - "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=", - "dev": true, - "requires": { - "has": "^1.0.1", - "postcss": "^5.0.10", - "postcss-value-parser": "^3.1.1" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "postcss-merge-longhand": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", - "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=", - "dev": true, - "requires": { - "postcss": "^5.0.4" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "postcss-merge-rules": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz", - "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=", - "dev": true, - "requires": { - "browserslist": "^1.5.2", - "caniuse-api": "^1.5.2", - "postcss": "^5.0.4", - "postcss-selector-parser": "^2.2.2", - "vendors": "^1.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "browserslist": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", - "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", - "dev": true, - "requires": { - "caniuse-db": "^1.0.30000639", - "electron-to-chromium": "^1.2.7" - } - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "postcss-message-helpers": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz", - "integrity": "sha1-pPL0+rbk/gAvCu0ABHjN9S+bpg4=", - "dev": true - }, - "postcss-minify-font-values": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", - "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=", - "dev": true, - "requires": { - "object-assign": "^4.0.1", - "postcss": "^5.0.4", - "postcss-value-parser": "^3.0.2" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "postcss-minify-gradients": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", - "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=", - "dev": true, - "requires": { - "postcss": "^5.0.12", - "postcss-value-parser": "^3.3.0" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "postcss-minify-params": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", - "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=", - "dev": true, - "requires": { - "alphanum-sort": "^1.0.1", - "postcss": "^5.0.2", - "postcss-value-parser": "^3.0.2", - "uniqs": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "postcss-minify-selectors": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", - "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=", - "dev": true, - "requires": { - "alphanum-sort": "^1.0.2", - "has": "^1.0.1", - "postcss": "^5.0.14", - "postcss-selector-parser": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "postcss-modules-extract-imports": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz", - "integrity": "sha1-ZhQOzs447wa/DT41XWm/WdFB6oU=", - "dev": true, - "requires": { - "postcss": "^6.0.1" - } - }, - "postcss-modules-local-by-default": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", - "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", - "dev": true, - "requires": { - "css-selector-tokenizer": "^0.7.0", - "postcss": "^6.0.1" - } - }, - "postcss-modules-scope": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", - "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", - "dev": true, - "requires": { - "css-selector-tokenizer": "^0.7.0", - "postcss": "^6.0.1" - } - }, - "postcss-modules-values": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", - "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", - "dev": true, - "requires": { - "icss-replace-symbols": "^1.1.0", - "postcss": "^6.0.1" - } - }, - "postcss-normalize-charset": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", - "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=", - "dev": true, - "requires": { - "postcss": "^5.0.5" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "postcss-normalize-url": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", - "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=", - "dev": true, - "requires": { - "is-absolute-url": "^2.0.0", - "normalize-url": "^1.4.0", - "postcss": "^5.0.14", - "postcss-value-parser": "^3.2.3" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "postcss-ordered-values": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", - "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=", - "dev": true, - "requires": { - "postcss": "^5.0.4", - "postcss-value-parser": "^3.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "postcss-reduce-idents": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", - "integrity": "sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=", - "dev": true, - "requires": { - "postcss": "^5.0.4", - "postcss-value-parser": "^3.0.2" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "postcss-reduce-initial": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", - "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=", - "dev": true, - "requires": { - "postcss": "^5.0.4" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "postcss-reduce-transforms": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", - "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=", - "dev": true, - "requires": { - "has": "^1.0.1", - "postcss": "^5.0.8", - "postcss-value-parser": "^3.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "postcss-selector-parser": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz", - "integrity": "sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=", - "dev": true, - "requires": { - "flatten": "^1.0.2", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - }, - "postcss-svgo": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", - "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=", - "dev": true, - "requires": { - "is-svg": "^2.0.0", - "postcss": "^5.0.14", - "postcss-value-parser": "^3.2.3", - "svgo": "^0.7.0" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "postcss-unique-selectors": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", - "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=", - "dev": true, - "requires": { - "alphanum-sort": "^1.0.1", - "postcss": "^5.0.4", - "uniqs": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "postcss-url": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/postcss-url/-/postcss-url-7.3.2.tgz", - "integrity": "sha512-QMV5mA+pCYZQcUEPQkmor9vcPQ2MT+Ipuu8qdi1gVxbNiIiErEGft+eny1ak19qALoBkccS5AHaCaCDzh7b9MA==", - "dev": true, - "requires": { - "mime": "^1.4.1", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.0", - "postcss": "^6.0.1", - "xxhashjs": "^0.2.1" - } - }, - "postcss-value-parser": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", - "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=", - "dev": true - }, - "postcss-zindex": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", - "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=", - "dev": true, - "requires": { - "has": "^1.0.1", - "postcss": "^5.0.4", - "uniqs": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", - "dev": true - }, - "preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", - "dev": true - }, - "prettier": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.14.2.tgz", - "integrity": "sha512-McHPg0n1pIke+A/4VcaS2en+pTNjy4xF+Uuq86u/5dyDO59/TtFZtQ708QIRkEZ3qwKz3GVkVa6mpxK/CpB8Rg==", - "dev": true - }, - "pretty": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pretty/-/pretty-2.0.0.tgz", - "integrity": "sha1-rbx5YLe7/iiaVX3F9zdhmiINBqU=", - "dev": true, - "requires": { - "condense-newlines": "^0.2.1", - "extend-shallow": "^2.0.1", - "js-beautify": "^1.6.12" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "pretty-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", - "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", - "dev": true, - "requires": { - "renderkid": "^2.0.1", - "utila": "~0.4" - } - }, - "pretty-format": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-22.4.3.tgz", - "integrity": "sha512-S4oT9/sT6MN7/3COoOy+ZJeA92VmOnveLHgrwBE3Z1W5N9S2A1QGNYiE1z75DAENbJrXXUb+OWXhpJcg05QKQQ==", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0", - "ansi-styles": "^3.2.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - } - } - }, - "private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", - "dev": true - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true - }, - "progress": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", - "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", - "dev": true - }, - "promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", - "dev": true - }, - "proto-list": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", - "dev": true - }, - "proxy-addr": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", - "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", - "dev": true, - "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.8.0" - } - }, - "proxy-agent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-2.0.0.tgz", - "integrity": "sha1-V+tTR6qAXXTsaByyVknbo5yTNJk=", - "dev": true, - "requires": { - "agent-base": "2", - "debug": "2", - "extend": "3", - "http-proxy-agent": "1", - "https-proxy-agent": "1", - "lru-cache": "~2.6.5", - "pac-proxy-agent": "1", - "socks-proxy-agent": "2" - }, - "dependencies": { - "lru-cache": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.5.tgz", - "integrity": "sha1-5W1jVBSO3o13B7WNFDIg/QjfD9U=", - "dev": true - } - } - }, - "prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", - "dev": true - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "psl": { - "version": "1.1.29", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", - "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==", - "dev": true - }, - "public-encrypt": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.2.tgz", - "integrity": "sha512-4kJ5Esocg8X3h8YgJsKAuoesBgB7mqH3eowiDzMUPKiRDDE7E/BqqZD1hnTByIaAFiwAw246YEltSq7tdrOH0Q==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1" - } - }, - "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "dev": true, - "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - } - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", - "dev": true - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - }, - "query-string": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", - "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", - "dev": true, - "requires": { - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" - } - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true - }, - "querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", - "dev": true - }, - "querystringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.0.0.tgz", - "integrity": "sha512-eTPo5t/4bgaMNZxyjWx6N2a6AuE0mq51KWvpc7nU/MAqixcI6v6KrGUKES0HaomdnolQBBXU/++X6/QQ9KL4tw==", - "dev": true - }, - "randomatic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.0.tgz", - "integrity": "sha512-KnGPVE0lo2WoXxIZ7cPR8YBpiol4gsSuOwDSg410oHh80ZMp5EiypNqL2K4Z77vJn6lB5rap7IkAmcUlalcnBQ==", - "dev": true, - "requires": { - "is-number": "^4.0.0", - "kind-of": "^6.0.0", - "math-random": "^1.0.1" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - } - } - }, - "randombytes": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", - "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dev": true, - "requires": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } - }, - "range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", - "dev": true - }, - "raw-body": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", - "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", - "dev": true, - "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.3", - "iconv-lite": "0.4.23", - "unpipe": "1.0.0" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - } - } - }, - "read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", - "dev": true, - "requires": { - "pify": "^2.3.0" - } - }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "dev": true, - "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" - } - }, - "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "dev": true, - "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" - }, - "dependencies": { - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "^2.0.0" - } - } - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "readdirp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", - "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "minimatch": "^3.0.2", - "readable-stream": "^2.0.2", - "set-immediate-shim": "^1.0.1" - } - }, - "realpath-native": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.0.2.tgz", - "integrity": "sha512-+S3zTvVt9yTntFrBpm7TQmQ3tzpCrnA1a/y+3cUHAc9ZR6aIjG0WNLR+Rj79QpJktY+VeW/TQtFlQ1bzsehI8g==", - "dev": true, - "requires": { - "util.promisify": "^1.0.0" - } - }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "dev": true, - "requires": { - "resolve": "^1.1.6" - } - }, - "redent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", - "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", - "dev": true, - "requires": { - "indent-string": "^2.1.0", - "strip-indent": "^1.0.1" - } - }, - "reduce-css-calc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", - "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", - "dev": true, - "requires": { - "balanced-match": "^0.4.2", - "math-expression-evaluator": "^1.2.14", - "reduce-function-call": "^1.0.1" - }, - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", - "dev": true - } - } - }, - "reduce-function-call": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.2.tgz", - "integrity": "sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk=", - "dev": true, - "requires": { - "balanced-match": "^0.4.2" - }, - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", - "dev": true - } - } - }, - "regenerate": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", - "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", - "dev": true - }, - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" - }, - "regenerator-transform": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", - "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", - "dev": true, - "requires": { - "babel-runtime": "^6.18.0", - "babel-types": "^6.19.0", - "private": "^0.1.6" - } - }, - "regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", - "dev": true, - "requires": { - "is-equal-shallow": "^0.1.3" - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "regexpp": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", - "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==", - "dev": true - }, - "regexpu-core": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", - "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", - "dev": true, - "requires": { - "regenerate": "^1.2.1", - "regjsgen": "^0.2.0", - "regjsparser": "^0.1.4" - } - }, - "regjsgen": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", - "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", - "dev": true - }, - "regjsparser": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", - "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - } - } - }, - "relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", - "dev": true - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "renderkid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.1.tgz", - "integrity": "sha1-iYyr/Ivt5Le5ETWj/9Mj5YwNsxk=", - "dev": true, - "requires": { - "css-select": "^1.1.0", - "dom-converter": "~0.1", - "htmlparser2": "~3.3.0", - "strip-ansi": "^3.0.0", - "utila": "~0.3" - }, - "dependencies": { - "utila": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz", - "integrity": "sha1-1+jn1+MJEHCSsF+NloiCTWM6QiY=", - "dev": true - } - } - }, - "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true, - "requires": { - "is-finite": "^1.0.0" - } - }, - "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, - "request-promise-core": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", - "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", - "dev": true, - "requires": { - "lodash": "^4.13.1" - } - }, - "request-promise-native": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.5.tgz", - "integrity": "sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU=", - "dev": true, - "requires": { - "request-promise-core": "1.1.1", - "stealthy-require": "^1.1.0", - "tough-cookie": ">=2.3.3" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true - }, - "require-uncached": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", - "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", - "dev": true, - "requires": { - "caller-path": "^0.1.0", - "resolve-from": "^1.0.0" - } - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true - }, - "resolve": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", - "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", - "dev": true, - "requires": { - "path-parse": "^1.0.5" - } - }, - "resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", - "dev": true, - "requires": { - "resolve-from": "^3.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", - "dev": true - } - } - }, - "resolve-from": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", - "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", - "dev": true - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true - }, - "right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "dev": true, - "requires": { - "align-text": "^0.1.1" - } - }, - "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", - "dev": true, - "requires": { - "glob": "^7.0.5" - } - }, - "ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "rsvp": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-3.6.2.tgz", - "integrity": "sha512-OfWGQTb9vnwRjwtA2QwpG2ICclHC3pgXZO5xt8H2EfgDquO0qVdSb5T88L4qJVAEugbS56pAuV4XZM58UX8ulw==", - "dev": true - }, - "run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", - "requires": { - "is-promise": "^2.1.0" - } - }, - "run-queue": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", - "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", - "dev": true, - "requires": { - "aproba": "^1.1.1" - } - }, - "rw": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=" - }, - "rx": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz", - "integrity": "sha1-pfE/957zt0D+MKqAP7CfmIBdR4I=" - }, - "rx-lite": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", - "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", - "dev": true - }, - "rx-lite-aggregates": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", - "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", - "dev": true, - "requires": { - "rx-lite": "*" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "requires": { - "ret": "~0.1.10" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sane": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/sane/-/sane-2.5.2.tgz", - "integrity": "sha1-tNwYYcIbQn6SlQej51HiosuKs/o=", - "dev": true, - "requires": { - "anymatch": "^2.0.0", - "capture-exit": "^1.2.0", - "exec-sh": "^0.2.0", - "fb-watchman": "^2.0.0", - "fsevents": "^1.2.3", - "micromatch": "^3.1.4", - "minimist": "^1.1.1", - "walker": "~1.0.5", - "watch": "~0.18.0" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - }, - "schema-utils": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz", - "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=", - "dev": true, - "requires": { - "ajv": "^5.0.0" - } - }, - "select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", - "dev": true - }, - "selenium-server": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/selenium-server/-/selenium-server-3.14.0.tgz", - "integrity": "sha512-+CCi1ED+7f36xpeGUqB8bWHde0To+9ZtegBHwWkbd9NsZcvANrtr8wlRNqHSD8yGmC0F7rixbgwiJEK9mTCLww==", - "dev": true - }, - "selfsigned": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.3.tgz", - "integrity": "sha512-vmZenZ+8Al3NLHkWnhBQ0x6BkML1eCP2xEi3JE+f3D9wW9fipD9NNJHYtE9XJM4TsPaHGZJIamrSI6MTg1dU2Q==", - "dev": true, - "requires": { - "node-forge": "0.7.5" - } - }, - "semver": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", - "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", - "dev": true - }, - "send": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", - "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", - "dev": true, - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.6.2", - "mime": "1.4.1", - "ms": "2.0.0", - "on-finished": "~2.3.0", - "range-parser": "~1.2.0", - "statuses": "~1.4.0" - }, - "dependencies": { - "mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", - "dev": true - }, - "statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", - "dev": true - } - } - }, - "serialize-javascript": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.5.0.tgz", - "integrity": "sha512-Ga8c8NjAAp46Br4+0oZ2WxJCwIzwP60Gq1YPgU+39PiTVxyed/iKE/zyZI6+UlVYH5Q4PaQdHhcegIFPZTUfoQ==", - "dev": true - }, - "serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", - "dev": true, - "requires": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - } - }, - "serve-static": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", - "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", - "dev": true, - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.2", - "send": "0.16.2" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", - "dev": true - }, - "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true - }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "shelljs": { - "version": "0.7.8", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", - "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", - "dev": true, - "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - } - }, - "shellwords": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", - "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "dev": true - }, - "sigmund": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", - "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", - "dev": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" - }, - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", - "dev": true - }, - "slice-ansi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", - "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0" - } - }, - "smart-buffer": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-1.1.15.tgz", - "integrity": "sha1-fxFLW2X6s+KjWqd1uxLw0cZJvxY=", - "dev": true - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "requires": { - "kind-of": "^3.2.0" - } - }, - "sockjs": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", - "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", - "dev": true, - "requires": { - "faye-websocket": "^0.10.0", - "uuid": "^3.0.1" - } - }, - "sockjs-client": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.5.tgz", - "integrity": "sha1-G7fA9yIsQPQq3xT0RCy9Eml3GoM=", - "dev": true, - "requires": { - "debug": "^2.6.6", - "eventsource": "0.1.6", - "faye-websocket": "~0.11.0", - "inherits": "^2.0.1", - "json3": "^3.3.2", - "url-parse": "^1.1.8" - }, - "dependencies": { - "faye-websocket": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", - "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", - "dev": true, - "requires": { - "websocket-driver": ">=0.5.1" - } - } - } - }, - "socks": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/socks/-/socks-1.1.10.tgz", - "integrity": "sha1-W4t/x8jzQcU+0FbpKbe/Tei6e1o=", - "dev": true, - "requires": { - "ip": "^1.1.4", - "smart-buffer": "^1.0.13" - }, - "dependencies": { - "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", - "dev": true - } - } - }, - "socks-proxy-agent": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-2.1.1.tgz", - "integrity": "sha512-sFtmYqdUK5dAMh85H0LEVFUCO7OhJJe1/z2x/Z6mxp3s7/QPf1RkZmpZy+BpuU0bEjcV9npqKjq9Y3kwFUjnxw==", - "dev": true, - "requires": { - "agent-base": "2", - "extend": "3", - "socks": "~1.1.5" - } - }, - "sort-keys": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", - "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", - "dev": true, - "requires": { - "is-plain-obj": "^1.0.0" - } - }, - "source-list-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz", - "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-resolve": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", - "dev": true, - "requires": { - "atob": "^2.1.1", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", - "dev": true, - "requires": { - "source-map": "^0.5.6" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true - }, - "spdx-correct": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", - "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", - "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", - "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==", - "dev": true - }, - "spdy": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-3.4.7.tgz", - "integrity": "sha1-Qv9B7OXMD5mjpsKKq7c/XDsDrLw=", - "dev": true, - "requires": { - "debug": "^2.6.8", - "handle-thing": "^1.2.5", - "http-deceiver": "^1.2.7", - "safe-buffer": "^5.0.1", - "select-hose": "^2.0.0", - "spdy-transport": "^2.0.18" - } - }, - "spdy-transport": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-2.1.0.tgz", - "integrity": "sha512-bpUeGpZcmZ692rrTiqf9/2EUakI6/kXX1Rpe0ib/DyOzbiexVfXkw6GnvI9hVGvIwVaUhkaBojjCZwLNRGQg1g==", - "dev": true, - "requires": { - "debug": "^2.6.8", - "detect-node": "^2.0.3", - "hpack.js": "^2.1.6", - "obuf": "^1.1.1", - "readable-stream": "^2.2.9", - "safe-buffer": "^5.0.1", - "wbuf": "^1.7.2" - } - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "sshpk": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", - "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", - "dev": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "ssri": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", - "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.1" - } - }, - "stack-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.1.tgz", - "integrity": "sha1-1PM6tU6OOHeLDKXP07OvsS22hiA=", - "dev": true - }, - "stackframe": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.0.4.tgz", - "integrity": "sha512-to7oADIniaYwS3MhtCa/sQhrxidCCQiF/qp4/m5iN3ipf0Y7Xlri0f6eG29r08aL7JYl8n32AF3Q5GYBZ7K8vw==", - "dev": true - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "dev": true - }, - "stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", - "dev": true - }, - "stream-browserify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", - "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", - "dev": true, - "requires": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" - } - }, - "stream-each": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", - "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" - } - }, - "stream-http": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", - "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", - "dev": true, - "requires": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.3.6", - "to-arraybuffer": "^1.0.0", - "xtend": "^4.0.0" - } - }, - "stream-shift": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", - "dev": true - }, - "strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", - "dev": true - }, - "string-length": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz", - "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=", - "dev": true, - "requires": { - "astral-regex": "^1.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "^0.2.0" - } - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "strip-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", - "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", - "dev": true, - "requires": { - "get-stdin": "^4.0.1" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "svg-tags": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", - "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=", - "dev": true - }, - "svgo": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz", - "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=", - "dev": true, - "requires": { - "coa": "~1.0.1", - "colors": "~1.1.2", - "csso": "~2.3.1", - "js-yaml": "~3.7.0", - "mkdirp": "~0.5.1", - "sax": "~1.2.1", - "whet.extend": "~0.9.9" - } - }, - "symbol-tree": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", - "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", - "dev": true - }, - "table": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", - "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", - "dev": true, - "requires": { - "ajv": "^5.2.3", - "ajv-keywords": "^2.1.0", - "chalk": "^2.1.0", - "lodash": "^4.17.4", - "slice-ansi": "1.0.0", - "string-width": "^2.1.1" - } - }, - "tapable": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz", - "integrity": "sha1-KcNXB8K3DlDQdIK10gLo7URtr9Q=", - "dev": true - }, - "test-exclude": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-4.2.3.tgz", - "integrity": "sha512-SYbXgY64PT+4GAL2ocI3HwPa4Q4TBKm0cwAVeKOt/Aoc0gSpNRjJX8w0pA1LMKZ3LBmd8pYBqApFNQLII9kavA==", - "dev": true, - "requires": { - "arrify": "^1.0.1", - "micromatch": "^2.3.11", - "object-assign": "^4.1.0", - "read-pkg-up": "^1.0.1", - "require-main-filename": "^1.0.1" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "throat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", - "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, - "through2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", - "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", - "dev": true, - "requires": { - "readable-stream": "^2.1.5", - "xtend": "~4.0.1" - } - }, - "thunkify": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/thunkify/-/thunkify-2.1.2.tgz", - "integrity": "sha1-+qDp0jDFGsyVyhOjYawFyn4EVT0=", - "dev": true - }, - "thunky": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.2.tgz", - "integrity": "sha1-qGLgGOP7HqLsP85dVWBc9X8kc3E=", - "dev": true - }, - "time-stamp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-2.1.0.tgz", - "integrity": "sha512-lJbq6KsFhZJtN3fPUVje1tq/hHsJOKUUcUj/MGCiQR6qWBDcyi5kxL9J7/RnaEChCn0+L/DUN2WvemDrkk4i3Q==", - "dev": true - }, - "timers-browserify": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", - "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", - "dev": true, - "requires": { - "setimmediate": "^1.0.4" - } - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "tmpl": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", - "dev": true - }, - "to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", - "dev": true - }, - "to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", - "dev": true - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - } - } - }, - "toposort": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz", - "integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=", - "dev": true - }, - "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "dev": true, - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - } - }, - "tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", - "dev": true, - "requires": { - "punycode": "^2.1.0" - }, - "dependencies": { - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - } - } - }, - "trim-newlines": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", - "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", - "dev": true - }, - "trim-right": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", - "dev": true - }, - "tryer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", - "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==", - "dev": true - }, - "tsconfig": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", - "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", - "dev": true, - "requires": { - "@types/strip-bom": "^3.0.0", - "@types/strip-json-comments": "0.0.30", - "strip-bom": "^3.0.0", - "strip-json-comments": "^2.0.0" - }, - "dependencies": { - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } - } - }, - "tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", - "dev": true - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true, - "optional": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "type-detect": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", - "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=", - "dev": true - }, - "type-is": { - "version": "1.6.16", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", - "dev": true, - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.18" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, - "uglify-js": { - "version": "3.4.9", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz", - "integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==", - "dev": true, - "requires": { - "commander": "~2.17.1", - "source-map": "~0.6.1" - } - }, - "uglify-to-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", - "dev": true, - "optional": true - }, - "uglifyjs-webpack-plugin": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz", - "integrity": "sha512-ovHIch0AMlxjD/97j9AYovZxG5wnHOPkL7T1GKochBADp/Zwc44pEWNqpKl1Loupp1WhFg7SlYmHZRUfdAacgw==", - "dev": true, - "requires": { - "cacache": "^10.0.4", - "find-cache-dir": "^1.0.0", - "schema-utils": "^0.4.5", - "serialize-javascript": "^1.4.0", - "source-map": "^0.6.1", - "uglify-es": "^3.3.4", - "webpack-sources": "^1.1.0", - "worker-farm": "^1.5.2" - }, - "dependencies": { - "ajv": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz", - "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", - "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", - "dev": true - }, - "commander": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", - "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", - "dev": true - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "schema-utils": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", - "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0" - } - }, - "uglify-es": { - "version": "3.3.9", - "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", - "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", - "dev": true, - "requires": { - "commander": "~2.13.0", - "source-map": "~0.6.1" - } - } - } - }, - "unc-path-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", - "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", - "dev": true - }, - "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } - } - }, - "uniq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", - "dev": true - }, - "uniqs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", - "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", - "dev": true - }, - "unique-filename": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.0.tgz", - "integrity": "sha1-0F8v5AMlYIcfMOk8vnNe6iAVFPM=", - "dev": true, - "requires": { - "unique-slug": "^2.0.0" - } - }, - "unique-slug": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.0.tgz", - "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4" - } - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "dev": true - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "upath": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", - "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", - "dev": true - }, - "upper-case": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", - "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", - "dev": true - }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - }, - "dependencies": { - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - } - } - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - } - } - }, - "url-loader": { - "version": "0.5.9", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-0.5.9.tgz", - "integrity": "sha512-B7QYFyvv+fOBqBVeefsxv6koWWtjmHaMFT6KZWti4KRw8YUD/hOU+3AECvXuzyVawIBx3z7zQRejXCDSO5kk1Q==", - "dev": true, - "requires": { - "loader-utils": "^1.0.2", - "mime": "1.3.x" - }, - "dependencies": { - "mime": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.6.tgz", - "integrity": "sha1-WR2E02U6awtKO5343lqoEI5y5eA=", - "dev": true - } - } - }, - "url-parse": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.3.tgz", - "integrity": "sha512-rh+KuAW36YKo0vClhQzLLveoj8FwPJNu65xLb7Mrt+eZht0IPT0IXgSv8gcMegZ6NvjJUALf6Mf25POlMwD1Fw==", - "dev": true, - "requires": { - "querystringify": "^2.0.0", - "requires-port": "^1.0.0" - } - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true - }, - "util": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", - "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", - "dev": true, - "requires": { - "inherits": "2.0.3" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "util.promisify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", - "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "object.getownpropertydescriptors": "^2.0.3" - } - }, - "utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", - "dev": true - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "dev": true - }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "dev": true - }, - "vendors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.2.tgz", - "integrity": "sha512-w/hry/368nO21AN9QljsaIhb9ZiZtZARoVH5f3CsFbawdLdayCgKRPup7CggujvySMxx0I91NOyxdVENohprLQ==", - "dev": true - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "vm-browserify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", - "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", - "dev": true, - "requires": { - "indexof": "0.0.1" - } - }, - "vue": { - "version": "2.5.17", - "resolved": "https://registry.npmjs.org/vue/-/vue-2.5.17.tgz", - "integrity": "sha512-mFbcWoDIJi0w0Za4emyLiW72Jae0yjANHbCVquMKijcavBGypqlF7zHRgMa5k4sesdv7hv2rB4JPdZfR+TPfhQ==" - }, - "vue-eslint-parser": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-2.0.3.tgz", - "integrity": "sha512-ZezcU71Owm84xVF6gfurBQUGg8WQ+WZGxgDEQu1IHFBZNx7BFZg3L1yHxrCBNNwbwFtE1GuvfJKMtb6Xuwc/Bw==", - "dev": true, - "requires": { - "debug": "^3.1.0", - "eslint-scope": "^3.7.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^3.5.2", - "esquery": "^1.0.0", - "lodash": "^4.17.4" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "vue-functional-data-merge": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/vue-functional-data-merge/-/vue-functional-data-merge-2.0.6.tgz", - "integrity": "sha512-eivElFOJwhXJopKlq71/8onDxOKK4quPwWGFF9yIVjpU2sNzxISRpufu18bh674ivSADuEAPU2OhT+vrH0E9Mg==" - }, - "vue-hot-reload-api": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.0.tgz", - "integrity": "sha512-2j/t+wIbyVMP5NvctQoSUvLkYKoWAAk2QlQiilrM2a6/ulzFgdcLUJfTvs4XQ/3eZhHiBmmEojbjmM4AzZj8JA==", - "dev": true - }, - "vue-jest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/vue-jest/-/vue-jest-1.4.0.tgz", - "integrity": "sha512-X5YXTXcpklijK3wXG/CiW8Frkz+YPBjR+//FD5rcmlnmEelz+8AQpKA8vhbAHJx3gOhA2tkWt8XEjvxq1S0heg==", - "dev": true, - "requires": { - "babel-core": "^6.25.0", - "babel-preset-vue-app": "^1.3.1", - "chalk": "^2.1.0", - "find-babel-config": "^1.1.0", - "js-beautify": "^1.6.14", - "node-cache": "^4.1.1", - "object-assign": "^4.1.1", - "source-map": "^0.5.6", - "tsconfig": "^7.0.0", - "vue-template-es2015-compiler": "^1.5.3" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "vue-loader": { - "version": "13.7.3", - "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-13.7.3.tgz", - "integrity": "sha512-ACCwbfeC6HjY2pnDii+Zer+MZ6sdOtwvLmDXRK/BoD3WNR551V22R6KEagwHoTRJ0ZlIhpCBkptpCU6+Ri/05w==", - "dev": true, - "requires": { - "consolidate": "^0.14.0", - "hash-sum": "^1.0.2", - "loader-utils": "^1.1.0", - "lru-cache": "^4.1.1", - "postcss": "^6.0.8", - "postcss-load-config": "^1.1.0", - "postcss-selector-parser": "^2.0.0", - "prettier": "^1.7.0", - "resolve": "^1.4.0", - "source-map": "^0.6.1", - "vue-hot-reload-api": "^2.2.0", - "vue-style-loader": "^3.0.0", - "vue-template-es2015-compiler": "^1.6.0" - }, - "dependencies": { - "cosmiconfig": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", - "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", - "dev": true, - "requires": { - "is-directory": "^0.3.1", - "js-yaml": "^3.4.3", - "minimist": "^1.2.0", - "object-assign": "^4.1.0", - "os-homedir": "^1.0.1", - "parse-json": "^2.2.0", - "require-from-string": "^1.1.0" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "postcss-load-config": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-1.2.0.tgz", - "integrity": "sha1-U56a/J3chiASHr+djDZz4M5Q0oo=", - "dev": true, - "requires": { - "cosmiconfig": "^2.1.0", - "object-assign": "^4.1.0", - "postcss-load-options": "^1.2.0", - "postcss-load-plugins": "^2.3.0" - } - }, - "require-from-string": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz", - "integrity": "sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=", - "dev": true - } - } - }, - "vue-router": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.0.1.tgz", - "integrity": "sha512-vLLoY452L+JBpALMP5UHum9+7nzR9PeIBCghU9ZtJ1eWm6ieUI8Zb/DI3MYxH32bxkjzYV1LRjNv4qr8d+uX/w==" - }, - "vue-slider-component": { - "version": "2.7.7", - "resolved": "https://registry.npmjs.org/vue-slider-component/-/vue-slider-component-2.7.7.tgz", - "integrity": "sha512-Gi3hIi9B6tVJ4Jkbar5gN+rBBZPncMgDwKPRWRKFwgKfMUrupB1HtWBylAUN0/kfhJm3DfDuzjr6NhLXS1BhEw==" - }, - "vue-style-loader": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-3.1.2.tgz", - "integrity": "sha512-ICtVdK/p+qXWpdSs2alWtsXt9YnDoYjQe0w5616j9+/EhjoxZkbun34uWgsMFnC1MhrMMwaWiImz3K2jK1Yp2Q==", - "dev": true, - "requires": { - "hash-sum": "^1.0.2", - "loader-utils": "^1.0.2" - } - }, - "vue-template-compiler": { - "version": "2.5.17", - "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.5.17.tgz", - "integrity": "sha512-63uI4syCwtGR5IJvZM0LN5tVsahrelomHtCxvRkZPJ/Tf3ADm1U1wG6KWycK3qCfqR+ygM5vewUvmJ0REAYksg==", - "dev": true, - "requires": { - "de-indent": "^1.0.2", - "he": "^1.1.0" - } - }, - "vue-template-es2015-compiler": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.6.0.tgz", - "integrity": "sha512-x3LV3wdmmERhVCYy3quqA57NJW7F3i6faas++pJQWtknWT+n7k30F4TVdHvCLn48peTJFRvCpxs3UuFPqgeELg==", - "dev": true - }, - "w3c-hr-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", - "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", - "dev": true, - "requires": { - "browser-process-hrtime": "^0.1.2" - } - }, - "walker": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", - "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", - "dev": true, - "requires": { - "makeerror": "1.0.x" - } - }, - "watch": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/watch/-/watch-0.18.0.tgz", - "integrity": "sha1-KAlUdsbffJDJYxOJkMClQj60uYY=", - "dev": true, - "requires": { - "exec-sh": "^0.2.0", - "minimist": "^1.2.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "watchpack": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", - "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", - "dev": true, - "requires": { - "chokidar": "^2.0.2", - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0" - } - }, - "wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "dev": true, - "requires": { - "minimalistic-assert": "^1.0.0" - } - }, - "webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "dev": true - }, - "webpack": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.12.0.tgz", - "integrity": "sha512-Sw7MdIIOv/nkzPzee4o0EdvCuPmxT98+vVpIvwtcwcF1Q4SDSNp92vwcKc4REe7NItH9f1S4ra9FuQ7yuYZ8bQ==", - "dev": true, - "requires": { - "acorn": "^5.0.0", - "acorn-dynamic-import": "^2.0.0", - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0", - "async": "^2.1.2", - "enhanced-resolve": "^3.4.0", - "escope": "^3.6.0", - "interpret": "^1.0.0", - "json-loader": "^0.5.4", - "json5": "^0.5.1", - "loader-runner": "^2.3.0", - "loader-utils": "^1.1.0", - "memory-fs": "~0.4.1", - "mkdirp": "~0.5.0", - "node-libs-browser": "^2.0.0", - "source-map": "^0.5.3", - "supports-color": "^4.2.1", - "tapable": "^0.2.7", - "uglifyjs-webpack-plugin": "^0.4.6", - "watchpack": "^1.4.0", - "webpack-sources": "^1.0.1", - "yargs": "^8.0.2" - }, - "dependencies": { - "ajv": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz", - "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", - "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", - "dev": true - }, - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "dev": true - }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "dev": true, - "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", - "wordwrap": "0.0.2" - } - }, - "enhanced-resolve": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz", - "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.4.0", - "object-assign": "^4.0.1", - "tapable": "^0.2.7" - } - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - } - }, - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "dev": true, - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "^2.0.0" - } - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "dev": true, - "requires": { - "has-flag": "^2.0.0" - } - }, - "tapable": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.8.tgz", - "integrity": "sha1-mTcqXJmb8t8WCvwNdL7U9HlIzSI=", - "dev": true - }, - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "dev": true, - "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" - }, - "dependencies": { - "yargs": { - "version": "3.10.0", - "resolved": "http://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true, - "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", - "window-size": "0.1.0" - } - } - } - }, - "uglifyjs-webpack-plugin": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz", - "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=", - "dev": true, - "requires": { - "source-map": "^0.5.6", - "uglify-js": "^2.8.29", - "webpack-sources": "^1.0.1" - } - }, - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "dev": true - }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true - }, - "yargs": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", - "integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=", - "dev": true, - "requires": { - "camelcase": "^4.1.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "read-pkg-up": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^7.0.0" - }, - "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" - }, - "dependencies": { - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - } - } - }, - "yargs-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", - "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", - "dev": true, - "requires": { - "camelcase": "^4.1.0" - }, - "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - } - } - } - } - }, - "webpack-bundle-analyzer": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.13.1.tgz", - "integrity": "sha512-rwxyfecTAxoarCC9VlHlIpfQCmmJ/qWD5bpbjkof+7HrNhTNZIwZITxN6CdlYL2axGmwNUQ+tFgcSOiNXMf/sQ==", - "dev": true, - "requires": { - "acorn": "^5.3.0", - "bfj-node4": "^5.2.0", - "chalk": "^2.3.0", - "commander": "^2.13.0", - "ejs": "^2.5.7", - "express": "^4.16.2", - "filesize": "^3.5.11", - "gzip-size": "^4.1.0", - "lodash": "^4.17.4", - "mkdirp": "^0.5.1", - "opener": "^1.4.3", - "ws": "^4.0.0" - }, - "dependencies": { - "ws": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-4.1.0.tgz", - "integrity": "sha512-ZGh/8kF9rrRNffkLFV4AzhvooEclrOH0xaugmqGsIfFgOE/pIz4fMc4Ef+5HSQqTEug2S9JZIWDR47duDSLfaA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0" - } - } - } - }, - "webpack-dev-middleware": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-1.12.2.tgz", - "integrity": "sha512-FCrqPy1yy/sN6U/SaEZcHKRXGlqU0DUaEBL45jkUYoB8foVb6wCnbIJ1HKIx+qUFTW+3JpVcCJCxZ8VATL4e+A==", - "dev": true, - "requires": { - "memory-fs": "~0.4.1", - "mime": "^1.5.0", - "path-is-absolute": "^1.0.0", - "range-parser": "^1.0.3", - "time-stamp": "^2.0.0" - }, - "dependencies": { - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "dev": true, - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - } - } - }, - "webpack-dev-server": { - "version": "2.11.3", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.11.3.tgz", - "integrity": "sha512-Qz22YEFhWx+M2vvJ+rQppRv39JA0h5NNbOOdODApdX6iZ52Diz7vTPXjF7kJlfn+Uc24Qr48I3SZ9yncQwRycg==", - "dev": true, - "requires": { - "ansi-html": "0.0.7", - "array-includes": "^3.0.3", - "bonjour": "^3.5.0", - "chokidar": "^2.0.0", - "compression": "^1.5.2", - "connect-history-api-fallback": "^1.3.0", - "debug": "^3.1.0", - "del": "^3.0.0", - "express": "^4.16.2", - "html-entities": "^1.2.0", - "http-proxy-middleware": "~0.17.4", - "import-local": "^1.0.0", - "internal-ip": "1.2.0", - "ip": "^1.1.5", - "killable": "^1.0.0", - "loglevel": "^1.4.1", - "opn": "^5.1.0", - "portfinder": "^1.0.9", - "selfsigned": "^1.9.1", - "serve-index": "^1.7.2", - "sockjs": "0.3.19", - "sockjs-client": "1.1.5", - "spdy": "^3.4.1", - "strip-ansi": "^3.0.0", - "supports-color": "^5.1.0", - "webpack-dev-middleware": "1.12.2", - "yargs": "6.6.0" - }, - "dependencies": { - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "dev": true - }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" - } - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "os-locale": { - "version": "1.4.0", - "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "dev": true, - "requires": { - "lcid": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "which-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", - "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", - "dev": true - }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true - }, - "yargs": { - "version": "6.6.0", - "resolved": "http://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", - "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=", - "dev": true, - "requires": { - "camelcase": "^3.0.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^1.4.0", - "read-pkg-up": "^1.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^1.0.2", - "which-module": "^1.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^4.2.0" - } - }, - "yargs-parser": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", - "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", - "dev": true, - "requires": { - "camelcase": "^3.0.0" - } - } - } - }, - "webpack-merge": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.1.4.tgz", - "integrity": "sha512-TmSe1HZKeOPey3oy1Ov2iS3guIZjWvMT2BBJDzzT5jScHTjVC3mpjJofgueEzaEd6ibhxRDD6MIblDr8tzh8iQ==", - "dev": true, - "requires": { - "lodash": "^4.17.5" - } - }, - "webpack-sources": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.2.0.tgz", - "integrity": "sha512-9BZwxR85dNsjWz3blyxdOhTgtnQvv3OEs5xofI0wPYTwu5kaWxS08UuD1oI7WLBLpRO+ylf0ofnXLXWmGb2WMw==", - "dev": true, - "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - }, - "websocket-driver": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", - "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", - "dev": true, - "requires": { - "http-parser-js": ">=0.4.0", - "websocket-extensions": ">=0.1.1" - } - }, - "websocket-extensions": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", - "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", - "dev": true - }, - "whatwg-encoding": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.4.tgz", - "integrity": "sha512-vM9KWN6MP2mIHZ86ytcyIv7e8Cj3KTfO2nd2c8PFDqcI4bxFmQp83ibq4wadq7rL9l9sZV6o9B0LTt8ygGAAXg==", - "dev": true, - "requires": { - "iconv-lite": "0.4.23" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - } - } - }, - "whatwg-mimetype": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.1.0.tgz", - "integrity": "sha512-FKxhYLytBQiUKjkYteN71fAUA3g6KpNXoho1isLiLSB3N1G4F35Q5vUxWfKFhBwi5IWF27VE6WxhrnnC+m0Mew==", - "dev": true - }, - "whatwg-url": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", - "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", - "dev": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - }, - "whet.extend": { - "version": "0.9.9", - "resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz", - "integrity": "sha1-+HfVv2SMl+WqVC+twW1qJZucEaE=", - "dev": true - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", - "dev": true - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - }, - "worker-farm": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz", - "integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==", - "dev": true, - "requires": { - "errno": "~0.1.7" - } - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "write": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", - "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - } - }, - "write-file-atomic": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz", - "integrity": "sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - }, - "ws": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", - "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0" - } - }, - "xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "dev": true - }, - "xregexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", - "integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=", - "dev": true - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true - }, - "xxhashjs": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz", - "integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==", - "dev": true, - "requires": { - "cuint": "^0.2.2" - } - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - }, - "yargs": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-10.1.2.tgz", - "integrity": "sha512-ivSoxqBGYOqQVruxD35+EyCFDYNEFL/Uo6FcOnz+9xZdZzK0Zzw4r4KhbrME1Oo2gOggwJod2MnsdamSG7H9ig==", - "dev": true, - "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.1.1", - "find-up": "^2.1.0", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^8.1.0" - }, - "dependencies": { - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true - } - } - }, - "yargs-parser": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-8.1.0.tgz", - "integrity": "sha512-yP+6QqN8BmrgW2ggLtTbdrOyBNSI7zBa4IykmiV5R1wl1JWNxQvWhMfMdmzIYtKU7oP3OOInY/tl2ov3BDjnJQ==", - "dev": true, - "requires": { - "camelcase": "^4.1.0" - } - }, - "yauzl": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", - "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", - "dev": true, - "requires": { - "fd-slicer": "~1.0.1" - } - } - } -} diff --git a/dmriprepViewer/package.json b/dmriprepViewer/package.json deleted file mode 100644 index c8f44ef0..00000000 --- a/dmriprepViewer/package.json +++ /dev/null @@ -1,91 +0,0 @@ -{ - "name": "dmriprep-viewer", - "version": "1.0.0", - "description": "Viewer for dmriprep reports", - "author": "akeshavan ", - "private": true, - "scripts": { - "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", - "start": "npm run dev", - "unit": "jest --config test/unit/jest.conf.js --coverage", - "e2e": "node test/e2e/runner.js", - "test": "npm run unit && npm run e2e", - "lint": "eslint --ext .js,.vue src test/unit test/e2e/specs", - "build": "node build/build.js" - }, - "dependencies": { - "axios": "^0.18.0", - "bootstrap-vue": "^2.0.0-rc.11", - "brainsprite.js": "git+https://git@github.com/SIMEXP/brainsprite.js.git", - "d3": "^5.7.0", - "lodash": "^4.17.11", - "vue": "^2.5.2", - "vue-router": "^3.0.1", - "vue-slider-component": "^2.7.7" - }, - "devDependencies": { - "autoprefixer": "^7.1.2", - "babel-core": "^6.22.1", - "babel-eslint": "^8.2.1", - "babel-helper-vue-jsx-merge-props": "^2.0.3", - "babel-jest": "^21.0.2", - "babel-loader": "^7.1.1", - "babel-plugin-dynamic-import-node": "^1.2.0", - "babel-plugin-syntax-jsx": "^6.18.0", - "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0", - "babel-plugin-transform-runtime": "^6.22.0", - "babel-plugin-transform-vue-jsx": "^3.5.0", - "babel-preset-env": "^1.3.2", - "babel-preset-stage-2": "^6.22.0", - "babel-register": "^6.22.0", - "chalk": "^2.0.1", - "chromedriver": "^2.27.2", - "copy-webpack-plugin": "^4.0.1", - "cross-spawn": "^5.0.1", - "css-loader": "^0.28.0", - "eslint": "^4.15.0", - "eslint-config-airbnb-base": "^11.3.0", - "eslint-friendly-formatter": "^3.0.0", - "eslint-import-resolver-webpack": "^0.8.3", - "eslint-loader": "^1.7.1", - "eslint-plugin-import": "^2.7.0", - "eslint-plugin-vue": "^4.0.0", - "extract-text-webpack-plugin": "^3.0.0", - "file-loader": "^1.1.4", - "friendly-errors-webpack-plugin": "^1.6.1", - "html-webpack-plugin": "^2.30.1", - "jest": "^22.0.4", - "jest-serializer-vue": "^0.3.0", - "nightwatch": "^0.9.12", - "node-notifier": "^5.1.2", - "optimize-css-assets-webpack-plugin": "^3.2.0", - "ora": "^1.2.0", - "portfinder": "^1.0.13", - "postcss-import": "^11.0.0", - "postcss-loader": "^2.0.8", - "postcss-url": "^7.2.1", - "rimraf": "^2.6.0", - "selenium-server": "^3.0.1", - "semver": "^5.3.0", - "shelljs": "^0.7.6", - "uglifyjs-webpack-plugin": "^1.1.1", - "url-loader": "^0.5.8", - "vue-jest": "^1.0.2", - "vue-loader": "^13.3.0", - "vue-style-loader": "^3.0.1", - "vue-template-compiler": "^2.5.2", - "webpack": "^3.6.0", - "webpack-bundle-analyzer": "^2.9.0", - "webpack-dev-server": "^2.9.1", - "webpack-merge": "^4.1.0" - }, - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - }, - "browserslist": [ - "> 1%", - "last 2 versions", - "not ie <= 8" - ] -} diff --git a/dmriprepViewer/src/App.vue b/dmriprepViewer/src/App.vue deleted file mode 100644 index 1d0185f2..00000000 --- a/dmriprepViewer/src/App.vue +++ /dev/null @@ -1,30 +0,0 @@ - - - - - diff --git a/dmriprepViewer/src/assets/logo.svg b/dmriprepViewer/src/assets/logo.svg deleted file mode 100644 index 16528882..00000000 --- a/dmriprepViewer/src/assets/logo.svg +++ /dev/null @@ -1 +0,0 @@ -alien science \ No newline at end of file diff --git a/dmriprepViewer/src/components/BrainSprite.vue b/dmriprepViewer/src/components/BrainSprite.vue deleted file mode 100644 index 15384a24..00000000 --- a/dmriprepViewer/src/components/BrainSprite.vue +++ /dev/null @@ -1,75 +0,0 @@ - - - - - diff --git a/dmriprepViewer/src/components/Bucket.vue b/dmriprepViewer/src/components/Bucket.vue deleted file mode 100644 index 4d3e65b5..00000000 --- a/dmriprepViewer/src/components/Bucket.vue +++ /dev/null @@ -1,195 +0,0 @@ - - - - - diff --git a/dmriprepViewer/src/components/GroupStats.vue b/dmriprepViewer/src/components/GroupStats.vue deleted file mode 100644 index d17716d9..00000000 --- a/dmriprepViewer/src/components/GroupStats.vue +++ /dev/null @@ -1,96 +0,0 @@ - - - - - diff --git a/dmriprepViewer/src/components/HelloWorld.vue b/dmriprepViewer/src/components/HelloWorld.vue deleted file mode 100644 index e225ba2d..00000000 --- a/dmriprepViewer/src/components/HelloWorld.vue +++ /dev/null @@ -1,114 +0,0 @@ - - - - - - diff --git a/dmriprepViewer/src/components/LineChart.vue b/dmriprepViewer/src/components/LineChart.vue deleted file mode 100644 index e335b6bd..00000000 --- a/dmriprepViewer/src/components/LineChart.vue +++ /dev/null @@ -1,191 +0,0 @@ - - - - - diff --git a/dmriprepViewer/src/components/Report.vue b/dmriprepViewer/src/components/Report.vue deleted file mode 100644 index a1c333d3..00000000 --- a/dmriprepViewer/src/components/Report.vue +++ /dev/null @@ -1,162 +0,0 @@ - - - - - diff --git a/dmriprepViewer/src/components/Sprite4D.vue b/dmriprepViewer/src/components/Sprite4D.vue deleted file mode 100644 index 2180661d..00000000 --- a/dmriprepViewer/src/components/Sprite4D.vue +++ /dev/null @@ -1,63 +0,0 @@ - - - - - diff --git a/dmriprepViewer/src/components/brainsprite.js b/dmriprepViewer/src/components/brainsprite.js deleted file mode 100644 index e0281843..00000000 --- a/dmriprepViewer/src/components/brainsprite.js +++ /dev/null @@ -1,587 +0,0 @@ -/* eslint-disable */ -function brainsprite(params) { - - // Function to add nearest neighbour interpolation to a canvas - function setNearestNeighbour(context,flag){ - context.imageSmoothingEnabled = flag; - return context; - } - - // Initialize the brain object - var brain = {}; - - // Initialize the brain object - var defaultParams = { - // Flag for "NaN" image values, i.e. unable to read values - nanValue: false, - - // Smoothing of the main slices - smooth: false, - - // Draw (or not) the current value - flagValue: false, - - // Background color for the canvas - colorBackground: '#000000', - - // Flag to turn on/off slice numbers - flagCoordinates: false, - - // Origins and voxel size - origin: { X: 0, Y: 0, Z: 0 }, - voxelSize: 1, - - // Affine transformation - affine: false, - - // Colorbar size parameters - heightColorBar: 0.04, - - // Font parameters - sizeFont: 0.075, - colorFont: '#FFFFFF', - - // Number of decimals displayed - nbDecimals: 3, - - // Flag to turn on/off the crosshair - crosshair: false, - - // Color of the crosshair - colorCrosshair: "#0000FF", - - // Size crosshair - percentage of the field of view - sizeCrosshair: 0.9, - - // Optional title for the viewer - title: false, - - // Coordinates for the initial cut - numSlice: false, - } - - var brain = Object.assign({}, defaultParams, params); - - // Build affine, if not specified - if (typeof brain.affine === 'boolean' && brain.affine === false) { - brain.affine = [[brain.voxelSize , 0 , 0 , -brain.origin.X], - [0 , brain.voxelSize , 0 , -brain.origin.Y], - [0 , 0 , brain.voxelSize , -brain.origin.Z], - [0 , 0 , 0 , 1]] - - } - - // The main canvas, where the three slices are drawn - brain.canvas = document.getElementById(params.canvas); - brain.context = brain.canvas.getContext('2d'); - brain.context = setNearestNeighbour(brain.context,brain.smooth); - - // An in-memory canvas to draw intermediate reconstruction - // of the coronal slice, at native resolution - brain.canvasY = document.createElement('canvas'); - brain.contextY = brain.canvasY.getContext('2d'); - - // An in-memory canvas to draw intermediate reconstruction - // of the axial slice, at native resolution - brain.canvasZ = document.createElement('canvas'); - brain.contextZ = brain.canvasZ.getContext('2d'); - - // An in-memory canvas to read the value of pixels - brain.canvasRead = document.createElement('canvas'); - brain.contextRead = brain.canvasRead.getContext('2d'); - brain.canvasRead.width = 1; - brain.canvasRead.height = 1; - - // Onclick events - brain.onclick = typeof params.onclick !== 'undefined' ? params.onclick : ""; - - // Font parameters - if (brain.flagCoordinates) { - brain.spaceFont = 0.1; - } else { - brain.spaceFont = 0; - }; - - //******************// - // The sprite image // - //******************// - brain.sprite = document.getElementById(params.sprite); - - // Number of columns and rows in the sprite - brain.nbCol = brain.sprite.width/params.nbSlice.Y; - brain.nbRow = brain.sprite.height/params.nbSlice.Z; - // Number of slices - - brain.nbSlice = { - X: typeof params.nbSlice.X !== 'undefined' ? params.nbSlice.X : brain.nbCol*brain.nbRow, - Y: params.nbSlice.Y, - Z: params.nbSlice.Z - }; - - // width and height for the canvas - brain.widthCanvas = {'X':0 , 'Y':0 , 'Z':0 }; - brain.heightCanvas = {'X':0 , 'Y':0 , 'Z':0 , 'max':0}; - - // the slice numbers - if (brain.numSlice == false) { - brain.numSlice = { X: Math.floor(brain.nbSlice.X/2), - Y: Math.floor(brain.nbSlice.Y/2), - Z: Math.floor(brain.nbSlice.Z/2)} - }; - - // Coordinates for current slices - these will get updated when drawing the slices - brain.coordinatesSlice = {'X': 0, 'Y': 0, 'Z': 0 }; - - //*************// - // The planes // - //*************// - brain.planes = {}; - // A master sagital canvas for the merge of background and overlay - brain.planes.canvasMaster = document.createElement('canvas'); - brain.planes.contextMaster = brain.planes.canvasMaster.getContext('2d'); - - //*************// - // The overlay // - //*************// - params.overlay = typeof params.overlay !== 'undefined' ? params.overlay : false; - if (params.overlay) { - // Initialize the overlay - brain.overlay = {}; - // Get the sprite - brain.overlay.sprite = document.getElementById(params.overlay.sprite); - // Ratio between the resolution of the foreground and background - // Number of columns and rows in the overlay - brain.overlay.nbCol = brain.overlay.sprite.width/params.overlay.nbSlice.Y; - brain.overlay.nbRow = brain.overlay.sprite.height/params.overlay.nbSlice.Z; - // Number of slices in the overlay - brain.overlay.nbSlice = { - X: typeof params.overlay.nbSlice.X !== 'undefined' ? params.overlay.nbSlice.X : brain.overlay.nbCol*brain.overlay.nbRow, - Y: params.overlay.nbSlice.Y, - Z: params.overlay.nbSlice.Z - }; - // opacity - brain.overlay.opacity = typeof params.overlay.opacity !== 'undefined' ? params.overlay.opacity : 1; - }; - - //**************// - // The colormap // - //**************// - params.colorMap = typeof params.colorMap !== 'undefined' ? params.colorMap: false; - if (params.colorMap) { - // Initialize the color map - brain.colorMap = {}; - // Get the sprite - brain.colorMap.img = document.getElementById(params.colorMap.img); - // Set min / max - brain.colorMap.min = params.colorMap.min; - brain.colorMap.max = params.colorMap.max; - // Set visibility - params.colorMap.hide = typeof params.colorMap.hide !== 'undefined' ? params.colorMap.hide: false; - // An in-memory canvas to store the colormap - brain.colorMap.canvas = document.createElement('canvas'); - brain.colorMap.context = brain.colorMap.canvas.getContext('2d'); - brain.colorMap.canvas.width = brain.colorMap.img.width; - brain.colorMap.canvas.height = brain.colorMap.img.height; - - // Copy the color map in an in-memory canvas - brain.colorMap.context.drawImage(brain.colorMap.img, - 0,0,brain.colorMap.img.width, brain.colorMap.img.height, - 0,0,brain.colorMap.img.width, brain.colorMap.img.height); - }; - - //*******************************************// - // Extract the value associated with a voxel // - //*******************************************// - brain.getValue = function(rgb,colorMap) { - if (!colorMap) { - return NaN; - } - var cv, dist, nbColor, ind, val, voxelValue; - nbColor = colorMap.canvas.width; - ind = NaN; - val = Infinity; - for (let xx=0; xx', -}); diff --git a/dmriprepViewer/src/router/index.js b/dmriprepViewer/src/router/index.js deleted file mode 100644 index 5ddffbe7..00000000 --- a/dmriprepViewer/src/router/index.js +++ /dev/null @@ -1,27 +0,0 @@ -import Vue from 'vue'; -import Router from 'vue-router'; -import HelloWorld from '@/components/HelloWorld'; -import Report from '@/components/Report'; -import Bucket from '@/components/Bucket'; - -Vue.use(Router); - -export default new Router({ - routes: [ - { - path: '/', - name: 'HelloWorld', - component: HelloWorld, - }, - { - path: '/report', - name: 'Report', - component: Report, - }, - { - path: '/bucket/:bucket', - name: 'Bucket', - component: Bucket, - }, - ], -}); diff --git a/dmriprepViewer/static/.gitkeep b/dmriprepViewer/static/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/dmriprepViewer/test/e2e/custom-assertions/elementCount.js b/dmriprepViewer/test/e2e/custom-assertions/elementCount.js deleted file mode 100644 index 818e6020..00000000 --- a/dmriprepViewer/test/e2e/custom-assertions/elementCount.js +++ /dev/null @@ -1,27 +0,0 @@ -// A custom Nightwatch assertion. -// The assertion name is the filename. -// Example usage: -// -// browser.assert.elementCount(selector, count) -// -// For more information on custom assertions see: -// http://nightwatchjs.org/guide#writing-custom-assertions - -exports.assertion = function (selector, count) { - this.message = 'Testing if element <' + selector + '> has count: ' + count - this.expected = count - this.pass = function (val) { - return val === this.expected - } - this.value = function (res) { - return res.value - } - this.command = function (cb) { - var self = this - return this.api.execute(function (selector) { - return document.querySelectorAll(selector).length - }, [selector], function (res) { - cb.call(self, res) - }) - } -} diff --git a/dmriprepViewer/test/e2e/nightwatch.conf.js b/dmriprepViewer/test/e2e/nightwatch.conf.js deleted file mode 100644 index f019c0ac..00000000 --- a/dmriprepViewer/test/e2e/nightwatch.conf.js +++ /dev/null @@ -1,46 +0,0 @@ -require('babel-register') -var config = require('../../config') - -// http://nightwatchjs.org/gettingstarted#settings-file -module.exports = { - src_folders: ['test/e2e/specs'], - output_folder: 'test/e2e/reports', - custom_assertions_path: ['test/e2e/custom-assertions'], - - selenium: { - start_process: true, - server_path: require('selenium-server').path, - host: '127.0.0.1', - port: 4444, - cli_args: { - 'webdriver.chrome.driver': require('chromedriver').path - } - }, - - test_settings: { - default: { - selenium_port: 4444, - selenium_host: 'localhost', - silent: true, - globals: { - devServerURL: 'http://localhost:' + (process.env.PORT || config.dev.port) - } - }, - - chrome: { - desiredCapabilities: { - browserName: 'chrome', - javascriptEnabled: true, - acceptSslCerts: true - } - }, - - firefox: { - desiredCapabilities: { - browserName: 'firefox', - javascriptEnabled: true, - acceptSslCerts: true - } - } - } -} diff --git a/dmriprepViewer/test/e2e/runner.js b/dmriprepViewer/test/e2e/runner.js deleted file mode 100644 index 27220329..00000000 --- a/dmriprepViewer/test/e2e/runner.js +++ /dev/null @@ -1,48 +0,0 @@ -// 1. start the dev server using production config -process.env.NODE_ENV = 'testing' - -const webpack = require('webpack') -const DevServer = require('webpack-dev-server') - -const webpackConfig = require('../../build/webpack.prod.conf') -const devConfigPromise = require('../../build/webpack.dev.conf') - -let server - -devConfigPromise.then(devConfig => { - const devServerOptions = devConfig.devServer - const compiler = webpack(webpackConfig) - server = new DevServer(compiler, devServerOptions) - const port = devServerOptions.port - const host = devServerOptions.host - return server.listen(port, host) -}) -.then(() => { - // 2. run the nightwatch test suite against it - // to run in additional browsers: - // 1. add an entry in test/e2e/nightwatch.conf.js under "test_settings" - // 2. add it to the --env flag below - // or override the environment flag, for example: `npm run e2e -- --env chrome,firefox` - // For more information on Nightwatch's config file, see - // http://nightwatchjs.org/guide#settings-file - let opts = process.argv.slice(2) - if (opts.indexOf('--config') === -1) { - opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js']) - } - if (opts.indexOf('--env') === -1) { - opts = opts.concat(['--env', 'chrome']) - } - - const spawn = require('cross-spawn') - const runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' }) - - runner.on('exit', function (code) { - server.close() - process.exit(code) - }) - - runner.on('error', function (err) { - server.close() - throw err - }) -}) diff --git a/dmriprepViewer/test/e2e/specs/test.js b/dmriprepViewer/test/e2e/specs/test.js deleted file mode 100644 index ee3514ee..00000000 --- a/dmriprepViewer/test/e2e/specs/test.js +++ /dev/null @@ -1,19 +0,0 @@ -// For authoring Nightwatch tests, see -// http://nightwatchjs.org/guide#usage - -module.exports = { - 'default e2e tests': function test(browser) { - // automatically uses dev Server port from /config.index.js - // default: http://localhost:8080 - // see nightwatch.conf.js - const devServer = browser.globals.devServerURL; - - browser - .url(devServer) - .waitForElementVisible('#app', 5000) - .assert.elementPresent('.hello') - .assert.containsText('h1', 'Welcome to Your Vue.js App') - .assert.elementCount('img', 1) - .end(); - }, -}; diff --git a/dmriprepViewer/test/unit/.eslintrc b/dmriprepViewer/test/unit/.eslintrc deleted file mode 100644 index 4d5d98f3..00000000 --- a/dmriprepViewer/test/unit/.eslintrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "env": { - "jest": true - }, - "globals": { - } -} diff --git a/dmriprepViewer/test/unit/jest.conf.js b/dmriprepViewer/test/unit/jest.conf.js deleted file mode 100644 index 8a1977ee..00000000 --- a/dmriprepViewer/test/unit/jest.conf.js +++ /dev/null @@ -1,30 +0,0 @@ -const path = require('path'); - -module.exports = { - rootDir: path.resolve(__dirname, '../../'), - moduleFileExtensions: [ - 'js', - 'json', - 'vue', - ], - moduleNameMapper: { - '^@/(.*)$': '/src/$1', - }, - transform: { - '^.+\\.js$': '/node_modules/babel-jest', - '.*\\.(vue)$': '/node_modules/vue-jest', - }, - testPathIgnorePatterns: [ - '/test/e2e', - ], - snapshotSerializers: ['/node_modules/jest-serializer-vue'], - setupFiles: ['/test/unit/setup'], - mapCoverage: true, - coverageDirectory: '/test/unit/coverage', - collectCoverageFrom: [ - 'src/**/*.{js,vue}', - '!src/main.js', - '!src/router/index.js', - '!**/node_modules/**', - ], -}; diff --git a/dmriprepViewer/test/unit/setup.js b/dmriprepViewer/test/unit/setup.js deleted file mode 100644 index a9bec302..00000000 --- a/dmriprepViewer/test/unit/setup.js +++ /dev/null @@ -1,3 +0,0 @@ -import Vue from 'vue'; - -Vue.config.productionTip = false; diff --git a/dmriprepViewer/test/unit/specs/HelloWorld.spec.js b/dmriprepViewer/test/unit/specs/HelloWorld.spec.js deleted file mode 100644 index 3348a98f..00000000 --- a/dmriprepViewer/test/unit/specs/HelloWorld.spec.js +++ /dev/null @@ -1,11 +0,0 @@ -import Vue from 'vue'; -import HelloWorld from '@/components/HelloWorld'; - -describe('HelloWorld.vue', () => { - it('should render correct contents', () => { - const Constructor = Vue.extend(HelloWorld); - const vm = new Constructor().$mount(); - expect(vm.$el.querySelector('.hello h1').textContent) - .toEqual('Welcome to Your Vue.js App'); - }); -}); diff --git a/docker/Dockerfile b/docker/Dockerfile deleted file mode 100644 index 06515d67..00000000 --- a/docker/Dockerfile +++ /dev/null @@ -1,174 +0,0 @@ -# Generated by Neurodocker version 0.4.1 -# Timestamp: 2018-09-04 17:44:34 UTC -# -# Thank you for using Neurodocker. If you discover any issues -# or ways to improve this software, please submit an issue or -# pull request on our GitHub repository: -# -# https://github.com/kaczmarj/neurodocker - -FROM debian:stretch - -ARG DEBIAN_FRONTEND="noninteractive" - -ENV LANG="en_US.UTF-8" \ - LC_ALL="en_US.UTF-8" \ - ND_ENTRYPOINT="/neurodocker/startup.sh" -RUN export ND_ENTRYPOINT="/neurodocker/startup.sh" \ - && apt-get update -qq \ - && apt-get install -y -q --no-install-recommends \ - apt-utils \ - bzip2 \ - ca-certificates \ - curl \ - locales \ - unzip \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \ - && sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen \ - && dpkg-reconfigure --frontend=noninteractive locales \ - && update-locale LANG="en_US.UTF-8" \ - && chmod 777 /opt && chmod a+s /opt \ - && mkdir -p /neurodocker \ - && if [ ! -f "$ND_ENTRYPOINT" ]; then \ - echo '#!/usr/bin/env bash' >> "$ND_ENTRYPOINT" \ - && echo 'set -e' >> "$ND_ENTRYPOINT" \ - && echo 'if [ -n "$1" ]; then "$@"; else /usr/bin/env bash; fi' >> "$ND_ENTRYPOINT"; \ - fi \ - && chmod -R 777 /neurodocker && chmod a+s /neurodocker - -ENTRYPOINT ["/neurodocker/startup.sh"] - -ENV FSLDIR="/opt/fsl-5.0.11" \ - PATH="/opt/fsl-5.0.11/bin:$PATH" -RUN apt-get update -qq \ - && apt-get install -y -q --no-install-recommends \ - bc \ - dc \ - file \ - libfontconfig1 \ - libfreetype6 \ - libgl1-mesa-dev \ - libglu1-mesa-dev \ - libgomp1 \ - libice6 \ - libmng1 \ - libxcursor1 \ - libxft2 \ - libxinerama1 \ - libxrandr2 \ - libxrender1 \ - libxt6 \ - wget \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \ - && echo "Downloading FSL ..." \ - && mkdir -p /opt/fsl-5.0.11 \ - && curl -fsSL --retry 5 https://fsl.fmrib.ox.ac.uk/fsldownloads/fsl-5.0.11-centos6_64.tar.gz \ - | tar -xz -C /opt/fsl-5.0.11 --strip-components 1 \ - && sed -i '$iecho Some packages in this Docker container are non-free' $ND_ENTRYPOINT \ - && sed -i '$iecho If you are considering commercial use of this container, please consult the relevant license:' $ND_ENTRYPOINT \ - && sed -i '$iecho https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/Licence' $ND_ENTRYPOINT \ - && sed -i '$isource $FSLDIR/etc/fslconf/fsl.sh' $ND_ENTRYPOINT \ - && echo "Installing FSL conda environment ..." \ - && bash /opt/fsl-5.0.11/etc/fslconf/fslpython_install.sh -f /opt/fsl-5.0.11 - -ENV CONDA_DIR="/opt/miniconda-latest" \ - PATH="/opt/miniconda-latest/bin:$PATH" -RUN export PATH="/opt/miniconda-latest/bin:$PATH" \ - && echo "Downloading Miniconda installer ..." \ - && conda_installer="/tmp/miniconda.sh" \ - && curl -fsSL --retry 5 -o "$conda_installer" https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh \ - && bash "$conda_installer" -b -p /opt/miniconda-latest \ - && rm -f "$conda_installer" \ - && conda update -yq -nbase conda \ - && conda config --system --prepend channels conda-forge \ - && conda config --system --set auto_update_conda false \ - && conda config --system --set show_channel_urls true \ - && sync && conda clean -tipsy && sync -# && conda create -y -q --name dmriprep - -# RUN conda install -y -q --name dmriprep \ -# -c \ -# conda-forge \ -# python=3.6 \ -# nipype \ -# dipy \ -# boto3 \ -# && sync && conda clean -tipsy && sync - - -ENV FREESURFER_HOME="/opt/freesurfer-6.0.0" \ - PATH="/opt/freesurfer-6.0.0/bin:$PATH" - -RUN apt-get update -qq \ - && apt-get install -y -q --no-install-recommends \ - bc \ - libgomp1 \ - libxmu6 \ - libxt6 \ - perl \ - tcsh \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \ - && echo "Downloading FreeSurfer ..." \ - && mkdir -p /opt/freesurfer-6.0.0 \ - && curl -fsSL --retry 5 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/freesurfer-6.0.0 --strip-components 1 \ - --exclude='freesurfer/average/mult-comp-cor' \ - --exclude='freesurfer/lib/cuda' \ - --exclude='freesurfer/lib/qt' \ - --exclude='freesurfer/subjects/V1_average' \ - --exclude='freesurfer/subjects/bert' \ - --exclude='freesurfer/subjects/cvs_avg35' \ - --exclude='freesurfer/subjects/cvs_avg35_inMNI152' \ - --exclude='freesurfer/subjects/fsaverage3' \ - --exclude='freesurfer/subjects/fsaverage4' \ - --exclude='freesurfer/subjects/fsaverage5' \ - --exclude='freesurfer/subjects/fsaverage6' \ - --exclude='freesurfer/subjects/fsaverage_sym' \ - --exclude='freesurfer/trctrain' \ - && sed -i '$isource "/opt/freesurfer-6.0.0/SetUpFreeSurfer.sh"' "$ND_ENTRYPOINT" - -COPY ./license.txt /opt/freesurfer-6.0.0/license.txt - -#&& sync && conda clean -tipsy && sync - -RUN apt-get update && apt-get install -y git gcc libopenblas-base -ADD environment.yml environment.yml -RUN conda env create -f environment.yml - -ENV LD_LIBRARY_PATH=/usr/lib/openblas-base/ - -RUN sed -i '$isource activate dmriprep' $ND_ENTRYPOINT - - -RUN echo '{ \ - \n "pkg_manager": "apt", \ - \n "instructions": [ \ - \n [ \ - \n "base", \ - \n "debian:stretch" \ - \n ], \ - \n [ \ - \n "fsl", \ - \n { \ - \n "version": "5.0.11" \ - \n } \ - \n ], \ - \n [ \ - \n "miniconda", \ - \n { \ - \n "conda_install": [ \ - \n "-c", \ - \n "conda-forge", \ - \n "python=3.6", \ - \n "nipype", \ - \n "dipy" \ - \n ], \ - \n "create_env": "dmriprep", \ - \n "use_env": "dmriprep" \ - \n } \ - \n ] \ - \n ] \ - \n}' > /neurodocker/neurodocker_specs.json diff --git a/docker/environment.yml b/docker/environment.yml deleted file mode 100644 index 30b45f5f..00000000 --- a/docker/environment.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: dmriprep -channels: - - conda-forge -dependencies: - - python=3.6 - - boto3 - - dask - - dipy=0.15 - - graphviz - - ipython - - pandas - - tqdm - - pip: - - "--editable=git+https://github.com/akeshavan/nipype@87e53615867c9850c359002909c71825343b2e35#egg=nipype" - - "--editable=git+https://git.fmrib.ox.ac.uk/matteob/eddy_qc_release@57bb11da6a634c4195593fbc439ba9f8998157b0#egg=eddy_qc" - - bids - - duecredit diff --git a/docker/license.txt b/docker/license.txt deleted file mode 100644 index 2f4623ee..00000000 --- a/docker/license.txt +++ /dev/null @@ -1,4 +0,0 @@ -keshavan@uw.edu -37716 - *C9LImOstH7yw - FSrY9yK/yWp8E diff --git a/kubernetes/create_kube_job.py b/kubernetes/create_kube_job.py deleted file mode 100755 index a19c3fa7..00000000 --- a/kubernetes/create_kube_job.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python -import sys -from boto3 import Session - -if __name__ == "__main__": - if len(sys.argv) > 1: - subject = sys.argv[1] - - session = Session() - credentials = session.get_credentials() - # Credentials are refreshable, so accessing your access key / secret key - # separately can lead to a race condition. Use this to get an actual matched - # set. - current_credentials = credentials.get_frozen_credentials() - - access_key = current_credentials.access_key - secret_key = current_credentials.secret_key - - with open("run_dmriprep.yml.tmpl", 'r') as template: - with open("jobs/job_{}.yml".format(subject), 'w') as f: - all_text = "\n".join(template.readlines()) - all_text = all_text.replace("{{subject_lower}}", subject.lower()) - all_text = all_text.replace("{{subject}}", subject) - all_text = all_text.replace("{{access_key}}", access_key) - all_text = all_text.replace("{{secret_key}}", secret_key) - f.write(all_text) - diff --git a/kubernetes/delete_cluster.sh b/kubernetes/delete_cluster.sh deleted file mode 100755 index 5f2d07f1..00000000 --- a/kubernetes/delete_cluster.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -gcloud container clusters delete dmriprep --zone=us-west1-a diff --git a/kubernetes/delete_job.sh b/kubernetes/delete_job.sh deleted file mode 100755 index 4c43b70e..00000000 --- a/kubernetes/delete_job.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -kubectl delete -f run_dmriprep.yml diff --git a/kubernetes/docker/build_tag_push.sh b/kubernetes/docker/build_tag_push.sh deleted file mode 100755 index afaec546..00000000 --- a/kubernetes/docker/build_tag_push.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -docker build -t dmriprep:kube4 -f dockerfile-dmriprep-kube . -docker tag dmriprep:kube4 gcr.io/dmriprep/dmriprep:kube4 -docker push gcr.io/dmriprep/dmriprep:kube4 diff --git a/kubernetes/docker/dmriprep_all.sh b/kubernetes/docker/dmriprep_all.sh deleted file mode 100755 index 3e0bfafa..00000000 --- a/kubernetes/docker/dmriprep_all.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -mkdir inputs -mkdir outputs -dmriprep-data --subject $1 $PWD/inputs/ -dmriprep $PWD/inputs $PWD/outputs -dmriprep-upload --access_key $2 --secret_key $3 $PWD/outputs preafq-hbn diff --git a/kubernetes/docker/dockerfile-dmriprep-kube b/kubernetes/docker/dockerfile-dmriprep-kube deleted file mode 100644 index 77bd5d0c..00000000 --- a/kubernetes/docker/dockerfile-dmriprep-kube +++ /dev/null @@ -1,3 +0,0 @@ -FROM dmriprep:prod -COPY dmriprep_all.sh /dmriprep_all.sh -CMD ["/neurodocker/startup.sh", "dmriprep_all.sh"] diff --git a/kubernetes/run_dmriprep.yml.tmpl b/kubernetes/run_dmriprep.yml.tmpl deleted file mode 100644 index 2bd01876..00000000 --- a/kubernetes/run_dmriprep.yml.tmpl +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - name: dmriprep-{{subject_lower}} -spec: - template: - spec: - containers: - - name: dmriprep-subject - image: gcr.io/dmriprep/dmriprep:kube4 - command: ["/neurodocker/startup.sh", "./dmriprep_all.sh", "{{subject}}", "{{access_key}}", "{{secret_key}}"] - resources: - requests: - memory: "12G" - cpu: "2" - restartPolicy: Never - backoffLimit: 1 diff --git a/kubernetes/run_job.sh b/kubernetes/run_job.sh deleted file mode 100755 index d87e4302..00000000 --- a/kubernetes/run_job.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -kubectl apply -f run_dmriprep.yml diff --git a/kubernetes/setup_gcp_kubernetes_dmriprep.sh b/kubernetes/setup_gcp_kubernetes_dmriprep.sh deleted file mode 100755 index d80c89fa..00000000 --- a/kubernetes/setup_gcp_kubernetes_dmriprep.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -# login -gcloud auth login anishakeshavan@gmail.com - -# set the project to dmriprep -gcloud config set project dmriprep - -# some variables -ZONE=us-west1-a -MAX_NODES=4 -CLUSTERNAME=dmriprep - -# set the default compute zone -gcloud config set compute/zone $ZONE - -# start the cluster! -gcloud beta container clusters create $CLUSTERNAME --machine-type n1-highmem-4 --enable-autoscaling --max-nodes=$MAX_NODES --num-nodes 1 --cluster-version latest --node-labels dmriprep/node-purpose=core diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..b4c8b1ad --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,22 @@ +[tool.black] +line-length = 79 +target-version = ['py36', 'py37', 'py38'] +include = '\.pyi?$' +exclude = ''' +/( + \.eggs + | \.git + | \.hg + | \.mypy_cache + | \.tox + | \.venv + | _build + | buck-out + | build + | dist + # The following are specific to Black, you probably don't want those. + | blib2to3 + | tests/data + | profiling +)/ +''' diff --git a/tox.ini b/tox.ini index b4e7bf75..2bb783a1 100644 --- a/tox.ini +++ b/tox.ini @@ -16,13 +16,5 @@ commands = flake8 dmriprep [testenv] setenv = PYTHONPATH = {toxinidir} -deps = - -r{toxinidir}/requirements_dev.txt -; If you want to make tox run the tests with the same versions, create a -; requirements.txt with the pinned versions and uncomment the following line: -; -r{toxinidir}/requirements.txt -commands = - pip install -U pip - py.test --basetemp={envtmpdir} - +commands = python setup.py test From 998ee5eb9f2b828e6df5a33803118c04fb78be54 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Mon, 8 Jul 2019 15:10:41 -0400 Subject: [PATCH 040/156] update precommit framework --- .flake8 | 7 ++++--- .pre-commit-config.yaml | 4 ++++ pyproject.toml | 4 ---- requirements_dev.txt | 1 + 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.flake8 b/.flake8 index 9f731ead..d9ad0b40 100644 --- a/.flake8 +++ b/.flake8 @@ -1,4 +1,5 @@ [flake8] -max-line-length = 80 -select = C,E,F,W,B,B950 -ignore = E501,N802,N806,W503,E203 +ignore = E203, E266, E501, W503, F403, F401 +max-line-length = 79 +max-complexity = 18 +select = B,C,E,F,W,T4,B9 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 799985da..17bddd8a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,3 +4,7 @@ repos: hooks: - id: black language_version: python3.6 +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.2.3 + hooks: + - id: flake8 diff --git a/pyproject.toml b/pyproject.toml index b4c8b1ad..3dbb3802 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,9 +14,5 @@ exclude = ''' | buck-out | build | dist - # The following are specific to Black, you probably don't want those. - | blib2to3 - | tests/data - | profiling )/ ''' diff --git a/requirements_dev.txt b/requirements_dev.txt index 17e71717..e043d23c 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -9,6 +9,7 @@ Sphinx==2.1.1 twine==1.13.0 black==19.3b0 versioneer==0.18 +pre-commit==1.17.0 pytest==4.6.3 pytest-runner==5.1 From 25c493947fe7d443305fac9d9438c740802b50e9 Mon Sep 17 00:00:00 2001 From: Salim Mansour Date: Mon, 8 Jul 2019 15:27:58 -0400 Subject: [PATCH 041/156] Added optional total_readout_time parameter --- dmriprep/cli.py | 11 ++++++++++- dmriprep/workflows/base.py | 15 +++++++++++---- dmriprep/workflows/dwi/base.py | 17 +++++++++++++---- 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/dmriprep/cli.py b/dmriprep/cli.py index 75f6b3fa..5d065b50 100644 --- a/dmriprep/cli.py +++ b/dmriprep/cli.py @@ -65,6 +65,14 @@ "be used.", default=0.3, ) +@click.option( + "--total-readout", + help="Manual option for what value will be used in acquired params step. " + "If this parameter is not provided the value will be taken from the " + "TotalReadoutTime field in the dwi json. ", + default=None, + type=(float), +) @click.argument("bids_dir") @click.argument("output_dir") @click.argument( @@ -78,6 +86,7 @@ def main( bet_dwi=0.3, bet_mag=0.3, slice_outlier_threshold=0.02, + total_readout=None, analysis_level="participant", ): """ @@ -106,7 +115,7 @@ def main( work_dir = os.path.join(output_dir, "scratch") wf = init_dmriprep_wf( - layout, subject_list, work_dir, output_dir, bet_dwi, bet_mag + layout, subject_list, work_dir, output_dir, bet_dwi, bet_mag, total_readout ) wf.write_graph(graph2use="colored") wf.config["execution"]["remove_unnecessary_outputs"] = False diff --git a/dmriprep/workflows/base.py b/dmriprep/workflows/base.py index f787b046..42d0a1e1 100644 --- a/dmriprep/workflows/base.py +++ b/dmriprep/workflows/base.py @@ -7,7 +7,10 @@ from .dwi import init_dwi_preproc_wf, init_output_wf -def init_dmriprep_wf(layout, subject_list, work_dir, output_dir, bet_dwi, bet_mag): +def init_dmriprep_wf( + layout, subject_list, work_dir, output_dir, + bet_dwi, bet_mag, total_readout +): dmriprep_wf = pe.Workflow(name="dmriprep_wf") dmriprep_wf.base_dir = work_dir @@ -20,7 +23,8 @@ def init_dmriprep_wf(layout, subject_list, work_dir, output_dir, bet_dwi, bet_ma work_dir=work_dir, output_dir=output_dir, bet_dwi=bet_dwi, - bet_mag=bet_mag + bet_mag=bet_mag, + total_readout=total_readout, ) single_subject_wf.config["execution"]["crashdump_dir"] = os.path.join( @@ -35,7 +39,10 @@ def init_dmriprep_wf(layout, subject_list, work_dir, output_dir, bet_dwi, bet_ma return dmriprep_wf -def init_single_subject_wf(layout, subject_id, name, work_dir, output_dir, bet_dwi, bet_mag): +def init_single_subject_wf( + layout, subject_id, name, work_dir, output_dir, + bet_dwi, bet_mag, total_readout +): dwi_files = layout.get( subject=subject_id, @@ -59,7 +66,7 @@ def init_single_subject_wf(layout, subject_id, name, work_dir, output_dir, bet_d metadata = layout.get_metadata(dwi_file) dwi_preproc_wf = init_dwi_preproc_wf( subject_id=subject_id, dwi_file=dwi_file, metadata=metadata, layout=layout, - bet_dwi_frac=bet_dwi, bet_mag_frac=bet_mag + bet_dwi_frac=bet_dwi, bet_mag_frac=bet_mag, total_readout=total_readout ) datasink_wf = init_output_wf( subject_id=subject_id, session_id=session_id, output_folder=output_dir diff --git a/dmriprep/workflows/dwi/base.py b/dmriprep/workflows/dwi/base.py index 63f66342..0bfc9fca 100755 --- a/dmriprep/workflows/dwi/base.py +++ b/dmriprep/workflows/dwi/base.py @@ -1,7 +1,10 @@ #!/usr/bin/env python -def init_dwi_preproc_wf(subject_id, dwi_file, metadata, layout, bet_dwi_frac, bet_mag_frac): +def init_dwi_preproc_wf( + subject_id, dwi_file, metadata, layout, + bet_dwi_frac, bet_mag_frac, total_readout +): from nipype.pipeline import engine as pe from nipype.interfaces import fsl, utility as niu @@ -72,7 +75,7 @@ def gen_index(in_file): name="gen_index", ) - def gen_acqparams(in_file, metadata): + def gen_acqparams(in_file, metadata, total_readout_time): import os.path as op from nipype.utils.filemanip import fname_presuffix @@ -90,7 +93,11 @@ def gen_acqparams(in_file, metadata): } pe_dir = metadata.get("PhaseEncodingDirection") - total_readout = metadata.get("TotalReadoutTime") + + if total_readout_time: + total_readout = total_readout_time + else: + total_readout = metadata.get("TotalReadoutTime") acq_param_lines = acq_param_dict[pe_dir] % total_readout @@ -101,13 +108,15 @@ def gen_acqparams(in_file, metadata): acqp = pe.Node( niu.Function( - input_names=["in_file", "metadata"], + input_names=["in_file", "metadata", "total_readout_time"], output_names=["out_file"], function=gen_acqparams, ), name="acqp", ) + acqp.inputs.total_readout_time = total_readout + def b0_average(in_dwi, in_bval, b0_thresh=10.0, out_file=None): """ A function that averages the *b0* volumes from a DWI dataset. From 2e96c78270d0c0ccd4393deee0377fa1909a7e63 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Mon, 8 Jul 2019 15:34:47 -0400 Subject: [PATCH 042/156] add upsample spec --- dmriprep/interfaces/{mrtrix.py => mrtrix3.py} | 94 +++++++++++++++- dmriprep/workflows/dwi/base.py | 101 +++++++++++------- 2 files changed, 151 insertions(+), 44 deletions(-) rename dmriprep/interfaces/{mrtrix.py => mrtrix3.py} (66%) diff --git a/dmriprep/interfaces/mrtrix.py b/dmriprep/interfaces/mrtrix3.py similarity index 66% rename from dmriprep/interfaces/mrtrix.py rename to dmriprep/interfaces/mrtrix3.py index 9dc585c1..c1f55613 100644 --- a/dmriprep/interfaces/mrtrix.py +++ b/dmriprep/interfaces/mrtrix3.py @@ -1,6 +1,11 @@ #!/usr/bin/env python -from __future__ import print_function, division, unicode_literals, absolute_import +from __future__ import ( + print_function, + division, + unicode_literals, + absolute_import, +) from nipype.interfaces.base import traits, TraitedSpec, File from nipype.interfaces.mrtrix3.base import MRTrix3BaseInputSpec, MRTrix3Base @@ -8,7 +13,11 @@ class DWIDenoiseInputSpec(MRTrix3BaseInputSpec): in_file = File( - exists=True, argstr="%s", position=-2, mandatory=True, desc="input DWI image" + exists=True, + argstr="%s", + position=-2, + mandatory=True, + desc="input DWI image", ) mask = File(exists=True, argstr="-mask %s", position=1, desc="mask image") extent = traits.Tuple( @@ -70,7 +79,11 @@ class DWIDenoise(MRTrix3Base): class MRDeGibbsInputSpec(MRTrix3BaseInputSpec): in_file = File( - exists=True, argstr="%s", position=-2, mandatory=True, desc="input DWI image" + exists=True, + argstr="%s", + position=-2, + mandatory=True, + desc="input DWI image", ) axes = traits.ListInt( default_value=[0, 1], @@ -150,3 +163,78 @@ class MRDeGibbs(MRTrix3Base): _cmd = "mrdegibbs" input_spec = MRDeGibbsInputSpec output_spec = MRDeGibbsOutputSpec + + +class MRResizeInputSpec(MRTrix3BaseInputSpec): + in_file = File( + exists=True, + argstr="%s", + position=-2, + mandatory=True, + desc="input DWI image", + ) + size = traits.List( + traits.Int(), + argstr="-size %s", + desc="define the new image size for the output image. This should be " + "specified as a comma-separated list.", + ) + voxel_size = traits.List( + traits.Float(), + argstr="-voxel %s", + desc="define the new voxel size for the output image. This can be " + "specified either as a single value to be used for all " + "dimensions, or as a comma-separated list of the size for each " + "voxel dimension.", + ) + scale = traits.Float( + argstr="-scale %s", + desc="scale the image resolution by the supplied factor. This can be " + "specified either as a single value to be used for all " + "dimensions, or as a comma-separated list of scale factors for " + "each dimension.", + ) + interp = traits.Enum( + "nearest", + "linear", + "cubic", + "sinc", + default="cubic", + argstr="-interp %s", + desc="set the interpolation method to use when resizing (choices: " + "nearest, linear, cubic, sinc. Default: cubic).", + ) + + out_file = File( + argstr="%s", + name_template="%s_resized", + name_source=["in_file"], + keep_extension=True, + position=-1, + desc="the output resized DWI image", + ) + + +class MRResizeOutputSpec(TraitedSpec): + out_file = File(desc="the output resized DWI image", exists=True) + + +class MRResize(MRTrix3Base): + """ + Resize an image by defining the new image resolution, voxel size or a scale factor + For more information, see + + Example + ------- + >>> import nipype.interfaces.mrtrix3 as mrt + >>> resize = mrt.MRResize() + >>> resize.inputs.in_file = 'dwi.mif' + >>> denoise.inputs.mask = 'mask.mif' + >>> denoise.cmdline # doctest: +ELLIPSIS + 'dwidenoise -mask mask.mif dwi.mif dwi_denoised.mif' + >>> denoise.run() # doctest: +SKIP + """ + + _cmd = "mrresize" + input_spec = MRResizeInputSpec + output_spec = MRResizeOutputSpec diff --git a/dmriprep/workflows/dwi/base.py b/dmriprep/workflows/dwi/base.py index 63f66342..a6a8fc96 100755 --- a/dmriprep/workflows/dwi/base.py +++ b/dmriprep/workflows/dwi/base.py @@ -1,12 +1,24 @@ #!/usr/bin/env python +import os +import multiprocessing -def init_dwi_preproc_wf(subject_id, dwi_file, metadata, layout, bet_dwi_frac, bet_mag_frac): - from nipype.pipeline import engine as pe - from nipype.interfaces import fsl, utility as niu +import numpy as np +import nibabel as nib +from nipype.pipeline import engine as pe +from nipype.interfaces import fsl, utility as niu +from nipype.utils import NUMPY_MMAP +from nipype.utils.filemanip import fname_presuffix +from dipy.segment.mask import median_otsu +from numba import cuda - from ...interfaces import mrtrix - from ..fieldmap.base import init_sdc_prep_wf +from ...interfaces import mrtrix3 +from ..fieldmap.base import init_sdc_prep_wf + + +def init_dwi_preproc_wf( + subject_id, dwi_file, metadata, layout, bet_dwi_frac, bet_mag_frac +): fmaps = [] fmaps = layout.get_fieldmap(dwi_file, return_list=True) @@ -18,7 +30,7 @@ def init_dwi_preproc_wf(subject_id, dwi_file, metadata, layout, bet_dwi_frac, be ) for fmap in fmaps: - fmap["metadata"] = layout.get_metadata(fmap["suffix"]) + fmap["metadata"] = layout.get_metadata(fmap[fmap["suffix"]]) sdc_wf = init_sdc_prep_wf(fmaps, metadata, layout, bet_mag_frac) @@ -45,21 +57,21 @@ def init_dwi_preproc_wf(subject_id, dwi_file, metadata, layout, bet_dwi_frac, be name="outputnode", ) - denoise = pe.Node(mrtrix.DWIDenoise(), name="denoise") + denoise = pe.Node(mrtrix3.DWIDenoise(), name="denoise") - unring = pe.Node(mrtrix.MRDeGibbs(), name="unring") + unring = pe.Node(mrtrix3.MRDeGibbs(), name="unring") + + resize = pe.Node(mrtrix3.MRResize(), name="resize") def gen_index(in_file): - import os.path as op - import numpy as np - import nibabel as nb - from nipype.utils import NUMPY_MMAP - from nipype.utils.filemanip import fname_presuffix out_file = fname_presuffix( - in_file, suffix="_index.txt", newpath=op.abspath("."), use_ext=False + in_file, + suffix="_index.txt", + newpath=os.path.abspath("."), + use_ext=False, ) - vols = nb.load(in_file, mmap=NUMPY_MMAP).get_data().shape[-1] + vols = nib.load(in_file, mmap=NUMPY_MMAP).get_data().shape[-1] index_lines = np.ones((vols,)) index_lines_reshape = index_lines.reshape(1, index_lines.shape[0]) np.savetxt(out_file, index_lines_reshape, fmt="%i") @@ -67,17 +79,20 @@ def gen_index(in_file): gen_idx = pe.Node( niu.Function( - input_names=["in_file"], output_names=["out_file"], function=gen_index + input_names=["in_file"], + output_names=["out_file"], + function=gen_index, ), name="gen_index", ) def gen_acqparams(in_file, metadata): - import os.path as op - from nipype.utils.filemanip import fname_presuffix out_file = fname_presuffix( - in_file, suffix="_acqparams.txt", newpath=op.abspath("."), use_ext=False + in_file, + suffix="_acqparams.txt", + newpath=os.path.abspath("."), + use_ext=False, ) acq_param_dict = { @@ -116,18 +131,13 @@ def b0_average(in_dwi, in_bval, b0_thresh=10.0, out_file=None): .. warning:: *b0* should be already registered (head motion artifact should be corrected). """ - import numpy as np - import nibabel as nb - import os.path as op - from nipype.utils import NUMPY_MMAP - from nipype.utils.filemanip import fname_presuffix if out_file is None: out_file = fname_presuffix( - in_dwi, suffix="_avg_b0", newpath=op.abspath(".") + in_dwi, suffix="_avg_b0", newpath=os.path.abspath(".") ) - imgs = np.array(nb.four_to_three(nb.load(in_dwi, mmap=NUMPY_MMAP))) + imgs = np.array(nib.four_to_three(nib.load(in_dwi, mmap=NUMPY_MMAP))) bval = np.loadtxt(in_bval) index = np.argwhere(bval <= b0_thresh).flatten().tolist() @@ -138,7 +148,7 @@ def b0_average(in_dwi, in_bval, b0_thresh=10.0, out_file=None): hdr.set_data_shape(b0.shape) hdr.set_xyzt_units("mm") hdr.set_data_dtype(np.float32) - nb.Nifti1Image(b0, imgs[0].affine, hdr).to_filename(out_file) + nib.Nifti1Image(b0, imgs[0].affine, hdr).to_filename(out_file) return out_file avg_b0_0 = pe.Node( @@ -151,7 +161,9 @@ def b0_average(in_dwi, in_bval, b0_thresh=10.0, out_file=None): ) # dilate mask - bet_dwi0 = pe.Node(fsl.BET(frac=bet_dwi_frac, mask=True, robust=True), name="bet_dwi_pre") + bet_dwi0 = pe.Node( + fsl.BET(frac=bet_dwi_frac, mask=True, robust=True), name="bet_dwi_pre" + ) # mrtrix3.MaskFilter @@ -160,19 +172,16 @@ def b0_average(in_dwi, in_bval, b0_thresh=10.0, out_file=None): name="fsl_eddy", ) - import multiprocessing - + # if nthreads not specified, do this ecc.inputs.num_threads = multiprocessing.cpu_count() - from numba import cuda - try: if cuda.gpus: ecc.inputs.use_cuda = True except: ecc.inputs.use_cuda = False - denoise_eddy = pe.Node(mrtrix.DWIDenoise(), name="denoise_eddy") + denoise_eddy = pe.Node(mrtrix3.DWIDenoise(), name="denoise_eddy") eddy_quad = pe.Node(fsl.EddyQuad(verbose=True), name="eddy_quad") @@ -182,10 +191,6 @@ def b0_average(in_dwi, in_bval, b0_thresh=10.0, out_file=None): fslroi = pe.Node(fsl.ExtractROI(t_min=0, t_size=1), name="fslroi") def get_b0_mask_fn(b0_file): - import nibabel as nib - from nipype.utils.filemanip import fname_presuffix - from dipy.segment.mask import median_otsu - import os mask_file = fname_presuffix( b0_file, suffix="_mask", newpath=os.path.abspath(".") @@ -198,7 +203,9 @@ def get_b0_mask_fn(b0_file): b0mask_node = pe.Node( niu.Function( - input_names=["b0_file"], output_names=["mask_file"], function=get_b0_mask_fn + input_names=["b0_file"], + output_names=["mask_file"], + function=get_b0_mask_fn, ), name="getB0Mask", ) @@ -213,9 +220,17 @@ def get_b0_mask_fn(b0_file): (unring, avg_b0_0, [("out_file", "in_dwi")]), (avg_b0_0, bet_dwi0, [("out_file", "in_file")]), (inputnode, gen_idx, [("dwi_file", "in_file")]), - (inputnode, acqp, [("dwi_file", "in_file"), ("metadata", "metadata")]), + ( + inputnode, + acqp, + [("dwi_file", "in_file"), ("metadata", "metadata")], + ), (unring, ecc, [("out_file", "in_file")]), - (inputnode, ecc, [("bval_file", "in_bval"), ("bvec_file", "in_bvec")]), + ( + inputnode, + ecc, + [("bval_file", "in_bval"), ("bvec_file", "in_bvec")], + ), (bet_dwi0, ecc, [("mask_file", "in_mask")]), (gen_idx, ecc, [("out_file", "in_index")]), (acqp, ecc, [("out_file", "in_acqp")]), @@ -241,7 +256,11 @@ def get_b0_mask_fn(b0_file): (bet_dwi0, sdc_wf, [("out_file", "inputnode.b0_stripped")]), (sdc_wf, ecc, [(("outputnode.out_fmap", get_path), "field")]), (sdc_wf, eddy_quad, [("outputnode.out_fmap", "field")]), - (ecc, dtifit, [("out_corrected", "dwi"), ("out_rotated_bvecs", "bvecs")]), + ( + ecc, + dtifit, + [("out_corrected", "dwi"), ("out_rotated_bvecs", "bvecs")], + ), (b0mask_node, dtifit, [("mask_file", "mask")]), (inputnode, dtifit, [("bval_file", "bvals")]), ] From aad6f1bb370ae8ada2b9cfe8a7db194292e64639 Mon Sep 17 00:00:00 2001 From: Salim Mansour Date: Mon, 8 Jul 2019 15:53:57 -0400 Subject: [PATCH 043/156] Removed outdated docker setup step --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index 54452b5b..58974637 100644 --- a/setup.py +++ b/setup.py @@ -51,7 +51,6 @@ "dmriprep-upload=dmriprep.cli:upload", ] }, - scripts=["./dmriprep-docker"], install_requires=requirements, extras_require=extras_require, license="BSD license", From 087676fa23023c48058c4cf0ace1e4e24d42498d Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Mon, 8 Jul 2019 16:17:17 -0400 Subject: [PATCH 044/156] remove subject data download and eddy outlier threshold --- dmriprep/__init__.py | 5 +- dmriprep/cli.py | 119 +--- dmriprep/data.py | 1125 -------------------------------- dmriprep/workflows/dwi/base.py | 1 - 4 files changed, 13 insertions(+), 1237 deletions(-) delete mode 100644 dmriprep/data.py diff --git a/dmriprep/__init__.py b/dmriprep/__init__.py index d69154a1..d7678e1e 100644 --- a/dmriprep/__init__.py +++ b/dmriprep/__init__.py @@ -16,7 +16,6 @@ warnings.filterwarnings("ignore", message="numpy.dtype size changed") warnings.filterwarnings("ignore", message="numpy.ufunc size changed") -from . import data from . import qc module_logger = logging.getLogger(__name__) @@ -46,7 +45,9 @@ handler.setLevel(logging.DEBUG) # create a logging format -formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") +formatter = logging.Formatter( + "%(asctime)s - %(name)s - %(levelname)s - %(message)s" +) handler.setFormatter(formatter) # add the handlers to the logger diff --git a/dmriprep/cli.py b/dmriprep/cli.py index 5d065b50..338dd4bb 100644 --- a/dmriprep/cli.py +++ b/dmriprep/cli.py @@ -9,7 +9,6 @@ import click from . import utils -from .data import get_dataset from .workflows.base import init_dmriprep_wf # Filter warnings that are visible whenever you import another package that @@ -37,16 +36,6 @@ default=5, type=(int), ) -@click.option( - "--slice-outlier-threshold", - help="Number of allowed outlier slices per volume. " - "If this is exceeded the volume is dropped from analysis. " - "If an int is provided, it is treated as number of allowed " - "outlier slices. If a float between 0 and 1 " - "(exclusive) is provided, it is treated the fraction of " - "allowed outlier slices.", - default=0.02, -) @click.option( "--bet-dwi", help="Fractional intensity threshold for BET on the DWI. " @@ -76,7 +65,9 @@ @click.argument("bids_dir") @click.argument("output_dir") @click.argument( - "analysis_level", type=click.Choice(["participant", "group"]), default="participant" + "analysis_level", + type=click.Choice(["participant", "group"]), + default="participant", ) def main( participant_label, @@ -85,7 +76,6 @@ def main( eddy_niter=5, bet_dwi=0.3, bet_mag=0.3, - slice_outlier_threshold=0.02, total_readout=None, analysis_level="participant", ): @@ -115,7 +105,13 @@ def main( work_dir = os.path.join(output_dir, "scratch") wf = init_dmriprep_wf( - layout, subject_list, work_dir, output_dir, bet_dwi, bet_mag, total_readout + layout, + subject_list, + work_dir, + output_dir, + bet_dwi, + bet_mag, + total_readout, ) wf.write_graph(graph2use="colored") wf.config["execution"]["remove_unnecessary_outputs"] = False @@ -125,100 +121,5 @@ def main( return 0 -@click.command() -@click.argument("output_dir") -@click.option( - "--subject", - help="subject id to download (will choose 1 subject if not specified", - default="sub-NDARBA507GCT", -) -@click.option( - "--study", - help="which study to download. Right now we only support the HBN dataset", - default="HBN", -) -def data(output_dir, study="HBN", subject="sub-NDARBA507GCT"): - """ - Download dwi raw data in BIDS format from public datasets - - :param output_dir: A directory to write files to - :param study: A study name, right now we only support 'HBN' - :param subject: A subject from the study, starting with 'sub-' - :return: None - """ - if not os.path.exists(output_dir): - os.makedirs(output_dir) - - if study.upper() != "HBN": - raise NotImplementedError( - "We only support data downloads from the HBN dataset right now." - ) - - get_dataset(os.path.abspath(output_dir), source=study.upper(), subject_id=subject) - print("done") - - -@click.command() -@click.argument("output_dir") -@click.argument("bucket") -@click.option("--access_key", help="your AWS access key") -@click.option("--secret_key", help="your AWS access secret") -@click.option( - "--provider", - default="s3", - help="Cloud storage provider. Only S3 is supported right now.", -) -@click.option("--subject", default=None, help="Subject id to upload (optional)") -def upload(output_dir, bucket, access_key, secret_key, provider="s3", subject=None): - """ - OUTPUT_DIR: The directory where the output files were stored. - - BUCKET: The cloud bucket name to upload data to. - """ - import boto3 - from dask import compute, delayed - from glob import glob - from tqdm.auto import tqdm - - output_dir = os.path.abspath(output_dir) - if not output_dir.endswith("/"): - output_dir += "/" - - if provider == "s3" or provider == "S3": - client = boto3.client( - "s3", aws_access_key_id=access_key, aws_secret_access_key=secret_key - ) - - if subject is not None: - assert os.path.exists( - os.path.join(output_dir, subject) - ), "this subject id does not exist!" - subjects = [subject] - else: - subjects = [ - os.path.split(s)[1] for s in glob(os.path.join(output_dir, "sub-*")) - ] - - def upload_subject(sub, sub_idx): - base_dir = os.path.join(output_dir, sub, "dmriprep") - for root, dirs, files in os.walk(base_dir): - if len(files): - for f in tqdm( - files, - desc=f"Uploading {sub} {root.split('/')[-1]}", - position=sub_idx, - ): - filepath = os.path.join(root, f) - key = root.replace(output_dir, "") - client.upload_file(filepath, bucket, os.path.join(key, f)) - - uploads = [delayed(upload_subject)(s, idx) for idx, s in enumerate(subjects)] - _ = list(compute(*uploads, scheduler="threads")) - else: - raise NotImplementedError( - "Only S3 is the only supported provider for data uploads at the moment" - ) - - if __name__ == "__main__": sys.exit(main()) # pragma: no cover diff --git a/dmriprep/data.py b/dmriprep/data.py deleted file mode 100644 index 15b4366e..00000000 --- a/dmriprep/data.py +++ /dev/null @@ -1,1125 +0,0 @@ -""" -Functions to download example data from public repositories. - -""" -import copy -import json -import logging -import os -import os.path as op -import re -import subprocess -from pathlib import Path - -import pandas as pd -from dask import compute, delayed -from dask.diagnostics import ProgressBar -from tqdm.auto import tqdm - - -mod_logger = logging.getLogger(__name__) - - -def get_dataset(output_dir, source="HBN", subject_id="sub-NDARBA507GCT"): - if source in ["HBN"]: - get_hbn_data(output_dir, subject_id) - else: - raise ValueError("Invalid dataset source") - - -def get_hbn_data(output_dir, subject_id): - hbn_study = HBN(subjects=subject_id) - subject = hbn_study.subjects[0] - subject.download(directory=output_dir) - # TODO: return a dict of subject ids and folder locations. - return os.path.join(output_dir, subject_id) - - -def get_s3_client(): - """Return a boto3 s3 client - - Returns - ------- - s3_client : boto3.client('s3') - """ - import boto3 - from botocore import UNSIGNED - from botocore.client import Config - - # Global s3 client to preserve anonymous config - s3_client = boto3.client("s3", config=Config(signature_version=UNSIGNED)) - return s3_client - - -def _get_matching_s3_keys(bucket, prefix="", suffix=""): - """Generate all the matching keys in an S3 bucket. - - Parameters - ---------- - bucket : str - Name of the S3 bucket - - prefix : str, optional - Only fetch keys that start with this prefix - - suffix : str, optional - Only fetch keys that end with this suffix - - Yields - ------ - key : str - S3 keys that match the prefix and suffix - """ - s3 = get_s3_client() - kwargs = {"Bucket": bucket, "MaxKeys": 1000} - - # If the prefix is a single string (not a tuple of strings), we can - # do the filtering directly in the S3 API. - if isinstance(prefix, str): - kwargs["Prefix"] = prefix - - while True: - # The S3 API response is a large blob of metadata. - # 'Contents' contains information about the listed objects. - resp = s3.list_objects_v2(**kwargs) - - try: - contents = resp["Contents"] - except KeyError: - return - - for obj in contents: - key = obj["Key"] - if key.startswith(prefix) and key.endswith(suffix): - yield key - - # The S3 API is paginated, returning up to 1000 keys at a time. - # Pass the continuation token into the next response, until we - # reach the final page (when this field is missing). - try: - kwargs["ContinuationToken"] = resp["NextContinuationToken"] - except KeyError: - break - - -def _download_from_s3(fname, bucket, key, overwrite=False): - """Download object from S3 to local file - - Parameters - ---------- - fname : str - File path to which to download the object - - bucket : str - S3 bucket name - - key : str - S3 key for the object to download - - overwrite : bool, default=False - If True, overwrite file if it already exists. - If False, skip download and return - """ - # Create the directory and file if necessary - s3 = get_s3_client() - Path(op.dirname(fname)).mkdir(parents=True, exist_ok=True) - try: - Path(fname).touch(exist_ok=overwrite) - - # Download the file - s3.download_file(Bucket=bucket, Key=key, Filename=fname) - except FileExistsError: - mod_logger.info(f"File {fname} already exists. Continuing...") - pass - - -def _recursive_split_ext(path): - """Recursively split pathname `path` into components. - - Parameters - ---------- - path : path-like object - - Returns - ------- - tuple : - tuple of path parts. All parts except the first have a beginning - period. - - """ - p0, p1 = op.splitext(path) - if p1: - return _recursive_split_ext(p0) + (p1,) - else: - return (p0,) - - -def _cumulative_paths(path_parts, add_ext=""): - """Return all possible cumulative path names from a list of path parts - - For example, if `path_parts` = ["a", ".b", ".c", ".d"], - this function will return ["a", "a.b", "a.b.c", "a.b.c.d"] - - Parameters - ---------- - path_parts: sequence of strings - The sequence of path substrings. All elements except the first must - begin with a period. - - add_ext: str - If provided, add this extension to each element of the result - - Returns - ------- - list : - List of cumulative path names - """ - try: - add_ext = add_ext if add_ext[0] == "." else "." + add_ext - except IndexError: - pass - - return ["".join(path_parts[: i + 1]) + add_ext for i in range(len(path_parts))] - - -class Study: - """A dMRI based study with a BIDS compliant directory structure""" - - def __init__(self, study_id, bucket, s3_prefix, subjects=None): - """Initialize a Study instance - - Parameters - ---------- - study_id : str - An identifier string for the study - - bucket : str - The S3 bucket that contains the study data - - s3_prefix : str - The S3 prefix common to all of the study objects on S3 - - subjects : str, sequence(str), int, or None - If int, retrieve S3 keys for the first `subjects` subjects. - If "all", retrieve all subjects. - If str or sequence of strings, retrieve S3 keys for the specified - subjects. If None, retrieve S3 keys for the first subject. - """ - if not isinstance(study_id, str): - raise TypeError("subject_id must be a string.") - - if not isinstance(bucket, str): - raise TypeError("bucket must be a string.") - - if not isinstance(s3_prefix, str): - raise TypeError("s3_prefix must be a string.") - - if not ( - subjects is None - or isinstance(subjects, int) - or isinstance(subjects, str) - or all(isinstance(s, str) for s in subjects) - ): - raise TypeError( - "subjects must be an int, string or a " "sequence of strings." - ) - - if isinstance(subjects, int) and subjects < 1: - raise ValueError("If subjects is an int, it must be " "greater than 0.") - - self._study_id = study_id - self._bucket = bucket - self._s3_prefix = s3_prefix - - self._all_subjects = self.list_all_subjects() - if subjects is None or subjects == 1: - enter_validation_loop = True - subjects = [sorted(self._all_subjects.keys())[0]] - self._n_requested = 1 - elif isinstance(subjects, int) and subjects > 1: - enter_validation_loop = True - self._n_requested = subjects - subjects = sorted(self._all_subjects.keys())[:subjects] - elif subjects == "all": - enter_validation_loop = False - self._n_requested = len(self._all_subjects) - subjects = list(self._all_subjects.keys()) - elif isinstance(subjects, str): - enter_validation_loop = False - self._n_requested = 1 - subjects = [subjects] - else: - enter_validation_loop = False - self._n_requested = len(subjects) - - if not set(subjects) <= set(self._all_subjects.keys()): - raise ValueError( - f"The following subjects could not be found in the study: " - f"{set(subjects) - set(self._all_subjects.keys())}" - ) - - subs = [delayed(self._get_subject)(s) for s in set(subjects)] - - print("Retrieving subject S3 keys") - with ProgressBar(): - subjects = list(compute(*subs, scheduler="threads")) - - self._subjects = [s for s in subjects if s.valid] - - if enter_validation_loop: - # index of first uninspected subject - idx_lo = self._n_requested - while len(self._subjects) < self._n_requested: - n_needed = self._n_requested - len(self._subjects) - if n_needed == 1: - subjects = [sorted(self._all_subjects.keys())[idx_lo]] - else: - subjects = sorted(self._all_subjects.keys())[ - idx_lo : idx_lo + n_needed - ] - - idx_lo += n_needed - - subs = [delayed(self._get_subject)(s) for s in set(subjects)] - - with ProgressBar(): - subjects = list(compute(*subs, scheduler="threads")) - - self._subjects += [s for s in subjects if s.valid] - - self._n_discarded = 0 - else: - self._n_discarded = len([s for s in subjects if not s.valid]) - - @property - def study_id(self): - """An identifier string for the study""" - return self._study_id - - @property - def bucket(self): - """The S3 bucket that contains the study data""" - return self._bucket - - @property - def s3_prefix(self): - """The S3 prefix common to all of the study objects on S3""" - return self._s3_prefix - - @property - def subjects(self): - """A list of Subject instances for each requested subject""" - return self._subjects - - def __repr__(self): - return ( - f"{type(self).__name__}(study_id={self.study_id}, " - f"bucket={self.bucket}, s3_prefix={self.s3_prefix})" - ) - - def _get_subject(self, subject_id): - """Return a Subject instance from a subject-ID""" - return Subject( - subject_id=subject_id, site=self._all_subjects[subject_id], study=self - ) - - def list_all_subjects(self): - """Return a study-specific list of subjects. - - Returns - ------- - dict - dict with participant_id as keys and site_id as values - """ - raise NotImplementedError - - def filter_keys(self, subject): - """Study-specific S3 key filtering - - Parameters - ---------- - subject : dmriprep.data.Subject - subject instance - """ - pass - - def postprocess(self, subject): - """Study-specific postprocessing steps - - Parameters - ---------- - subject : dmriprep.data.Subject - subject instance - """ - pass - - def download(self, directory, include_site=False, overwrite=False, pbar=True): - """Download files for each subject in the study - - Parameters - ---------- - directory : str - Directory to which to download subject files - - include_site : bool, default=False - If True, include the site-ID in the download path - - overwrite : bool, default=False - If True, overwrite files for each subject - - pbar : bool, default=True - If True, include progress bar - - See Also - -------- - dmriprep.data.Subject.download() - """ - results = [ - delayed(sub.download)( - directory=directory, - include_site=include_site, - overwrite=overwrite, - pbar=pbar, - pbar_idx=idx, - ) - for idx, sub in enumerate(self.subjects) - ] - - compute(*results, scheduler="threads") - - -class S3BidsStudy(Study): - """ - - """ - - def __init__(self, study_id, bucket, s3_prefix=None, subjects=None): - """ - Initialize a study which is organized as BIDS compliant S3 bucket, or a - sub-path of this bucket. - - Parameters - ---------- - study_id : str An identifier string for the study - - bucket : str The S3 bucket that contains the study data - - s3_prefix : str, optional The S3 prefix common to all of the study - objects on S3. Defaults to the root of the bucket. - - subjects : str, sequence(str), int, or None - If int, retrieve S3 keys for the first `subjects` subjects. - If str or sequence of strings, retrieve S3 keys for the specified - subjects. If None, retrieve S3 keys for the first subject. If 'all', - use all of the subjects in the study. - """ - if s3_prefix is None: - s3_prefix = "" - super().__init__( - study_id=study_id, bucket=bucket, s3_prefix=s3_prefix, subjects=subjects - ) - - def list_all_subjects(self): - """ - Find the identifiers of all subjects - """ - # XXX Ariel will figure this out. - - -class HBN(Study): - """The HBN study - - See Also - -------- - dmriprep.data.Study - """ - - def __init__(self, subjects=None): - """Initialize the HBN instance - - Parameters - ---------- - subjects : str, sequence(str), int, or None - If int, retrieve S3 keys for the first `subjects` subjects. - If str or sequence of strings, retrieve S3 keys for the specified - subjects. If None, retrieve S3 keys for the first subject. - """ - super().__init__( - study_id="HBN", - bucket="fcp-indi", - s3_prefix="data/Projects/HBN/MRI", - subjects=subjects, - ) - - def list_all_subjects(self): - """Return dict of HBN subjects - - Retrieve subjects from the participants.tsv files at each site - - Returns - ------- - dict - dict with participant_id as keys and site_id as values - """ - - def get_site_tsv_keys(site_id): - pre = "data/Projects/HBN/MRI/" - raw = pre + f"{site_id}/participants.tsv" - deriv = pre + f"{site_id}/derivatives/participants.tsv" - return {"raw": raw, "deriv": deriv} - - sites = ["Site-CBIC", "Site-RU", "Site-SI"] - tsv_keys = {site: get_site_tsv_keys(site) for site in sites} - - s3 = get_s3_client() - - def get_subs_from_tsv_key(s3_key): - response = s3.get_object(Bucket=self.bucket, Key=s3_key) - - return set(pd.read_csv(response.get("Body")).participant_id.values) - - subjects = {} - for site, s3_keys in tsv_keys.items(): - site_subs = {k: get_subs_from_tsv_key(v) for k, v in s3_keys.items()} - subjects[site] = site_subs["raw"] & site_subs["deriv"] - - all_subjects = {} - for site, subs in subjects.items(): - for s in subs: - all_subjects[s] = site - - return all_subjects - - def filter_keys(self, subject): - """Filter S3 keys based on HBN specific vagaries - - HBN Site-CBIC has multiple anatomy folders due to - motion correction software at the scanner level. - If subject.site == "Site-CBIC" then choose only the - anatomy files in the T1W_VNavNorm files - - Parameters - ---------- - subject : dmriprep.data.Subject - subject instance - """ - if subject.site == "Site-CBIC": - t1w_keys = subject.s3_keys["t1w"] - freesurfer_keys = subject.s3_keys["freesurfer"] - correct_dir = "T1w_VNavNorm" - - subject._s3_keys["t1w"] = list(filter(lambda x: correct_dir in x, t1w_keys)) - - subject._s3_keys["freesurfer"] = list( - filter(lambda x: correct_dir in x, freesurfer_keys) - ) - - def postprocess(self, subject): - """Move the T1 file back into the freesurfer directory. - - This step is specific to the HBN dataset where the T1 files - are outside of the derivatives/sub-XXX/freesurfer directory, - due to defacing protocols. - - Parameters - ---------- - subject : dmriprep.data.Subject - subject instance - """ - for sess in subject.files.keys(): - t1_file = subject.files[sess]["t1w"][0] - freesurfer_path = op.join(op.dirname(t1_file), "freesurfer") - - convert_cmd = "mri_convert {in_:s} {out_:s}".format( - in_=t1_file, out_=op.join(freesurfer_path, "mri", "orig.mgz") - ) - - fnull = open(os.devnull, "w") - subprocess.call(convert_cmd.split(), stdout=fnull, stderr=subprocess.STDOUT) - - # if the site is CBIC, then the freesurfer directory has an additional level. - # move that level up by 1 (e.g. removing the T1w_VNavNorm folder - if subject.site == "Site-CBIC": - newpath = freesurfer_path.replace("T1w_VNavNorm/", "") - move_cmd = "mv {oldpath} {newpath}".format( - oldpath=freesurfer_path, newpath=newpath - ) - fnull1 = open(os.devnull, "w") - subprocess.call( - move_cmd.split(), stdout=fnull1, stderr=subprocess.STDOUT - ) - - # now check that the AP/PA files are named correctly - # eg it should look like "sub-{id}_dir-{dir}_acq-dwi_epi.nii.gz - # but sometimes it looks like sub-{id}_acq-dwi_run-01_epi.nii.gz - # which is silly. the direction should be in the filename. - - -class Subject: - """A single dMRI study subject""" - - def __init__(self, subject_id, study, site=None): - """Initialize a Subject instance - - Parameters - ---------- - subject_id : str - Subject-ID for this subject - - study : dmriprep.data.Study - The Study for which this subject was a participant - - site : str, optional - Site-ID for the site from which this subject's data was collected - """ - if not isinstance(subject_id, str): - raise TypeError("subject_id must be a string.") - - if not isinstance(study, Study): - raise TypeError("study must be an instance of Study.") - - self._subject_id = subject_id - self._study = study - self._site = site - self._valid = False - self._organize_s3_keys() - if self.valid: - self.study.filter_keys(self) - self._s3_keys = self._determine_directions(self._s3_keys) - self._files = None - - @property - def subject_id(self): - """An identifier string for the subject""" - return self._subject_id - - @property - def study(self): - """The study in which this subject participated""" - return self._study - - @property - def site(self): - """The site at which this subject was a participant""" - return self._site - - @property - def valid(self): - """If True, this subject has all the required MRI files""" - return self._valid - - @property - def s3_keys(self): - """S3 keys for this subject's dMRI data - - The S3 keys are stored in a dict with following keys: - - "dwi": Nifti file for DWI image - - "bval": Bval file - - "bvec": Bvec file - - "epi_ap": Nifti EPI image (anterior to posterior) - - "epi_pa": Nifti EPI image (posterior to anterior) - - "freesurfer": Freesurfer structural files - - "t1w": T1W Nifti file - """ - return self._s3_keys - - @property - def files(self): - """Local files for this subject's dMRI data - - Before the call to subject.download(), this is None. - Afterward, the files are stored in a dict with keys - for each imaging session. The value for each session - is itself a dict with the following keys: - - "dwi": Nifti file for DWI image - - "bval": Bval file - - "bvec": Bvec file - - "epi_ap": Nifti EPI image (anterior to posterior) - - "epi_pa": Nifti EPI image (posterior to anterior) - - "freesurfer": Freesurfer structural files - - "t1w": T1W Nifti file - """ - return self._files - - def __repr__(self): - return ( - f"{type(self).__name__}(subject_id={self.subject_id}, " - f"study_id={self.study.study_id}, site={self.site}, " - f"valid={self.valid})" - ) - - def _list_s3_keys(self): - """Get all required S3 keys for this subject - - Returns - ------- - s3_keys : dict - S3 keys organized into "raw" and "deriv" lists - """ - prefixes = { - "raw": "/".join([self.study.s3_prefix, self.site, self.subject_id]), - "deriv": "/".join( - [self.study.s3_prefix, self.site, "derivatives", self.subject_id] - ), - } - - s3_keys = { - rd: list(_get_matching_s3_keys(bucket=self.study.bucket, prefix=prefix)) - for rd, prefix in prefixes.items() - } - - return s3_keys - - def _organize_s3_keys(self): - """Organize S3 keys into a dict - - The dict keys are "dwi," "bvec," "bval," "epi_nii," "epi_json," - "freesurfer," "t1w" and the values are the associated S3 object keys - """ - # Retrieve and unpack the s3_keys - s3_keys = self._list_s3_keys() - dwi_keys = [k for k in s3_keys["raw"] if "/dwi/" in k] - fmap_keys = [k for k in s3_keys["raw"] if "/fmap/" in k] - deriv_keys = s3_keys["deriv"] - all_json_keys = [k for k in s3_keys["raw"] if k.endswith(".json")] - - # Get the dwi files, bvec files, and bval files - dwi = [f for f in dwi_keys if f.endswith(".nii.gz") and "TRACEW" not in f] - bvec = [f for f in dwi_keys if f.endswith(".bvec")] - bval = [f for f in dwi_keys if f.endswith(".bval")] - epi_nii = [f for f in fmap_keys if f.endswith("epi.nii.gz") and "fMRI" not in f] - epi_json = [f for f in fmap_keys if f.endswith("epi.json") and "fMRI" not in f] - t1w = [f for f in deriv_keys if f.endswith("/T1w.nii.gz")] - freesurfer = [f for f in deriv_keys if "/freesurfer/" in f] - - json_keys = [] - for file_list in [dwi, bvec, bval, epi_nii, t1w]: - for f in file_list: - potential_keys = _cumulative_paths( - _recursive_split_ext(f), add_ext="json" - ) - json_keys += [k for k in potential_keys if k in all_json_keys] - - # Use truthiness of non-empty lists to verify that all - # of the required prereq files exist in `s3_keys` - # TODO: If some of the files are missing, look farther up in the directory - # TODO: structure to see if there are files we should inherit - if all([dwi, bval, bvec, epi_nii, epi_json, t1w, freesurfer]): - self._valid = True - self._s3_keys = dict( - dwi=dwi, - bvec=bvec, - bval=bval, - json=json_keys, - epi_nii=epi_nii, - epi_json=epi_json, - freesurfer=freesurfer, - t1w=t1w, - ) - else: - self._valid = False - self._s3_keys = None - - def download( - self, directory, include_site=False, overwrite=False, pbar=True, pbar_idx=0 - ): - """Download files from S3 - - Parameters - ---------- - directory : str - Directory to which to download subject files - - include_site : bool, default=False - If True, include the site-ID in the download path - - overwrite : bool, default=False - If True, overwrite files for each subject - - pbar : bool, default=True - If True, include download progress bar - - pbar_idx : int, default=0 - Progress bar index for multithreaded progress bars - """ - if not self.valid: - mod_logger.warning( - f"Subject {self.subject_id} is not a valid subject. " - f"Skipping download." - ) - return - - if include_site: - directory = op.join(directory, self.site) - - files = { - k: [ - op.abspath(op.join(directory, p.split("/" + self.site + "/")[-1])) - for p in v - ] - for k, v in self.s3_keys.items() - } - - # Generate list of (key, file) tuples - key_file_pairs = [] - - for ftype in self.s3_keys.keys(): - s3_keys = self.s3_keys[ftype] - if isinstance(s3_keys, str): - key_file_pairs.append((s3_keys, files[ftype])) - elif all(isinstance(x, str) for x in s3_keys): - for key, fname in zip(s3_keys, files[ftype]): - key_file_pairs.append((key, fname)) - else: - raise TypeError( - f"This subject {self.subject_id} has {ftype} S3 keys that " - f"are neither strings nor a sequence of strings. The " - f"offending S3 keys are {s3_keys!s}" - ) - - try: - files_by_session = self._separate_sessions(files) - self._files = files_by_session - except NotImplementedError: - self._valid = False - mod_logger.warning( - f"Subject {self.subject_id} has inconsistent session numbers." - f"Skipping download." - ) - return - - if not files_by_session.keys(): - # There were no valid sessions - self._valid = False - mod_logger.warning( - f"Subject {self.subject_id} is not a valid subject. " - f"Skipping download." - ) - return - - # Now iterate through the list and download each item - if pbar: - progress = tqdm( - desc=f"Download {self.subject_id}", - position=pbar_idx, - total=len(key_file_pairs) + 1, - ) - - for (key, fname) in key_file_pairs: - _download_from_s3( - fname=fname, bucket=self.study.bucket, key=key, overwrite=overwrite - ) - - if pbar: - progress.update() - - if pbar: - progress.set_description(f"Postproc {self.subject_id}") - - self.study.postprocess(subject=self) - - if pbar: - progress.update() - progress.close() - - def _determine_directions( - self, - input_files, - input_type="s3", - metadata_source="json", - json_key="PhaseEncodingDirection", - ap_value="j-", - pa_value="j", - ): - """Determine direction ['AP', 'PA'] of single subject's EPI nifty files - - Use either metadata in associated json file or filename - - Parameters - ---------- - input_files : dict - The local input files for the subject - - input_type : "s3" or "local", default="s3" - The location of the input files, local or on S3 - - metadata_source : "json" or "filename", default="json" - If "filename," look for the direction in the filename, - otherwise, use the json file and the other parameters - - json_key : string, default="PhaseEncodingDirection" - The key that stores the direction information - - ap_value : string, default="j-" - Metadata value to associate with dir-AP - - pa_value : string, default="j" - Metadata value to associate with dir-PA - - Returns - ------- - dict - Filenames or S3 keys where all fields match self.files or - self.s3_keys except that in the "epi_nii" and "epi_json" keys - have been replaced with "epi_ap" and "epi_pa." - """ - if metadata_source not in ["filename", "json"]: - raise ValueError('metadata_source must be "filename" or "json".') - - if input_type not in ["s3", "local"]: - raise ValueError('input_type must be "local" or "s3".') - - epi_files = input_files["epi_nii"] - json_files = input_files["epi_json"] - if metadata_source == "filename": - ap_files = [f for f in epi_files if "dir-AP" in f] - pa_files = [f for f in epi_files if "dir-PA" in f] - else: - # Confirm that each nifty file has a corresponding json file. - required_json = set([f.replace(".nii.gz", ".json") for f in epi_files]) - if set(json_files) != required_json: - self._valid = False - mod_logger.warning( - f"Subject {self.subject_id} does not have json files " - f"corresponding to its fmap NIFTI files. Failed to " - f"find the following expected files: " - f"{required_json - set(json_files)}. Subject deemed " - f"invalid." - ) - return input_files - - def get_json(json_file): - if input_type == "local": - with open(json_file, "r") as fp: - meta = json.load(fp) - else: - s3 = get_s3_client() - response = s3.get_object(Bucket=self.study.bucket, Key=json_file) - meta = json.loads(response.get("Body").read()) - - return meta - - ap_files = [] - pa_files = [] - for jfile in json_files: - metadata = get_json(jfile) - - direction = metadata.get(json_key) - if direction == ap_value: - if "dir-PA" in jfile: - mod_logger.warning( - "The key {key:s}={val:s} does not match the direction " - "suggested by the filename {fn:s}".format( - key=json_key, val=direction, fn=jfile - ) - ) - ap_files.append(jfile.replace(".json", ".nii.gz")) - elif direction == pa_value: - if "dir-AP" in jfile: - mod_logger.warning( - "The key {key:s}={val:s} does not match the direction " - "suggested by the filename {fn:s}".format( - key=json_key, val=direction, fn=jfile - ) - ) - pa_files.append(jfile.replace(".json", ".nii.gz")) - elif direction is None: - mod_logger.warning( - "The key {key:s} does not exist in file {jfile:s}. " - "Falling back on filename to determine directionality." - "\n\n".format(key=json_key, jfile=jfile) - ) - if "dir-AP" in jfile: - ap_files.append(jfile.replace(".json", ".nii.gz")) - elif "dir-PA" in jfile: - pa_files.append(jfile.replace(".json", ".nii.gz")) - else: - self._valid = False - mod_logger.warning( - f"Subject {self.subject_id} lacks the expected " - f"{json_key} key in file {jfile} and the " - f"directionality could not be inferred from the " - f"file name. Setting subject validity to False." - ) - return input_files - else: - mod_logger.warning( - "The metadata in file {jfile:s} does not match the dir-PA " - "or dir-AP values that you provided. {key:s} = {val:s}. " - "Falling back on filename to determine directionality.\n\n" - "".format(jfile=jfile, key=json_key, val=direction) - ) - if "dir-AP" in jfile: - ap_files.append(jfile.replace(".json", ".nii.gz")) - elif "dir-PA" in jfile: - pa_files.append(jfile.replace(".json", ".nii.gz")) - else: - self._valid = False - mod_logger.warning( - "The metadata for key {key:s} in file {jfile:s} does " - "not match the dir-PA or dir-AP values that you " - "provided. {key:s} = {val:s}. And the directionality " - "could not be inferred from the file name.".format( - key=json_key, jfile=jfile, val=direction - ) - ) - return input_files - - files = copy.deepcopy(input_files) - del files["epi_nii"] - del files["epi_json"] - files["epi_ap"] = ap_files - files["epi_pa"] = pa_files - - return files - - def _separate_sessions( - self, input_files, multiples_policy="sessions", assign_empty_sessions=True - ): - """Separate input file register into different sessions - - Parameters - ---------- - input_files : dict - Dictionary of subject files - - multiples_policy : "sessions" or "concatenate" - Flag that dictates how to handle multiple files in a session. - If "sessions," treat multiples as different sessions and assign - to new session IDs. If "concatenate," concatenate multiples into - a single session - - assign_empty_sessions : bool - If True, assign session IDs to files without a session ID in - their path - - Returns - ------- - dict of dicts - Dict of Dicts of file names - """ - if multiples_policy not in ["sessions", "concatenate"]: - raise ValueError( - '`multiples_policy` must be either "sessions" or ' '"concatenate"' - ) - - # Take only the first of the T1W nifty files - if len(input_files["t1w"]) > 1: - mod_logger.warning( - f"Found more than one T1W file for subject {self.subject_id} " - f"at site {self.site}. Discarding the others.\n\n" - ) - - t1w = input_files["t1w"] - - # Take only the first freesurfer directory - freesurfer_dirs = { - f.split("/freesurfer/")[0] for f in input_files["freesurfer"] - } - - if len(freesurfer_dirs) > 1: - mod_logger.warning( - f"Found more than one freesurfer directory for subject " - f"{self.subject_id} at site {self.site}. Discarding the " - f"others.\n\n" - ) - - freesurfer_dir = freesurfer_dirs.pop() - freesurfer = [ - f for f in input_files["freesurfer"] if f.startswith(freesurfer_dir) - ] - - # Organize the files by session ID - def get_sess_id(filename, fallback="null"): - # Retrieve the session ID from a filename - match = re.search("/ses-[0-9a-zA-Z]*/", filename) - if match is not None: - return match.group().strip("/") - else: - return fallback - - ftypes = ["dwi", "bvec", "bval", "epi_ap", "epi_pa"] - - sess_ids = {ft: {get_sess_id(fn) for fn in input_files[ft]} for ft in ftypes} - - if not all([s == list(sess_ids.values())[0] for s in sess_ids.values()]): - mod_logger.warning( - "Session numbers are inconsistent for subject {sub:s} at site " - "{site:s}. Ses-IDs: {sess_ids!s}.\n" - "Files: {files!s}\n\n".format( - sub=self.subject_id, - site=self.site, - sess_ids=sess_ids, - files={ - k: (v) - for k, v in input_files.items() - if k in ["dwi", "bvec", "bval", "epi_ap", "epi_pa"] - }, - ) - ) - return - - # We just confirmed that all of the session ID sets are equal so we can - # pop one set of session IDs off of `sess_ids` and use it from now on - sess_ids = sess_ids[ftypes[0]] - - # Collect files by session ID and then file type - files_by_session = { - sess: { - ft: [f for f in input_files[ft] if get_sess_id(f) == sess] - for ft in ftypes - } - for sess in sess_ids - } - - output_files = {} - - # Loop over each session ID - for session, files in files_by_session.items(): - # Confirm that the subject has an equal number of each type of file - n_files = { - k: len(v) - for k, v in files.items() - if k in ["dwi", "bvec", "bval", "epi_ap", "epi_pa"] - } - - if len(set(n_files.values())) != 1: - mod_logger.warning( - f"The number of files is inconsistent for subject " - f"{self.subject_id} at site {self.site}. The file numbers " - f"are {n_files!s}\n\n" - ) - elif len(set(n_files.values())) == 1: - # There is only one set of files in this session. - # Append to output. - if session == "null": - output_session = "ses-01" if assign_empty_sessions else None - else: - output_session = session - - output_files[output_session] = dict( - dwi=input_files["dwi"], - bvec=input_files["bvec"], - bval=input_files["bval"], - epi_ap=input_files["epi_ap"], - epi_pa=input_files["epi_pa"], - json=input_files["json"], - t1w=t1w, - freesurfer=freesurfer, - ) - else: - # There are multiple copies of files for this one session ID. - if multiples_policy == "concatenate": - # The multiple copies represent one session and should be - # concatenated - raise NotImplementedError( - "Concatenation of multiples not " "yet implemented." - ) - else: - # The multiple copies represent multiple sessions and - # should be further subdivided into sessions - raise NotImplementedError( - "Session subdivision not yet " "implemented." - ) - - return output_files diff --git a/dmriprep/workflows/dwi/base.py b/dmriprep/workflows/dwi/base.py index f9bcb4dc..43d829fb 100755 --- a/dmriprep/workflows/dwi/base.py +++ b/dmriprep/workflows/dwi/base.py @@ -52,7 +52,6 @@ def init_dwi_preproc_wf( "bval_file", "out_dir", "eddy_niter", - "slice_outlier_threshold", ] ), name="inputnode", From 612e01f0fa37c7e998df4a2fb1bb12ae14ff7a98 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Mon, 8 Jul 2019 16:17:32 -0400 Subject: [PATCH 045/156] rename datasink to outputs --- dmriprep/workflows/dwi/{datasink.py => outputs.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename dmriprep/workflows/dwi/{datasink.py => outputs.py} (100%) diff --git a/dmriprep/workflows/dwi/datasink.py b/dmriprep/workflows/dwi/outputs.py similarity index 100% rename from dmriprep/workflows/dwi/datasink.py rename to dmriprep/workflows/dwi/outputs.py From e1212dc74d464cdf3858d012e233cd2874f53d9b Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Mon, 8 Jul 2019 16:18:32 -0400 Subject: [PATCH 046/156] rename datasink to outputs --- dmriprep/workflows/dwi/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dmriprep/workflows/dwi/__init__.py b/dmriprep/workflows/dwi/__init__.py index c8dbb344..24c6efe7 100644 --- a/dmriprep/workflows/dwi/__init__.py +++ b/dmriprep/workflows/dwi/__init__.py @@ -1,4 +1,4 @@ #!/usr/bin/env python from .base import init_dwi_preproc_wf -from .datasink import init_output_wf +from .outputs import init_output_wf From 286f9a912f8c68eac8e3930e0da3473a2641c3a8 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Mon, 8 Jul 2019 17:19:55 -0400 Subject: [PATCH 047/156] update configs --- .gitignore | 3 --- CONTRIBUTING.rst | 11 ++++++++++- requirements.txt | 2 -- setup.py | 2 -- tox.ini | 5 ++--- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index a8237ba3..3cae9a46 100644 --- a/.gitignore +++ b/.gitignore @@ -103,6 +103,3 @@ ENV/ # Mac OS nonsense: .DS_Store - -#kubernetes stuff -kubernetes/jobs/ diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 6a178e58..2896e4d5 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -119,10 +119,19 @@ Before you submit a pull request, check that it meets these guidelines: 2. If the pull request adds functionality, the docs should be updated. Put your new functionality into a function with a docstring, and add the feature to the list in README.rst. -3. The pull request should work for Python 2.7, 3.4, 3.5 and 3.6, and for PyPy. Check +3. The pull request should work for Python 3.5, 3.6 and 3.7, and for PyPy. Check https://travis-ci.org/nipy/dmriprep/pull_requests and make sure that the tests pass for all supported Python versions. +When opening a pull request, please use one of the following prefixes: + +* **[ENH]** for enhancements +* **[FIX]** for bug fixes +* **[TST]** for new or updated tests +* **[DOC]** for new or updated documentation +* **[STY]** for stylistic changes +* **[REF]** for refactoring existing code + Tips ---- diff --git a/requirements.txt b/requirements.txt index 6788228f..29ffc900 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,4 @@ Click>=6.0 -dask==1.2.2 dipy==0.16.0 nipype==1.2.1 pandas==0.24.2 @@ -7,5 +6,4 @@ parse==1.12.0 tqdm==4.32.1 pybids==0.9.1 matplotlib==3.1.0 -cytoolz==0.9.0.1 numba==0.44.0 diff --git a/setup.py b/setup.py index 58974637..b4add0ae 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,6 @@ requirements = [ "Click>=6.0", - "dask", "dipy", "nipype", "pandas", @@ -21,7 +20,6 @@ "tqdm", "pybids", "matplotlib", - "cytoolz", "numba", ] diff --git a/tox.ini b/tox.ini index 2bb783a1..4347a530 100644 --- a/tox.ini +++ b/tox.ini @@ -1,12 +1,11 @@ [tox] -envlist = py27, py34, py35, py36, flake8 +envlist = py35, py36, py37, flake8 [travis] python = + 3.7: py37 3.6: py36 3.5: py35 - 3.4: py34 - 2.7: py27 [testenv:flake8] basepython = python From ebaca839976f409bb90f82b9515c24ea936418f7 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Mon, 8 Jul 2019 17:30:15 -0400 Subject: [PATCH 048/156] updating cli --- dmriprep/cli.py | 11 ++++++++++ dmriprep/workflows/base.py | 40 +++++++++++++++++++++++++++------- dmriprep/workflows/dwi/base.py | 12 +++++----- 3 files changed, 50 insertions(+), 13 deletions(-) diff --git a/dmriprep/cli.py b/dmriprep/cli.py index 338dd4bb..9dce15cf 100644 --- a/dmriprep/cli.py +++ b/dmriprep/cli.py @@ -28,6 +28,15 @@ "can be specified with a space separated list.", default=None, ) +@click.command() +@click.option( + "--ignore", + help="Ignore selected parts of the workflow.", + type=click.Choice(["denoise", "unring"]), +) +@click.option( + "--resize-scale", help="Scale factor to resize DWI image", type=(float) +) @click.option( "--eddy-niter", help="Fixed number of eddy iterations. See " @@ -73,6 +82,7 @@ def main( participant_label, bids_dir, output_dir, + resize_scale, eddy_niter=5, bet_dwi=0.3, bet_mag=0.3, @@ -109,6 +119,7 @@ def main( subject_list, work_dir, output_dir, + resize_scale, bet_dwi, bet_mag, total_readout, diff --git a/dmriprep/workflows/base.py b/dmriprep/workflows/base.py index 42d0a1e1..588db766 100644 --- a/dmriprep/workflows/base.py +++ b/dmriprep/workflows/base.py @@ -8,8 +8,14 @@ def init_dmriprep_wf( - layout, subject_list, work_dir, output_dir, - bet_dwi, bet_mag, total_readout + layout, + subject_list, + work_dir, + output_dir, + resize_scale, + bet_dwi, + bet_mag, + total_readout, ): dmriprep_wf = pe.Workflow(name="dmriprep_wf") dmriprep_wf.base_dir = work_dir @@ -22,6 +28,7 @@ def init_dmriprep_wf( name="single_subject_" + subject_id + "_wf", work_dir=work_dir, output_dir=output_dir, + resize_scale=resize_scale, bet_dwi=bet_dwi, bet_mag=bet_mag, total_readout=total_readout, @@ -40,8 +47,15 @@ def init_dmriprep_wf( def init_single_subject_wf( - layout, subject_id, name, work_dir, output_dir, - bet_dwi, bet_mag, total_readout + layout, + subject_id, + name, + work_dir, + output_dir, + resize_scale, + bet_dwi, + bet_mag, + total_readout, ): dwi_files = layout.get( @@ -65,14 +79,24 @@ def init_single_subject_wf( session_id = entities["session"] metadata = layout.get_metadata(dwi_file) dwi_preproc_wf = init_dwi_preproc_wf( - subject_id=subject_id, dwi_file=dwi_file, metadata=metadata, layout=layout, - bet_dwi_frac=bet_dwi, bet_mag_frac=bet_mag, total_readout=total_readout + subject_id=subject_id, + dwi_file=dwi_file, + dwi_meta=metadata, + layout=layout, + resize_scale=resize_scale, + bet_dwi_frac=bet_dwi, + bet_mag_frac=bet_mag, + total_readout=total_readout, ) datasink_wf = init_output_wf( - subject_id=subject_id, session_id=session_id, output_folder=output_dir + subject_id=subject_id, + session_id=session_id, + output_folder=output_dir, ) - dwi_preproc_wf.base_dir = os.path.join(os.path.abspath(work_dir), subject_id) + dwi_preproc_wf.base_dir = os.path.join( + os.path.abspath(work_dir), subject_id + ) inputspec = dwi_preproc_wf.get_node("inputnode") inputspec.inputs.subject_id = subject_id diff --git a/dmriprep/workflows/dwi/base.py b/dmriprep/workflows/dwi/base.py index 43d829fb..e133b630 100755 --- a/dmriprep/workflows/dwi/base.py +++ b/dmriprep/workflows/dwi/base.py @@ -19,8 +19,9 @@ def init_dwi_preproc_wf( subject_id, dwi_file, - metadata, + dwi_meta, layout, + resize_scale, bet_dwi_frac, bet_mag_frac, total_readout, @@ -38,7 +39,7 @@ def init_dwi_preproc_wf( for fmap in fmaps: fmap["metadata"] = layout.get_metadata(fmap[fmap["suffix"]]) - sdc_wf = init_sdc_prep_wf(fmaps, metadata, layout, bet_mag_frac) + sdc_wf = init_sdc_prep_wf(fmaps, dwi_meta, layout, bet_mag_frac) dwi_wf = pe.Workflow(name="dwi_preproc_wf") @@ -47,7 +48,7 @@ def init_dwi_preproc_wf( fields=[ "subject_id", "dwi_file", - "metadata", + "di_meta", "bvec_file", "bval_file", "out_dir", @@ -66,7 +67,8 @@ def init_dwi_preproc_wf( unring = pe.Node(mrtrix3.MRDeGibbs(), name="unring") - resize = pe.Node(mrtrix3.MRResize(), name="resize") + if resize_scale: + resize = pe.Node(mrtrix3.MRResize(scale=resize_scale), name="resize") def gen_index(in_file): @@ -234,7 +236,7 @@ def get_b0_mask_fn(b0_file): ( inputnode, acqp, - [("dwi_file", "in_file"), ("metadata", "metadata")], + [("dwi_file", "in_file"), ("dwi_meta", "metadata")], ), (unring, ecc, [("out_file", "in_file")]), ( From d39949ac03702a92915525867a8ae82932cdd665 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Mon, 8 Jul 2019 17:30:27 -0400 Subject: [PATCH 049/156] move package imports to top --- dmriprep/workflows/dwi/outputs.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/dmriprep/workflows/dwi/outputs.py b/dmriprep/workflows/dwi/outputs.py index 30cd1bc0..c14fe3a5 100644 --- a/dmriprep/workflows/dwi/outputs.py +++ b/dmriprep/workflows/dwi/outputs.py @@ -1,9 +1,12 @@ #!/usr/bin/env python +import os + +from nipype.pipeline import engine as pe +from nipype.interfaces import io as nio, utility as niu + def init_output_wf(subject_id, session_id, output_folder): - from nipype.pipeline import engine as pe - from nipype.interfaces import io as nio, utility as niu op_wf = pe.Workflow(name="output_wf") @@ -30,10 +33,13 @@ def init_output_wf(subject_id, session_id, output_folder): ) def build_path(output_folder, subject_id, session_id): - import os.path as op - return op.join( - output_folder, "dmriprep", "sub-" + subject_id, "ses-" + session_id, "dwi" + return os.path.join( + output_folder, + "dmriprep", + "sub-" + subject_id, + "ses-" + session_id, + "dwi", ) concat = pe.Node( From e2c08434ebfa7e6b8397e4c47139b53acc64b85d Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Mon, 8 Jul 2019 17:30:49 -0400 Subject: [PATCH 050/156] update docs --- docs/dmriprep.interfaces.rst | 30 ++++++++++++++++++ docs/dmriprep.rst | 46 ++++++++++++++++++++++++++++ docs/dmriprep.workflows.dwi.rst | 38 +++++++++++++++++++++++ docs/dmriprep.workflows.fieldmap.rst | 38 +++++++++++++++++++++++ docs/dmriprep.workflows.rst | 30 ++++++++++++++++++ docs/modules.rst | 7 +++++ 6 files changed, 189 insertions(+) create mode 100644 docs/dmriprep.interfaces.rst create mode 100644 docs/dmriprep.rst create mode 100644 docs/dmriprep.workflows.dwi.rst create mode 100644 docs/dmriprep.workflows.fieldmap.rst create mode 100644 docs/dmriprep.workflows.rst create mode 100644 docs/modules.rst diff --git a/docs/dmriprep.interfaces.rst b/docs/dmriprep.interfaces.rst new file mode 100644 index 00000000..a7517461 --- /dev/null +++ b/docs/dmriprep.interfaces.rst @@ -0,0 +1,30 @@ +dmriprep.interfaces package +=========================== + +Submodules +---------- + +dmriprep.interfaces.fmap module +------------------------------- + +.. automodule:: dmriprep.interfaces.fmap + :members: + :undoc-members: + :show-inheritance: + +dmriprep.interfaces.mrtrix3 module +---------------------------------- + +.. automodule:: dmriprep.interfaces.mrtrix3 + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: dmriprep.interfaces + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/dmriprep.rst b/docs/dmriprep.rst new file mode 100644 index 00000000..4fef8f43 --- /dev/null +++ b/docs/dmriprep.rst @@ -0,0 +1,46 @@ +dmriprep package +================ + +Subpackages +----------- + +.. toctree:: + + dmriprep.interfaces + dmriprep.workflows + +Submodules +---------- + +dmriprep.cli module +------------------- + +.. automodule:: dmriprep.cli + :members: + :undoc-members: + :show-inheritance: + +dmriprep.qc module +------------------ + +.. automodule:: dmriprep.qc + :members: + :undoc-members: + :show-inheritance: + +dmriprep.utils module +--------------------- + +.. automodule:: dmriprep.utils + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: dmriprep + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/dmriprep.workflows.dwi.rst b/docs/dmriprep.workflows.dwi.rst new file mode 100644 index 00000000..187803f6 --- /dev/null +++ b/docs/dmriprep.workflows.dwi.rst @@ -0,0 +1,38 @@ +dmriprep.workflows.dwi package +============================== + +Submodules +---------- + +dmriprep.workflows.dwi.base module +---------------------------------- + +.. automodule:: dmriprep.workflows.dwi.base + :members: + :undoc-members: + :show-inheritance: + +dmriprep.workflows.dwi.outputs module +------------------------------------- + +.. automodule:: dmriprep.workflows.dwi.outputs + :members: + :undoc-members: + :show-inheritance: + +dmriprep.workflows.dwi.tensor module +------------------------------------ + +.. automodule:: dmriprep.workflows.dwi.tensor + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: dmriprep.workflows.dwi + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/dmriprep.workflows.fieldmap.rst b/docs/dmriprep.workflows.fieldmap.rst new file mode 100644 index 00000000..73619ae0 --- /dev/null +++ b/docs/dmriprep.workflows.fieldmap.rst @@ -0,0 +1,38 @@ +dmriprep.workflows.fieldmap package +=================================== + +Submodules +---------- + +dmriprep.workflows.fieldmap.base module +--------------------------------------- + +.. automodule:: dmriprep.workflows.fieldmap.base + :members: + :undoc-members: + :show-inheritance: + +dmriprep.workflows.fieldmap.fmap module +--------------------------------------- + +.. automodule:: dmriprep.workflows.fieldmap.fmap + :members: + :undoc-members: + :show-inheritance: + +dmriprep.workflows.fieldmap.phasediff module +-------------------------------------------- + +.. automodule:: dmriprep.workflows.fieldmap.phasediff + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: dmriprep.workflows.fieldmap + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/dmriprep.workflows.rst b/docs/dmriprep.workflows.rst new file mode 100644 index 00000000..8dc264ff --- /dev/null +++ b/docs/dmriprep.workflows.rst @@ -0,0 +1,30 @@ +dmriprep.workflows package +========================== + +Subpackages +----------- + +.. toctree:: + + dmriprep.workflows.dwi + dmriprep.workflows.fieldmap + +Submodules +---------- + +dmriprep.workflows.base module +------------------------------ + +.. automodule:: dmriprep.workflows.base + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: dmriprep.workflows + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/modules.rst b/docs/modules.rst new file mode 100644 index 00000000..05dd5a95 --- /dev/null +++ b/docs/modules.rst @@ -0,0 +1,7 @@ +dmriprep +======== + +.. toctree:: + :maxdepth: 4 + + dmriprep From d02c7bcdaa60d6f709669468c63ece902d539619 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Mon, 8 Jul 2019 17:31:04 -0400 Subject: [PATCH 051/156] temp tensor module --- dmriprep/workflows/dwi/tensor.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 dmriprep/workflows/dwi/tensor.py diff --git a/dmriprep/workflows/dwi/tensor.py b/dmriprep/workflows/dwi/tensor.py new file mode 100644 index 00000000..e69de29b From c4690b67f30f2a47e92374aac9fe3b92463a374c Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Mon, 8 Jul 2019 17:34:57 -0400 Subject: [PATCH 052/156] update grabbing fmap metadata --- dmriprep/workflows/dwi/base.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dmriprep/workflows/dwi/base.py b/dmriprep/workflows/dwi/base.py index e133b630..dfc3588e 100755 --- a/dmriprep/workflows/dwi/base.py +++ b/dmriprep/workflows/dwi/base.py @@ -37,7 +37,12 @@ def init_dwi_preproc_wf( ) for fmap in fmaps: - fmap["metadata"] = layout.get_metadata(fmap[fmap["suffix"]]) + if fmap["suffix"] == "phase": + fmap["metadata"] = {} + fmap["metadata"]["phase1"] = layout.get_metadata(fmap["phase1"]) + fmap["metadata"]["phase2"] = layout.get_metadata(fmap["phase2"]) + else: + fmap["metadata"] = layout.get_metadata(fmap[fmap["suffix"]]) sdc_wf = init_sdc_prep_wf(fmaps, dwi_meta, layout, bet_mag_frac) From 80005d0cc97f9c1ad75620a2d7c4ce2b660a420b Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Mon, 8 Jul 2019 17:36:12 -0400 Subject: [PATCH 053/156] rename dmriprep to dmripreproc --- .github/ISSUE_TEMPLATE.md | 2 +- .travis.yml | 2 +- CONTRIBUTING.rst | 28 ++++++++-------- Makefile | 8 ++--- README.md | 32 +++++++++---------- {dmriprep => dmripreproc}/__init__.py | 8 ++--- {dmriprep => dmripreproc}/cli.py | 8 ++--- .../interfaces/__init__.py | 0 {dmriprep => dmripreproc}/interfaces/fmap.py | 0 .../interfaces/mrtrix3.py | 0 {dmriprep => dmripreproc}/qc.py | 0 {dmriprep => dmripreproc}/utils.py | 0 .../workflows/__init__.py | 0 {dmriprep => dmripreproc}/workflows/base.py | 12 +++---- .../workflows/dwi/__init__.py | 0 .../workflows/dwi/base.py | 0 .../workflows/dwi/outputs.py | 2 +- .../workflows/dwi/tensor.py | 0 .../workflows/fieldmap/__init__.py | 0 .../workflows/fieldmap/base.py | 0 .../workflows/fieldmap/fmap.py | 0 .../workflows/fieldmap/phasediff.py | 0 docs/Makefile | 2 +- docs/conf.py | 26 +++++++-------- docs/dmriprep.interfaces.rst | 12 +++---- docs/dmriprep.rst | 20 ++++++------ docs/dmriprep.workflows.dwi.rst | 16 +++++----- docs/dmriprep.workflows.fieldmap.rst | 16 +++++----- docs/dmriprep.workflows.rst | 12 +++---- docs/img/dmriprep_icon.svg | 4 +-- docs/index.rst | 2 +- docs/installation.rst | 16 +++++----- docs/make.bat | 2 +- docs/modules.rst | 4 +-- docs/usage.rst | 4 +-- setup.cfg | 2 +- setup.py | 14 ++++---- tests/test_dmriprep.py | 4 +-- tests/test_utils.py | 4 +-- tox.ini | 2 +- 40 files changed, 132 insertions(+), 132 deletions(-) rename {dmriprep => dmripreproc}/__init__.py (85%) rename {dmriprep => dmripreproc}/cli.py (95%) rename {dmriprep => dmripreproc}/interfaces/__init__.py (100%) rename {dmriprep => dmripreproc}/interfaces/fmap.py (100%) rename {dmriprep => dmripreproc}/interfaces/mrtrix3.py (100%) rename {dmriprep => dmripreproc}/qc.py (100%) rename {dmriprep => dmripreproc}/utils.py (100%) rename {dmriprep => dmripreproc}/workflows/__init__.py (100%) rename {dmriprep => dmripreproc}/workflows/base.py (93%) rename {dmriprep => dmripreproc}/workflows/dwi/__init__.py (100%) rename {dmriprep => dmripreproc}/workflows/dwi/base.py (100%) rename {dmriprep => dmripreproc}/workflows/dwi/outputs.py (98%) rename {dmriprep => dmripreproc}/workflows/dwi/tensor.py (100%) rename {dmriprep => dmripreproc}/workflows/fieldmap/__init__.py (100%) rename {dmriprep => dmripreproc}/workflows/fieldmap/base.py (100%) rename {dmriprep => dmripreproc}/workflows/fieldmap/fmap.py (100%) rename {dmriprep => dmripreproc}/workflows/fieldmap/phasediff.py (100%) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 82215755..b1e3287a 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,4 +1,4 @@ -* dmriprep version: +* dmriprepoc version: * Python version: * Operating System: diff --git a/.travis.yml b/.travis.yml index fee7fbc0..3f5e8338 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,5 +24,5 @@ deploy: secure: PLEASE_REPLACE_ME on: tags: true - repo: tigrlab/dmripreproc + repo: tigrlab/dmriprepocroc python: 3.7 diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 2896e4d5..7b7906a9 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -7,10 +7,10 @@ Contributing Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given. -Installing a development version of dmriprep +Installing a development version of dmriprepoc -------------------------------------------- -First, you can install a development version of dmriprep by cloning this repository +First, you can install a development version of dmriprepoc by cloning this repository and then typing:: $ pip install -e .[dev] @@ -32,7 +32,7 @@ You can contribute in many ways: Report Bugs ~~~~~~~~~~~ -Report bugs at https://github.com/nipy/dmriprep/issues. +Report bugs at https://github.com/nipy/dmriprepoc/issues. If you are reporting a bug, please include: @@ -55,14 +55,14 @@ and "help wanted" is open to whoever wants to implement it. Write Documentation ~~~~~~~~~~~~~~~~~~~ -dmriprep could always use more documentation, whether as part of the -official dmriprep docs, in docstrings, or even on the web in blog posts, +dmriprepoc could always use more documentation, whether as part of the +official dmriprepoc docs, in docstrings, or even on the web in blog posts, articles, and such. Submit Feedback ~~~~~~~~~~~~~~~ -The best way to send feedback is to file an issue at https://github.com/nipy/dmriprep/issues. +The best way to send feedback is to file an issue at https://github.com/nipy/dmriprepoc/issues. If you are proposing a feature: @@ -74,17 +74,17 @@ If you are proposing a feature: Get Started! ------------ -Ready to contribute? Here's how to set up `dmriprep` for local development. +Ready to contribute? Here's how to set up `dmriprepoc` for local development. -1. Fork the `dmriprep` repo on GitHub. +1. Fork the `dmriprepoc` repo on GitHub. 2. Clone your fork locally:: - $ git clone git@github.com:your_name_here/dmriprep.git + $ git clone git@github.com:your_name_here/dmriprepoc.git 3. Install your local copy into a virtualenv. Assuming you have virtualenvwrapper installed, this is how you set up your fork for local development:: - $ mkvirtualenv dmriprep - $ cd dmriprep/ + $ mkvirtualenv dmriprepoc + $ cd dmriprepoc/ $ python setup.py develop 4. Create a branch for local development:: @@ -96,7 +96,7 @@ Ready to contribute? Here's how to set up `dmriprep` for local development. 5. When you're done making changes, check that your changes pass flake8 and the tests, including testing other Python versions with tox:: - $ flake8 dmriprep tests + $ flake8 dmriprepoc tests $ python setup.py test or py.test $ tox @@ -120,7 +120,7 @@ Before you submit a pull request, check that it meets these guidelines: your new functionality into a function with a docstring, and add the feature to the list in README.rst. 3. The pull request should work for Python 3.5, 3.6 and 3.7, and for PyPy. Check - https://travis-ci.org/nipy/dmriprep/pull_requests + https://travis-ci.org/nipy/dmriprepoc/pull_requests and make sure that the tests pass for all supported Python versions. When opening a pull request, please use one of the following prefixes: @@ -137,7 +137,7 @@ Tips To run a subset of tests:: -$ py.test tests.test_dmriprep +$ py.test tests.test_dmriprepoc Deploying diff --git a/Makefile b/Makefile index 7f76d452..88ba6d85 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,7 @@ clean-test: ## remove test and coverage artifacts rm -fr .pytest_cache lint: ## check style with flake8 - flake8 dmriprep tests + flake8 dmriprepoc tests test: ## run tests quickly with the default Python py.test @@ -60,15 +60,15 @@ test-all: ## run tests on every Python version with tox tox coverage: ## check code coverage quickly with the default Python - coverage run --source dmriprep -m pytest + coverage run --source dmriprepoc -m pytest coverage report -m coverage html $(BROWSER) htmlcov/index.html docs: ## generate Sphinx HTML documentation, including API docs - rm -f docs/dmriprep.rst + rm -f docs/dmriprepoc.rst rm -f docs/modules.rst - sphinx-apidoc -o docs/ dmriprep + sphinx-apidoc -o docs/ dmriprepoc $(MAKE) -C docs clean $(MAKE) -C docs html $(BROWSER) docs/_build/html/index.html diff --git a/README.md b/README.md index ed752966..dc65c3eb 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,16 @@ -[![Known Vulnerabilities](https://snyk.io/test/github/nipy/dmriprep/badge.svg)](https://snyk.io/test/github/nipy/dmriprep) -[![Documentation Status](https://readthedocs.org/projects/dmriprep/badge/?version=latest)](httpsL//dmriprep.readthredocs.io/en/latest/?badge=latest) -[![Build Status](https://travis-ci.org/nipy/dmriprep.png?branch=master)](https://travis-ci.org/nipy/dmriprep) -[![Coverage Status](https://coveralls.io/repos/github/nipy/dmriprep/badge.svg?branch=master)](https://coveralls.io/github/nipy/dmriprep?branch=master) +[![Known Vulnerabilities](https://snyk.io/test/github/nipy/dmriprepoc/badge.svg)](https://snyk.io/test/github/nipy/dmriprepoc) +[![Documentation Status](https://readthedocs.org/projects/dmriprepoc/badge/?version=latest)](httpsL//dmriprepoc.readthredocs.io/en/latest/?badge=latest) +[![Build Status](https://travis-ci.org/nipy/dmriprepoc.png?branch=master)](https://travis-ci.org/nipy/dmriprepoc) +[![Coverage Status](https://coveralls.io/repos/github/nipy/dmriprepoc/badge.svg?branch=master)](https://coveralls.io/github/nipy/dmriprepoc?branch=master) [![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/python/black) -# dmriprep dmriprep +# dmriprepoc dmriprepoc Preprocessing of neuroimaging data in preparation for AFQ analysis * Free software: BSD license -* Documentation: https://dmriprep.readthedocs.io. +* Documentation: https://dmriprepoc.readthedocs.io. ## Preparing your data @@ -34,28 +34,28 @@ You should have raw data organized in the BIDS format. Also, you should have run ## Quickstart ```bash -git clone https://github.com/nipy/dmriprep -cd dmriprep +git clone https://github.com/nipy/dmriprepoc +cd dmriprepoc python setup.py install -dmriprep $BIDS_INPUT_DIR $OUTPUT_DIR --participant-label 01 +dmriprepoc $BIDS_INPUT_DIR $OUTPUT_DIR --participant-label 01 ``` ```bash -git clone https://github.com/nipy/dmriprep -cd dmriprep +git clone https://github.com/nipy/dmriprepoc +cd dmriprepoc make docker # If you don't want to log into the docker image: -docker run -ti -v $BIDS_INPUT_DIR:/inputs -v $OUTPUT_DIR:/outputs dmriprep:prod dmriprep /inputs /outputs +docker run -ti -v $BIDS_INPUT_DIR:/inputs -v $OUTPUT_DIR:/outputs dmriprepoc:prod dmriprepoc /inputs /outputs # If you want to log into the image: -docker run -ti -v $BIDS_INPUT_DIR:/inputs -v $OUTPUT_DIR:/outputs dmriprep:prod +docker run -ti -v $BIDS_INPUT_DIR:/inputs -v $OUTPUT_DIR:/outputs dmriprepoc:prod # Run this inside the docker image: -dmriprep /inputs /outpus --participant-label 01 +dmriprepoc /inputs /outpus --participant-label 01 ``` ## Features @@ -64,11 +64,11 @@ dmriprep /inputs /outpus --participant-label 01 ## Contributing -We love contributions! dmriprep is open source, built on open source, +We love contributions! dmriprepoc is open source, built on open source, and we'd love to have you hang out in our community. We have developed some [guidelines](CONTRIBUTING.rst) for contributing to -dmriprep. +dmriprepoc. **Imposter syndrome disclaimer**: We want your help. No, really. diff --git a/dmriprep/__init__.py b/dmripreproc/__init__.py similarity index 85% rename from dmriprep/__init__.py rename to dmripreproc/__init__.py index d7678e1e..ea3a9a84 100644 --- a/dmriprep/__init__.py +++ b/dmripreproc/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -"""Top-level package for dmriprep.""" +"""Top-level package for dmriprepoc.""" __author__ = """Anisha Keshavan""" __email__ = "anishakeshavan@gmail.com" @@ -22,13 +22,13 @@ # get the log level from environment variable if "DMIRPREP_LOGLEVEL" in os.environ: - loglevel = os.environ["DMRIPREP_LOGLEVEL"] + loglevel = os.environ["dmriprepoc_LOGLEVEL"] module_logger.setLevel(getattr(logging, loglevel.upper())) else: module_logger.setLevel(logging.WARNING) # create a file handler -logpath = os.path.join(os.path.expanduser("~"), ".dmriprep", "dmriprep.log") +logpath = os.path.join(os.path.expanduser("~"), ".dmriprepoc", "dmriprepoc.log") # Create the config directory if it doesn't exist logdir = os.path.dirname(logpath) @@ -52,4 +52,4 @@ # add the handlers to the logger module_logger.addHandler(handler) -module_logger.info("Started new dmriprep session") +module_logger.info("Started new dmriprepoc session") diff --git a/dmriprep/cli.py b/dmripreproc/cli.py similarity index 95% rename from dmriprep/cli.py rename to dmripreproc/cli.py index 9dce15cf..ad21a3c2 100644 --- a/dmriprep/cli.py +++ b/dmripreproc/cli.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -"""Console script for dmriprep.""" +"""Console script for dmriprepoc.""" import os import sys import warnings @@ -9,7 +9,7 @@ import click from . import utils -from .workflows.base import init_dmriprep_wf +from .workflows.base import init_dmriprepoc_wf # Filter warnings that are visible whenever you import another package that # was compiled against an older numpy than is installed. @@ -104,7 +104,7 @@ def main( """ if analysis_level is not "participant": raise NotImplementedError( - "The only valid analysis level for dmriprep " + "The only valid analysis level for dmriprepoc " "is participant at the moment." ) @@ -114,7 +114,7 @@ def main( ) work_dir = os.path.join(output_dir, "scratch") - wf = init_dmriprep_wf( + wf = init_dmriprepoc_wf( layout, subject_list, work_dir, diff --git a/dmriprep/interfaces/__init__.py b/dmripreproc/interfaces/__init__.py similarity index 100% rename from dmriprep/interfaces/__init__.py rename to dmripreproc/interfaces/__init__.py diff --git a/dmriprep/interfaces/fmap.py b/dmripreproc/interfaces/fmap.py similarity index 100% rename from dmriprep/interfaces/fmap.py rename to dmripreproc/interfaces/fmap.py diff --git a/dmriprep/interfaces/mrtrix3.py b/dmripreproc/interfaces/mrtrix3.py similarity index 100% rename from dmriprep/interfaces/mrtrix3.py rename to dmripreproc/interfaces/mrtrix3.py diff --git a/dmriprep/qc.py b/dmripreproc/qc.py similarity index 100% rename from dmriprep/qc.py rename to dmripreproc/qc.py diff --git a/dmriprep/utils.py b/dmripreproc/utils.py similarity index 100% rename from dmriprep/utils.py rename to dmripreproc/utils.py diff --git a/dmriprep/workflows/__init__.py b/dmripreproc/workflows/__init__.py similarity index 100% rename from dmriprep/workflows/__init__.py rename to dmripreproc/workflows/__init__.py diff --git a/dmriprep/workflows/base.py b/dmripreproc/workflows/base.py similarity index 93% rename from dmriprep/workflows/base.py rename to dmripreproc/workflows/base.py index 588db766..80f02787 100644 --- a/dmriprep/workflows/base.py +++ b/dmripreproc/workflows/base.py @@ -7,7 +7,7 @@ from .dwi import init_dwi_preproc_wf, init_output_wf -def init_dmriprep_wf( +def init_dmriprepoc_wf( layout, subject_list, work_dir, @@ -17,8 +17,8 @@ def init_dmriprep_wf( bet_mag, total_readout, ): - dmriprep_wf = pe.Workflow(name="dmriprep_wf") - dmriprep_wf.base_dir = work_dir + dmriprepoc_wf = pe.Workflow(name="dmriprepoc_wf") + dmriprepoc_wf.base_dir = work_dir for subject_id in subject_list: @@ -35,15 +35,15 @@ def init_dmriprep_wf( ) single_subject_wf.config["execution"]["crashdump_dir"] = os.path.join( - output_dir, "dmriprep", "sub-" + subject_id, "log" + output_dir, "dmriprepoc", "sub-" + subject_id, "log" ) for node in single_subject_wf._get_all_nodes(): node.config = deepcopy(single_subject_wf.config) - dmriprep_wf.add_nodes([single_subject_wf]) + dmriprepoc_wf.add_nodes([single_subject_wf]) - return dmriprep_wf + return dmriprepoc_wf def init_single_subject_wf( diff --git a/dmriprep/workflows/dwi/__init__.py b/dmripreproc/workflows/dwi/__init__.py similarity index 100% rename from dmriprep/workflows/dwi/__init__.py rename to dmripreproc/workflows/dwi/__init__.py diff --git a/dmriprep/workflows/dwi/base.py b/dmripreproc/workflows/dwi/base.py similarity index 100% rename from dmriprep/workflows/dwi/base.py rename to dmripreproc/workflows/dwi/base.py diff --git a/dmriprep/workflows/dwi/outputs.py b/dmripreproc/workflows/dwi/outputs.py similarity index 98% rename from dmriprep/workflows/dwi/outputs.py rename to dmripreproc/workflows/dwi/outputs.py index c14fe3a5..a69322f9 100644 --- a/dmriprep/workflows/dwi/outputs.py +++ b/dmripreproc/workflows/dwi/outputs.py @@ -36,7 +36,7 @@ def build_path(output_folder, subject_id, session_id): return os.path.join( output_folder, - "dmriprep", + "dmriprepoc", "sub-" + subject_id, "ses-" + session_id, "dwi", diff --git a/dmriprep/workflows/dwi/tensor.py b/dmripreproc/workflows/dwi/tensor.py similarity index 100% rename from dmriprep/workflows/dwi/tensor.py rename to dmripreproc/workflows/dwi/tensor.py diff --git a/dmriprep/workflows/fieldmap/__init__.py b/dmripreproc/workflows/fieldmap/__init__.py similarity index 100% rename from dmriprep/workflows/fieldmap/__init__.py rename to dmripreproc/workflows/fieldmap/__init__.py diff --git a/dmriprep/workflows/fieldmap/base.py b/dmripreproc/workflows/fieldmap/base.py similarity index 100% rename from dmriprep/workflows/fieldmap/base.py rename to dmripreproc/workflows/fieldmap/base.py diff --git a/dmriprep/workflows/fieldmap/fmap.py b/dmripreproc/workflows/fieldmap/fmap.py similarity index 100% rename from dmriprep/workflows/fieldmap/fmap.py rename to dmripreproc/workflows/fieldmap/fmap.py diff --git a/dmriprep/workflows/fieldmap/phasediff.py b/dmripreproc/workflows/fieldmap/phasediff.py similarity index 100% rename from dmriprep/workflows/fieldmap/phasediff.py rename to dmripreproc/workflows/fieldmap/phasediff.py diff --git a/docs/Makefile b/docs/Makefile index 9b90b864..22b578e9 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -4,7 +4,7 @@ # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = python -msphinx -SPHINXPROJ = dmriprep +SPHINXPROJ = dmriprepoc SOURCEDIR = . BUILDDIR = _build diff --git a/docs/conf.py b/docs/conf.py index 97b2a08e..1969c5d6 100755 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # -# dmriprep documentation build configuration file, created by +# dmriprepoc documentation build configuration file, created by # sphinx-quickstart on Fri Jun 9 13:47:02 2017. # # This file is execfile()d with the current directory set to its @@ -22,7 +22,7 @@ import sys sys.path.insert(0, os.path.abspath('..')) -import dmriprep +import dmriprepoc # -- General configuration --------------------------------------------- @@ -47,7 +47,7 @@ master_doc = 'index' # General information about the project. -project = u'dmriprep' +project = u'dmriprepoc' copyright = u"2018, Anisha Keshavan" author = u"Anisha Keshavan" @@ -56,9 +56,9 @@ # the built documents. # # The short X.Y version. -version = dmriprep.__version__ +version = dmriprepoc.__version__ # The full version, including alpha/beta/rc tags. -release = dmriprep.__version__ +release = dmriprepoc.__version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -101,7 +101,7 @@ # -- Options for HTMLHelp output --------------------------------------- # Output file base name for HTML help builder. -htmlhelp_basename = 'dmriprepdoc' +htmlhelp_basename = 'dmriprepocdoc' # -- Options for LaTeX output ------------------------------------------ @@ -128,8 +128,8 @@ # (source start file, target name, title, author, documentclass # [howto, manual, or own class]). latex_documents = [ - (master_doc, 'dmriprep.tex', - u'dmriprep Documentation', + (master_doc, 'dmriprepoc.tex', + u'dmriprepoc Documentation', u'Anisha Keshavan', 'manual'), ] @@ -139,8 +139,8 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - (master_doc, 'dmriprep', - u'dmriprep Documentation', + (master_doc, 'dmriprepoc', + u'dmriprepoc Documentation', [author], 1) ] @@ -151,10 +151,10 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'dmriprep', - u'dmriprep Documentation', + (master_doc, 'dmriprepoc', + u'dmriprepoc Documentation', author, - 'dmriprep', + 'dmriprepoc', 'One line description of project.', 'Miscellaneous'), ] diff --git a/docs/dmriprep.interfaces.rst b/docs/dmriprep.interfaces.rst index a7517461..81890580 100644 --- a/docs/dmriprep.interfaces.rst +++ b/docs/dmriprep.interfaces.rst @@ -1,21 +1,21 @@ -dmriprep.interfaces package +dmriprepoc.interfaces package =========================== Submodules ---------- -dmriprep.interfaces.fmap module +dmriprepoc.interfaces.fmap module ------------------------------- -.. automodule:: dmriprep.interfaces.fmap +.. automodule:: dmriprepoc.interfaces.fmap :members: :undoc-members: :show-inheritance: -dmriprep.interfaces.mrtrix3 module +dmriprepoc.interfaces.mrtrix3 module ---------------------------------- -.. automodule:: dmriprep.interfaces.mrtrix3 +.. automodule:: dmriprepoc.interfaces.mrtrix3 :members: :undoc-members: :show-inheritance: @@ -24,7 +24,7 @@ dmriprep.interfaces.mrtrix3 module Module contents --------------- -.. automodule:: dmriprep.interfaces +.. automodule:: dmriprepoc.interfaces :members: :undoc-members: :show-inheritance: diff --git a/docs/dmriprep.rst b/docs/dmriprep.rst index 4fef8f43..dd17d085 100644 --- a/docs/dmriprep.rst +++ b/docs/dmriprep.rst @@ -1,4 +1,4 @@ -dmriprep package +dmriprepoc package ================ Subpackages @@ -6,32 +6,32 @@ Subpackages .. toctree:: - dmriprep.interfaces - dmriprep.workflows + dmriprepoc.interfaces + dmriprepoc.workflows Submodules ---------- -dmriprep.cli module +dmriprepoc.cli module ------------------- -.. automodule:: dmriprep.cli +.. automodule:: dmriprepoc.cli :members: :undoc-members: :show-inheritance: -dmriprep.qc module +dmriprepoc.qc module ------------------ -.. automodule:: dmriprep.qc +.. automodule:: dmriprepoc.qc :members: :undoc-members: :show-inheritance: -dmriprep.utils module +dmriprepoc.utils module --------------------- -.. automodule:: dmriprep.utils +.. automodule:: dmriprepoc.utils :members: :undoc-members: :show-inheritance: @@ -40,7 +40,7 @@ dmriprep.utils module Module contents --------------- -.. automodule:: dmriprep +.. automodule:: dmriprepoc :members: :undoc-members: :show-inheritance: diff --git a/docs/dmriprep.workflows.dwi.rst b/docs/dmriprep.workflows.dwi.rst index 187803f6..66a921ff 100644 --- a/docs/dmriprep.workflows.dwi.rst +++ b/docs/dmriprep.workflows.dwi.rst @@ -1,29 +1,29 @@ -dmriprep.workflows.dwi package +dmriprepoc.workflows.dwi package ============================== Submodules ---------- -dmriprep.workflows.dwi.base module +dmriprepoc.workflows.dwi.base module ---------------------------------- -.. automodule:: dmriprep.workflows.dwi.base +.. automodule:: dmriprepoc.workflows.dwi.base :members: :undoc-members: :show-inheritance: -dmriprep.workflows.dwi.outputs module +dmriprepoc.workflows.dwi.outputs module ------------------------------------- -.. automodule:: dmriprep.workflows.dwi.outputs +.. automodule:: dmriprepoc.workflows.dwi.outputs :members: :undoc-members: :show-inheritance: -dmriprep.workflows.dwi.tensor module +dmriprepoc.workflows.dwi.tensor module ------------------------------------ -.. automodule:: dmriprep.workflows.dwi.tensor +.. automodule:: dmriprepoc.workflows.dwi.tensor :members: :undoc-members: :show-inheritance: @@ -32,7 +32,7 @@ dmriprep.workflows.dwi.tensor module Module contents --------------- -.. automodule:: dmriprep.workflows.dwi +.. automodule:: dmriprepoc.workflows.dwi :members: :undoc-members: :show-inheritance: diff --git a/docs/dmriprep.workflows.fieldmap.rst b/docs/dmriprep.workflows.fieldmap.rst index 73619ae0..6ad3e485 100644 --- a/docs/dmriprep.workflows.fieldmap.rst +++ b/docs/dmriprep.workflows.fieldmap.rst @@ -1,29 +1,29 @@ -dmriprep.workflows.fieldmap package +dmriprepoc.workflows.fieldmap package =================================== Submodules ---------- -dmriprep.workflows.fieldmap.base module +dmriprepoc.workflows.fieldmap.base module --------------------------------------- -.. automodule:: dmriprep.workflows.fieldmap.base +.. automodule:: dmriprepoc.workflows.fieldmap.base :members: :undoc-members: :show-inheritance: -dmriprep.workflows.fieldmap.fmap module +dmriprepoc.workflows.fieldmap.fmap module --------------------------------------- -.. automodule:: dmriprep.workflows.fieldmap.fmap +.. automodule:: dmriprepoc.workflows.fieldmap.fmap :members: :undoc-members: :show-inheritance: -dmriprep.workflows.fieldmap.phasediff module +dmriprepoc.workflows.fieldmap.phasediff module -------------------------------------------- -.. automodule:: dmriprep.workflows.fieldmap.phasediff +.. automodule:: dmriprepoc.workflows.fieldmap.phasediff :members: :undoc-members: :show-inheritance: @@ -32,7 +32,7 @@ dmriprep.workflows.fieldmap.phasediff module Module contents --------------- -.. automodule:: dmriprep.workflows.fieldmap +.. automodule:: dmriprepoc.workflows.fieldmap :members: :undoc-members: :show-inheritance: diff --git a/docs/dmriprep.workflows.rst b/docs/dmriprep.workflows.rst index 8dc264ff..11d0f655 100644 --- a/docs/dmriprep.workflows.rst +++ b/docs/dmriprep.workflows.rst @@ -1,4 +1,4 @@ -dmriprep.workflows package +dmriprepoc.workflows package ========================== Subpackages @@ -6,16 +6,16 @@ Subpackages .. toctree:: - dmriprep.workflows.dwi - dmriprep.workflows.fieldmap + dmriprepoc.workflows.dwi + dmriprepoc.workflows.fieldmap Submodules ---------- -dmriprep.workflows.base module +dmriprepoc.workflows.base module ------------------------------ -.. automodule:: dmriprep.workflows.base +.. automodule:: dmriprepoc.workflows.base :members: :undoc-members: :show-inheritance: @@ -24,7 +24,7 @@ dmriprep.workflows.base module Module contents --------------- -.. automodule:: dmriprep.workflows +.. automodule:: dmriprepoc.workflows :members: :undoc-members: :show-inheritance: diff --git a/docs/img/dmriprep_icon.svg b/docs/img/dmriprep_icon.svg index ce8ba162..e6df76d0 100644 --- a/docs/img/dmriprep_icon.svg +++ b/docs/img/dmriprep_icon.svg @@ -1,9 +1,9 @@ -dmriprepdmriprepoc \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index d654265b..92dcb5bb 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,4 +1,4 @@ -Welcome to dmriprep's documentation! +Welcome to dmriprepoc's documentation! ====================================== .. toctree:: diff --git a/docs/installation.rst b/docs/installation.rst index 9a80cbc9..d56cd9c9 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -8,13 +8,13 @@ Installation Stable release -------------- -To install dmriprep, run this command in your terminal: +To install dmriprepoc, run this command in your terminal: .. code-block:: console - $ pip install dmriprep + $ pip install dmriprepoc -This is the preferred method to install dmriprep, as it will always install the most recent stable release. +This is the preferred method to install dmriprepoc, as it will always install the most recent stable release. If you don't have `pip`_ installed, this `Python installation guide`_ can guide you through the process. @@ -26,19 +26,19 @@ you through the process. From sources ------------ -The sources for dmriprep can be downloaded from the `Github repo`_. +The sources for dmriprepoc can be downloaded from the `Github repo`_. You can either clone the public repository: .. code-block:: console - $ git clone git://github.com/nipy/dmriprep + $ git clone git://github.com/nipy/dmriprepoc Or download the `tarball`_: .. code-block:: console - $ curl -OL https://github.com/nipy/dmriprep/tarball/master + $ curl -OL https://github.com/nipy/dmriprepoc/tarball/master Once you have a copy of the source, you can install it with: @@ -47,5 +47,5 @@ Once you have a copy of the source, you can install it with: $ python setup.py install -.. _Github repo: https://github.com/nipy/dmriprep -.. _tarball: https://github.com/nipy/dmriprep/tarball/master +.. _Github repo: https://github.com/nipy/dmriprepoc +.. _tarball: https://github.com/nipy/dmriprepoc/tarball/master diff --git a/docs/make.bat b/docs/make.bat index c8af47bb..265138ef 100644 --- a/docs/make.bat +++ b/docs/make.bat @@ -9,7 +9,7 @@ if "%SPHINXBUILD%" == "" ( ) set SOURCEDIR=. set BUILDDIR=_build -set SPHINXPROJ=dmriprep +set SPHINXPROJ=dmriprepoc if "%1" == "" goto help diff --git a/docs/modules.rst b/docs/modules.rst index 05dd5a95..1e0222cc 100644 --- a/docs/modules.rst +++ b/docs/modules.rst @@ -1,7 +1,7 @@ -dmriprep +dmriprepoc ======== .. toctree:: :maxdepth: 4 - dmriprep + dmriprepoc diff --git a/docs/usage.rst b/docs/usage.rst index a747741b..56bc3cf4 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -2,6 +2,6 @@ Usage ===== -To use dmriprep in a project:: +To use dmriprepoc in a project:: - import dmriprep + import dmriprepoc diff --git a/setup.cfg b/setup.cfg index c9b349a8..da6c0ceb 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,7 +7,7 @@ tag = True search = version='{current_version}' replace = version='{new_version}' -[bumpversion:file:dmriprep/__init__.py] +[bumpversion:file:dmriprepoc/__init__.py] search = __version__ = '{current_version}' replace = __version__ = '{new_version}' diff --git a/setup.py b/setup.py index b4add0ae..c093d648 100644 --- a/setup.py +++ b/setup.py @@ -44,9 +44,9 @@ description="Preprocessing of neuroimaging data in preparation for AFQ analysis", entry_points={ "console_scripts": [ - "dmriprep=dmriprep.cli:main", - "dmriprep-data=dmriprep.cli:data", - "dmriprep-upload=dmriprep.cli:upload", + "dmriprepoc=dmriprepoc.cli:main", + "dmriprepoc-data=dmriprepoc.cli:data", + "dmriprepoc-upload=dmriprepoc.cli:upload", ] }, install_requires=requirements, @@ -54,13 +54,13 @@ license="BSD license", long_description=readme + "\n\n" + history, include_package_data=True, - keywords="dmriprep", - name="dmriprep", - packages=find_packages(include=["dmriprep*"]), + keywords="dmriprepoc", + name="dmriprepoc", + packages=find_packages(include=["dmriprepoc*"]), setup_requires=setup_requirements, test_suite="tests", tests_require=test_requirements, - url="https://github.com/nipy/dmriprep", + url="https://github.com/nipy/dmriprepoc", version="0.1.0", zip_safe=False, ) diff --git a/tests/test_dmriprep.py b/tests/test_dmriprep.py index 1786035f..783ccb6f 100644 --- a/tests/test_dmriprep.py +++ b/tests/test_dmriprep.py @@ -1,13 +1,13 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -"""Tests for `dmriprep` package.""" +"""Tests for `dmriprepoc` package.""" import pytest from click.testing import CliRunner -from dmriprep import cli +from dmriprepoc import cli @pytest.fixture diff --git a/tests/test_utils.py b/tests/test_utils.py index f5fa1b10..1ab5c059 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,11 +1,11 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -"""Tests for `dmriprep` package.""" +"""Tests for `dmriprepoc` package.""" import numpy as np -from dmriprep.utils import is_hemispherical +from dmriprepoc.utils import is_hemispherical def uniform_points_on_sphere(npoints=1, hemisphere=True, rotate=(0, 0, 0)): diff --git a/tox.ini b/tox.ini index 4347a530..589cce4e 100644 --- a/tox.ini +++ b/tox.ini @@ -10,7 +10,7 @@ python = [testenv:flake8] basepython = python deps = flake8 -commands = flake8 dmriprep +commands = flake8 dmriprepoc [testenv] setenv = From 62221c9cf3a81a6cee9f3456e65052979238e5b6 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Tue, 9 Jul 2019 06:25:13 -0400 Subject: [PATCH 054/156] add data to gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 3cae9a46..b54dbe26 100644 --- a/.gitignore +++ b/.gitignore @@ -103,3 +103,6 @@ ENV/ # Mac OS nonsense: .DS_Store + +# data +notebooks/data/ From dbee40dd5ba4ee912da9ea2a37452e790f418eb4 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Tue, 9 Jul 2019 06:26:07 -0400 Subject: [PATCH 055/156] add topup workflow --- dmriprep/workflows/fieldmap/__init__.py | 1 + dmriprep/workflows/fieldmap/base.py | 98 +++++++++++++++++++------ dmriprep/workflows/fieldmap/pepolar.py | 79 ++++++++++++++++++++ 3 files changed, 156 insertions(+), 22 deletions(-) create mode 100644 dmriprep/workflows/fieldmap/pepolar.py diff --git a/dmriprep/workflows/fieldmap/__init__.py b/dmriprep/workflows/fieldmap/__init__.py index 2b2d1f4b..0b2ecddd 100644 --- a/dmriprep/workflows/fieldmap/__init__.py +++ b/dmriprep/workflows/fieldmap/__init__.py @@ -1,5 +1,6 @@ #!/usr/bin/env python from .base import init_sdc_prep_wf +from .pepolar import init_pepolar_wf from .fmap import init_fmap_wf from .phasediff import init_phase_wf, init_phdiff_wf diff --git a/dmriprep/workflows/fieldmap/base.py b/dmriprep/workflows/fieldmap/base.py index 1447c7f0..c84fe674 100644 --- a/dmriprep/workflows/fieldmap/base.py +++ b/dmriprep/workflows/fieldmap/base.py @@ -1,15 +1,20 @@ #!/usr/bin/env python +from nipype.pipeline import engine as pe +from nipype.interfaces import utility as niu + FMAP_PRIORITY = {"epi": 0, "fieldmap": 1, "phasediff": 2, "phase": 3, "syn": 4} -def init_sdc_prep_wf(fmaps, metadata, layout, bet_mag_frac, omp_nthreads=1, fmap_bspline=False): - from nipype.pipeline import engine as pe - from nipype.interfaces import utility as niu +def init_sdc_prep_wf( + fmaps, metadata, layout, bet_mag_frac, omp_nthreads=1, fmap_bspline=False +): sdc_prep_wf = pe.Workflow(name="sdc_prep_wf") - inputnode = pe.Node(niu.IdentityInterface(fields=["b0_stripped"]), name="inputnode") + inputnode = pe.Node( + niu.IdentityInterface(fields=["b0_stripped"]), name="inputnode" + ) outputnode = pe.Node( niu.IdentityInterface( @@ -20,7 +25,7 @@ def init_sdc_prep_wf(fmaps, metadata, layout, bet_mag_frac, omp_nthreads=1, fmap "bold_ref_brain", "out_warp", "syn_bold_ref", - "method" + "method", ] ), name="outputnode", @@ -29,6 +34,11 @@ def init_sdc_prep_wf(fmaps, metadata, layout, bet_mag_frac, omp_nthreads=1, fmap fmaps.sort(key=lambda fmap: FMAP_PRIORITY[fmap["suffix"]]) fmap = fmaps[0] + if fmap["suffix"] == "epi": + from .pepolar import init_pepolar_wf + + pepolar_wf = init_pepolar_wf() + if fmap["suffix"] == "fieldmap": from .fmap import init_fmap_wf @@ -38,41 +48,69 @@ def init_sdc_prep_wf(fmaps, metadata, layout, bet_mag_frac, omp_nthreads=1, fmap sdc_prep_wf.connect( [ - (inputnode, fmap_wf, [("b0_stripped", "inputnode.b0_stripped")]), + ( + inputnode, + fmap_wf, + [("b0_stripped", "inputnode.b0_stripped")], + ), (fmap_wf, outputnode, [("outputnode.out_fmap", "out_fmap")]), ] ) - if fmap['suffix'] in ('phasediff', 'phase'): + if fmap["suffix"] in ("phasediff", "phase"): from .phasediff import init_phase_wf, init_phdiff_wf from .fmap import init_fmap_wf - if fmap['suffix'] == 'phasediff': + + if fmap["suffix"] == "phasediff": phase_wf = init_phdiff_wf(bet_mag_frac) - phase_wf.inputs.inputnode.phasediff = fmap['phasediff'] + phase_wf.inputs.inputnode.phasediff = fmap["phasediff"] phase_wf.inputs.inputnode.magnitude1 = [ - fmap_ for key, fmap_ in sorted(fmap.items()) + fmap_ + for key, fmap_ in sorted(fmap.items()) if key.startswith("magnitude1") ][0] - phase_wf.inputs.inputnode.phases_meta = layout.get_metadata(phase_wf.inputs.inputnode.phasediff) + phase_wf.inputs.inputnode.phases_meta = layout.get_metadata( + phase_wf.inputs.inputnode.phasediff + ) post_phase_wf = init_fmap_wf() sdc_prep_wf.connect( [ - (inputnode, post_phase_wf, [("b0_stripped", "inputnode.b0_stripped")]), - (phase_wf, post_phase_wf, [("outputnode.out_fmap", "inputnode.fieldmap")]), - (phase_wf, post_phase_wf, [("outputnode.out_mag", "inputnode.magnitude")]), - (post_phase_wf, outputnode, [("outputnode.out_fmap", "out_fmap")]) + ( + inputnode, + post_phase_wf, + [("b0_stripped", "inputnode.b0_stripped")], + ), + ( + phase_wf, + post_phase_wf, + [("outputnode.out_fmap", "inputnode.fieldmap")], + ), + ( + phase_wf, + post_phase_wf, + [("outputnode.out_mag", "inputnode.magnitude")], + ), + ( + post_phase_wf, + outputnode, + [("outputnode.out_fmap", "out_fmap")], + ), ] ) - elif fmap['suffix'] == 'phase': + elif fmap["suffix"] == "phase": phase_wf = init_phase_wf(bet_mag_frac) - phase_wf.inputs.inputnode.phasediff = [fmap['phase1'], fmap['phase2']] + phase_wf.inputs.inputnode.phasediff = [ + fmap["phase1"], + fmap["phase2"], + ] phase_wf.inputs.inputnode.magnitude1 = [ - fmap_ for key, fmap_ in sorted(fmap.items()) + fmap_ + for key, fmap_ in sorted(fmap.items()) if key.startswith("magnitude1") ][0] @@ -84,10 +122,26 @@ def init_sdc_prep_wf(fmaps, metadata, layout, bet_mag_frac, omp_nthreads=1, fmap sdc_prep_wf.connect( [ - (inputnode, post_phase_wf, [("b0_stripped", "inputnode.b0_stripped")]), - (phase_wf, post_phase_wf, [("outputnode.out_fmap", "inputnode.fieldmap")]), - (phase_wf, post_phase_wf, [("outputnode.out_mag", "inputnode.magnitude")]), - (post_phase_wf, outputnode, [("outputnode.out_fmap", "out_fmap")]) + ( + inputnode, + post_phase_wf, + [("b0_stripped", "inputnode.b0_stripped")], + ), + ( + phase_wf, + post_phase_wf, + [("outputnode.out_fmap", "inputnode.fieldmap")], + ), + ( + phase_wf, + post_phase_wf, + [("outputnode.out_mag", "inputnode.magnitude")], + ), + ( + post_phase_wf, + outputnode, + [("outputnode.out_fmap", "out_fmap")], + ), ] ) return sdc_prep_wf diff --git a/dmriprep/workflows/fieldmap/pepolar.py b/dmriprep/workflows/fieldmap/pepolar.py new file mode 100644 index 00000000..34ca318c --- /dev/null +++ b/dmriprep/workflows/fieldmap/pepolar.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python + +from nipype.pipeline import engine as pe +from nipype.interfaces import fsl, utility as niu + + +def init_pepolar_wf(subject_id, dwi_meta, epi_fmaps): + + dwi_file_pe = dwi_meta["PhaseEncodingDirection"] + + usable_fieldmaps_matching_pe = [] + usable_fieldmaps_opposite_pe = [] + + for fmap, pe_dir in epi_fmaps: + if pe_dir == dwi_file_pe: + usable_fieldmaps_matching_pe.append(fmap) + elif pe_dir[0] == dwi_file_pe[0]: + usable_fieldmaps_opposite_pe.append(fmap) + + if not usable_fieldmaps_opposite_pe: + raise Exception("None of the discovered fieldmaps for " + "participant {} has the right phase " + "encoding direction".format(subject_id)) + + wf = pe.Workflow(name="pepolar_wf") + + inputnode = pe.Node(niu.IdentityInterface(fields=["b0_stripped"])) + + outputnode = pe.Node(niu.IdentityInterface(fields=["out_topup"])) + + topup_wf = init_topup_wf() + topup_wf.inputnode.altepi_file = usable_fieldmaps_opposite_pe[0] + + if not usable_fieldmaps_matching_pe: + wf.connect( + [ + (inputnode, topup_wf, [("b0_stripped", "inputnode.epi_file")]) + ] + ) + else: + topup_wf.inputnode.epi_file = usable_fieldmaps_matching_pe[0] + + return wf + + +def init_topup_wf(): + + wf = pe.Workflow(name="topup_wf") + + inputnode = pe.Node( + niu.IdentityInterface(fields=["epi_file", "altepi_file", "encoding_directions"]), + name="inputnode") + + outputnode = pe.Node( + niu.IdentityInterface(fields=["out_fmap"]), + name="outputnode") + + list_merge = pe.Node(niu.Merge(numinputs=2), name="list_merge") + + merge = pe.Node(fsl.Merge(dimension="t"), name="mergeAPPA") + + topup = pe.Node(fsl.TOPUP(), name="topup") + topup.inputs.readout_times = [0.05, 0.05] + + wf.connect( + [ + ( + inputnode, + list_merge, + [("epi_file", "in1"), ("altepi_file", "in2")] + ), + (list_merge, merge, [("out", "in_files")]), + (merge, topup, [("merged_file", "in_file")]), + (inputnode, topup, [("encoding_directions", "encoding_direction")]), + (topup, outputnode, [("out_field", "out_fmap")]), + ] + ) + + return wf From f54fd7f992eb1fb943ee969a76cc1019286861b0 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Tue, 9 Jul 2019 06:26:20 -0400 Subject: [PATCH 056/156] style changes --- dmriprep/workflows/fieldmap/fmap.py | 14 ++- dmriprep/workflows/fieldmap/phasediff.py | 108 ++++++++++++++--------- 2 files changed, 77 insertions(+), 45 deletions(-) diff --git a/dmriprep/workflows/fieldmap/fmap.py b/dmriprep/workflows/fieldmap/fmap.py index df002a5d..72aa0615 100644 --- a/dmriprep/workflows/fieldmap/fmap.py +++ b/dmriprep/workflows/fieldmap/fmap.py @@ -1,15 +1,21 @@ #!/usr/bin/env python +from nipype.pipeline import engine as pe +from nipype.interfaces import fsl, utility as niu + def init_fmap_wf(): - from nipype.pipeline import engine as pe - from nipype.interfaces import fsl, utility as niu wf = pe.Workflow(name="fmap_prep_wf") - inputnode = pe.Node(niu.IdentityInterface(fields=["fieldmap", "magnitude", "b0_stripped"]), name="inputnode") + inputnode = pe.Node( + niu.IdentityInterface(fields=["fieldmap", "magnitude", "b0_stripped"]), + name="inputnode", + ) - outputnode = pe.Node(niu.IdentityInterface(fields=["out_fmap"]), name="outputnode") + outputnode = pe.Node( + niu.IdentityInterface(fields=["out_fmap"]), name="outputnode" + ) rad_to_hz = pe.Node( fsl.BinaryMaths(operation="div", operand_value=6.28), name="radToHz" diff --git a/dmriprep/workflows/fieldmap/phasediff.py b/dmriprep/workflows/fieldmap/phasediff.py index de5e7cae..c47ec565 100644 --- a/dmriprep/workflows/fieldmap/phasediff.py +++ b/dmriprep/workflows/fieldmap/phasediff.py @@ -1,29 +1,41 @@ #!/usr/bin/env python +from nipype.pipeline import engine as pe +from nipype.interfaces import fsl, utility as niu +from ...interfaces import Phasediff2Fieldmap, Phases2Fieldmap + + def init_phase_wf(bet_mag_frac): - from nipype.pipeline import engine as pe - from nipype.interfaces import fsl, utility as niu - from ...interfaces import Phasediff2Fieldmap, Phases2Fieldmap wf = pe.Workflow(name="phase_prep_wf") - inputnode = pe.Node(niu.IdentityInterface(fields=["magnitude1", "phasediff", "b0_stripped", "phases_meta"]), name="inputnode") + inputnode = pe.Node( + niu.IdentityInterface( + fields=["magnitude1", "phasediff", "b0_stripped", "phases_meta"] + ), + name="inputnode", + ) - outputnode = pe.Node(niu.IdentityInterface(fields=["out_fmap", "out_mag"]), name="outputnode") + outputnode = pe.Node( + niu.IdentityInterface(fields=["out_fmap", "out_mag"]), + name="outputnode", + ) - phases2fmap = pe.Node(Phases2Fieldmap(), name='phases2fmap') + phases2fmap = pe.Node(Phases2Fieldmap(), name="phases2fmap") - mag_bet = pe.Node(fsl.BET(frac=bet_mag_frac, robust=True), - name='mag_bet') + mag_bet = pe.Node(fsl.BET(frac=bet_mag_frac, robust=True), name="mag_bet") - prep_fmap = pe.Node(fsl.PrepareFieldmap(scanner='SIEMENS', nocheck=True), - name='prep_fmap') + prep_fmap = pe.Node( + fsl.PrepareFieldmap(scanner="SIEMENS", nocheck=True), name="prep_fmap" + ) fslroi = pe.Node(fsl.ExtractROI(t_min=0, t_size=1), name="fslroi_phase") delta = pe.Node( niu.Function( - input_names=["in_values"], output_names=["out_value"], function=delta_te + input_names=["in_values"], + output_names=["out_value"], + function=delta_te, ), name="delta", ) @@ -33,66 +45,74 @@ def init_phase_wf(bet_mag_frac): # Mag bet (inputnode, mag_bet, [("magnitude1", "in_file")]), # phases -> phdiff - (inputnode, phases2fmap, [('phases_meta', 'metadatas')]), - (inputnode, phases2fmap, [('phasediff', 'phase_files')]), + (inputnode, phases2fmap, [("phases_meta", "metadatas")]), + (inputnode, phases2fmap, [("phasediff", "phase_files")]), # phdiff delta_te - (phases2fmap, delta, [('phasediff_metadata', 'in_values')]), + (phases2fmap, delta, [("phasediff_metadata", "in_values")]), # prep fmap (mag_bet, prep_fmap, [("out_file", "in_magnitude")]), - (phases2fmap, prep_fmap, [('out_file', 'in_phase')]), - (delta, prep_fmap, [('out_value', 'delta_TE')]), + (phases2fmap, prep_fmap, [("out_file", "in_phase")]), + (delta, prep_fmap, [("out_value", "delta_TE")]), # Remove second empty volume (prep_fmap, fslroi, [("out_fieldmap", "in_file")]), (fslroi, outputnode, [("roi_file", "out_fmap")]), - (inputnode, outputnode, [("magnitude1", "out_mag")]) + (inputnode, outputnode, [("magnitude1", "out_mag")]), ] ) return wf + def init_phdiff_wf(bet_mag_frac): - from nipype.pipeline import engine as pe - from nipype.interfaces import fsl, utility as niu - from ...interfaces import Phasediff2Fieldmap wf = pe.Workflow(name="phdiff_prep_wf") - inputnode = pe.Node(niu.IdentityInterface(fields=["magnitude1", "phasediff", "b0_stripped", "phases_meta"]), name="inputnode") + inputnode = pe.Node( + niu.IdentityInterface( + fields=["magnitude1", "phasediff", "b0_stripped", "phases_meta"] + ), + name="inputnode", + ) - outputnode = pe.Node(niu.IdentityInterface(fields=["out_fmap", "out_mag"]), name="outputnode") + outputnode = pe.Node( + niu.IdentityInterface(fields=["out_fmap", "out_mag"]), + name="outputnode", + ) - mag_bet = pe.Node(fsl.BET(frac=bet_mag_frac, robust=True), - name='mag_bet') + mag_bet = pe.Node(fsl.BET(frac=bet_mag_frac, robust=True), name="mag_bet") - prep_fmap = pe.Node(fsl.PrepareFieldmap(scanner='SIEMENS'), - name='prep_fmap') + prep_fmap = pe.Node( + fsl.PrepareFieldmap(scanner="SIEMENS"), name="prep_fmap" + ) fslroi = pe.Node(fsl.ExtractROI(t_min=0, t_size=1), name="fslroi_phase") delta = pe.Node( niu.Function( - input_names=["in_values"], output_names=["out_value"], function=delta_te + input_names=["in_values"], + output_names=["out_value"], + function=delta_te, ), name="delta", ) - #phdiff2fmap = pe.Node(Phasediff2Fieldmap(), name='phdiff2fmap') + # phdiff2fmap = pe.Node(Phasediff2Fieldmap(), name='phdiff2fmap') wf.connect( [ # mag bet (inputnode, mag_bet, [("magnitude1", "in_file")]), # phdiff delta_te - (inputnode, delta, [('phases_meta', 'in_values')]), + (inputnode, delta, [("phases_meta", "in_values")]), # prep fmap (mag_bet, prep_fmap, [("out_file", "in_magnitude")]), - (inputnode, prep_fmap, [('phasediff', 'in_phase')]), - (delta, prep_fmap, [('out_value', 'delta_TE')]), + (inputnode, prep_fmap, [("phasediff", "in_phase")]), + (delta, prep_fmap, [("out_value", "delta_TE")]), # Remove second empty volume (prep_fmap, fslroi, [("out_fieldmap", "in_file")]), # Output (fslroi, outputnode, [("roi_file", "out_fmap")]), - (inputnode, outputnode, [("magnitude1", "out_mag")]) + (inputnode, outputnode, [("magnitude1", "out_mag")]), ] ) return wf @@ -100,22 +120,24 @@ def init_phdiff_wf(bet_mag_frac): def get_metadata(in_file, bids_dir): from bids import BIDSLayout + layout = BIDSLayout(bids_dir, validate=False) out_dict = layout.get_metadata(in_file) return out_dict + def delta_te(in_values, te1=None, te2=None): """Read :math:`\Delta_\text{TE}` from BIDS metadata dict""" if isinstance(in_values, float): te2 = in_values - te1 = 0. + te1 = 0.0 if isinstance(in_values, dict): - te1 = in_values.get('EchoTime1') - te2 = in_values.get('EchoTime2') + te1 = in_values.get("EchoTime1") + te2 = in_values.get("EchoTime2") if not all((te1, te2)): - te2 = in_values.get('EchoTimeDifference') + te2 = in_values.get("EchoTimeDifference") te1 = 0 if isinstance(in_values, list): @@ -127,13 +149,17 @@ def delta_te(in_values, te1=None, te2=None): # For convienience if both are missing we should give one error about them if te1 is None and te2 is None: - raise RuntimeError('EchoTime1 and EchoTime2 metadata fields not found. ' - 'Please consult the BIDS specification.') + raise RuntimeError( + "EchoTime1 and EchoTime2 metadata fields not found. " + "Please consult the BIDS specification." + ) if te1 is None: raise RuntimeError( - 'EchoTime1 metadata field not found. Please consult the BIDS specification.') + "EchoTime1 metadata field not found. Please consult the BIDS specification." + ) if te2 is None: raise RuntimeError( - 'EchoTime2 metadata field not found. Please consult the BIDS specification.') + "EchoTime2 metadata field not found. Please consult the BIDS specification." + ) - return 1000*abs(float(te2) - float(te1)) + return 1000 * abs(float(te2) - float(te1)) From cff00f95533a2bf583af2d08009a3bee5e47abd4 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Tue, 9 Jul 2019 06:26:39 -0400 Subject: [PATCH 057/156] roll back nipype version in requirements --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 29ffc900..38c66853 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ Click>=6.0 dipy==0.16.0 -nipype==1.2.1 +nipype==1.2.0 pandas==0.24.2 parse==1.12.0 tqdm==4.32.1 From be1066b6b42ed8efcc3d6a0408540819514b51be Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Tue, 9 Jul 2019 06:27:41 -0400 Subject: [PATCH 058/156] add testing jupyter notebooks --- notebooks/acq_param_testing.ipynb | 125 ++++ notebooks/fmap_testing.ipynb | 618 ++++++++++++++++++++ notebooks/make_qc.ipynb | 395 +++++++++++++ notebooks/slice_timing.ipynb | 939 ++++++++++++++++++++++++++++++ notebooks/test_wf.ipynb | 348 +++++++++++ 5 files changed, 2425 insertions(+) create mode 100644 notebooks/acq_param_testing.ipynb create mode 100644 notebooks/fmap_testing.ipynb create mode 100644 notebooks/make_qc.ipynb create mode 100644 notebooks/slice_timing.ipynb create mode 100644 notebooks/test_wf.ipynb diff --git a/notebooks/acq_param_testing.ipynb b/notebooks/acq_param_testing.ipynb new file mode 100644 index 00000000..73a54760 --- /dev/null +++ b/notebooks/acq_param_testing.ipynb @@ -0,0 +1,125 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "base_dir = \"/projects/mjoseph/pipelines/testing/data\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- GE/Philips: EchoTrainLength it will be Echo train length/PE steps \n", + "- SIEMENS: EPIFactor \n", + "acquisition direction A>>P or P>>A" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "total_readout_time = 1 / PE bandwidth \n", + "effective echo spacing = 1 / (PE bandwidth * (ETL - 1) * acc factor)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- echo spacing = .53ms\n", + "- number of in plane lines in the reconstructed epi image = 64\n", + "- acceleration factor = 2\n", + "- number of in plane reference lines = 24\n", + "\n", + "My questions: \n", + "What do I have to enter as the echo spacing for b0 field unwarping with \n", + "feat/fugue?\n", + "(a) .53ms/2 (echo_spacing*#lines_in_images / acceleration factor) \n", + "(b) .53*64/(32+24) (echo_spacing*#lines_in_images/(#lines_in_images + \n", + "#reference_lines))\n", + "(c) something else ..." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "from bids import BIDSLayout\n", + "\n", + "layout = BIDSLayout(base_dir)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "dwi_file = \"{}/sub-CMP0178/ses-01/dwi/sub-CMP0178_ses-01_acq-singleshell_dwi.nii.gz\"" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "metadata = layout.get_metadata(dwi_file)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{}" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "metadata" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "dmriprep_venv", + "language": "python", + "name": "dmriprep_venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebooks/fmap_testing.ipynb b/notebooks/fmap_testing.ipynb new file mode 100644 index 00000000..ff9defb2 --- /dev/null +++ b/notebooks/fmap_testing.ipynb @@ -0,0 +1,618 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "https://fmriprep.readthedocs.io/en/0.5.0/sdc/estimation.html" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "from bids import BIDSLayout" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "base_dir = os.path.abspath(\"data\")\n", + "output_dir = os.path.abspath(\"outputs\")\n", + "\n", + "layout = BIDSLayout(base_dir)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "def get_fmaps(dwi_file):\n", + " dwi_metadata = layout.get_metadata(dwi_file)\n", + " fmaps = []\n", + " fmaps = layout.get_fieldmap(dwi_file, return_list=True)\n", + " if not fmaps:\n", + " print(\"No fmaps found\")\n", + " for fmap in fmaps:\n", + " if fmap[\"suffix\"] == \"phase\":\n", + " fmap[\"metadata\"] = {}\n", + " fmap[\"metadata\"][\"phase1\"] = layout.get_metadata(fmap[\"phase1\"])\n", + " fmap[\"metadata\"][\"phase2\"] = layout.get_metadata(fmap[\"phase2\"])\n", + " else:\n", + " fmap[\"metadata\"] = layout.get_metadata(fmap[fmap[\"suffix\"]])\n", + " \n", + " FMAP_PRIORITY = {\"epi\": 0, \"fieldmap\": 1, \"phasediff\": 2, \"phase\": 3, \"syn\": 4}\n", + " \n", + " fmaps.sort(key=lambda fmap: FMAP_PRIORITY[fmap[\"suffix\"]])\n", + " \n", + " return fmaps" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Topup" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/Michael/.pyenv/versions/dwi_venv/lib/python3.7/site-packages/bids/layout/layout.py:659: UserWarning: In pybids 0.9.0, the 'extensions' filter was deprecated in favor of 'extension'. The former will stop working in 0.11.0.\n", + " warnings.warn(\"In pybids 0.9.0, the 'extensions' filter was \"\n" + ] + } + ], + "source": [ + "subject_id = \"NDARAJ366ZFA\"\n", + "dwi_files = layout.get(subject=subject_id, datatype=\"dwi\", suffix=\"dwi\", extensions=[\".nii\", \".nii.gz\"], return_type=\"filename\")" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "for dwi_file in dwi_files:\n", + " dwi_metadata = layout.get_metadata(dwi_file)\n", + " fmaps = get_fmaps(dwi_file)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [], + "source": [ + "fmap = fmaps[0]\n", + "if fmap[\"suffix\"] == \"epi\":\n", + " epi_fmaps = [(fmap_['epi'], fmap_['metadata'][\"PhaseEncodingDirection\"]) for fmap_ in fmaps if fmap_['suffix'] == 'epi']" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [], + "source": [ + "dwi_file_pe = dwi_metadata[\"PhaseEncodingDirection\"]\n", + "\n", + "usable_fieldmaps_matching_pe = []\n", + "usable_fieldmaps_opposite_pe = []\n", + "\n", + "for fmap, pe_dir in epi_fmaps:\n", + " if pe_dir == dwi_file_pe:\n", + " usable_fieldmaps_matching_pe.append(fmap)\n", + " elif pe_dir[0] == dwi_file_pe[0]:\n", + " usable_fieldmaps_opposite_pe.append(fmap)\n", + " \n", + "if not usable_fieldmaps_opposite_pe:\n", + " print(\"Can't use topup\")" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [], + "source": [ + "from nipype.pipeline import engine as pe\n", + "from nipype.interfaces import fsl, utility as niu\n", + "\n", + "def init_topup_wf(epi_fmaps):\n", + " \n", + " PE_DIRECTION_MAPPING = {\n", + " \"i\" : \"x\",\n", + " \"i-\": \"x-\",\n", + " \"j\" : \"y\",\n", + " \"j-\" : \"y-\",\n", + " \"k\" : \"z\",\n", + " \"k-\" : \"z-\"\n", + " }\n", + " \n", + " epi_file = epi_fmaps[0][0]\n", + " epi_dir = PE_DIRECTION_MAPPING[epi_fmaps[0][1]]\n", + " altepi_file = epi_fmaps[1][0]\n", + " altepi_dir = PE_DIRECTION_MAPPING[epi_fmaps[1][1]]\n", + " \n", + " wf = pe.Workflow(name=\"topup_wf\")\n", + " \n", + " inputnode = pe.Node(\n", + " niu.IdentityInterface(fields=[\"epi_file\", \"altepi_file\"]),\n", + " name=\"inputnode\") \n", + " inputnode.epi_file = epi_file\n", + " inputnode.altepi_file = altepi_file\n", + " \n", + " outputnode = pe.Node(\n", + " niu.IdentityInterface(fields=[\"out_fmap\"]),\n", + " name=\"outputnode\")\n", + " \n", + " list_merge = pe.Node(niu.Merge(numinputs=2), name=\"list_merge\")\n", + " \n", + " merge = pe.Node(fsl.Merge(dimension=\"t\"), name=\"mergeAPPA\")\n", + " \n", + " topup = pe.Node(fsl.TOPUP(), name=\"topup\")\n", + " topup.inputs.encoding_direction = [epi_dir, altepi_dir]\n", + " topup.inputs.readout_times = [0.05, 0.05]\n", + " \n", + " wf.connect(\n", + " [\n", + " (\n", + " inputnode,\n", + " list_merge,\n", + " [(\"epi_file\", \"in1\"), (\"altepi_file\", \"in2\")]\n", + " ),\n", + " (list_merge, merge, [(\"out\", \"in_files\")]),\n", + " (merge, topup, [(\"merged_file\", \"in_file\")]),\n", + " (topup, outputnode, [(\"out_field\", \"out_fmap\")]),\n", + " ]\n", + " )\n", + " \n", + " return wf" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "190708-19:55:05,845 nipype.workflow INFO:\n", + "\t Workflow topup_wf settings: ['check', 'execution', 'logging', 'monitoring']\n", + "190708-19:55:05,931 nipype.workflow INFO:\n", + "\t Running serially.\n", + "190708-19:55:05,933 nipype.workflow INFO:\n", + "\t [Node] Setting-up \"topup_wf.list_merge\" in \"/Users/Michael/projects/pipelines/dmripreproc/notebooks/outputs/topup_wf/list_merge\".\n", + "190708-19:55:05,943 nipype.workflow INFO:\n", + "\t [Node] Running \"list_merge\" (\"nipype.interfaces.utility.base.Merge\")\n", + "190708-19:55:06,71 nipype.workflow INFO:\n", + "\t [Node] Finished \"topup_wf.list_merge\".\n", + "190708-19:55:06,73 nipype.workflow INFO:\n", + "\t [Node] Setting-up \"topup_wf.mergeAPPA\" in \"/Users/Michael/projects/pipelines/dmripreproc/notebooks/outputs/topup_wf/mergeAPPA\".\n", + "190708-19:55:06,80 nipype.workflow WARNING:\n", + "\t [Node] Error on \"topup_wf.mergeAPPA\" (/Users/Michael/projects/pipelines/dmripreproc/notebooks/outputs/topup_wf/mergeAPPA)\n", + "190708-19:55:06,86 nipype.workflow ERROR:\n", + "\t Node mergeAPPA failed to run on host Michaels-iMac.local.\n", + "190708-19:55:06,209 nipype.workflow ERROR:\n", + "\t Saving crash info to /Users/Michael/projects/pipelines/dmripreproc/notebooks/crash-20190708-195506-Michael-mergeAPPA-ce285b5c-427d-40a6-81d3-1f832935f05c.pklz\n", + "Traceback (most recent call last):\n", + " File \"/Users/Michael/.pyenv/versions/dwi_venv/lib/python3.7/site-packages/nipype/pipeline/plugins/linear.py\", line 48, in run\n", + " node.run(updatehash=updatehash)\n", + " File \"/Users/Michael/.pyenv/versions/dwi_venv/lib/python3.7/site-packages/nipype/pipeline/engine/nodes.py\", line 472, in run\n", + " result = self._run_interface(execute=True)\n", + " File \"/Users/Michael/.pyenv/versions/dwi_venv/lib/python3.7/site-packages/nipype/pipeline/engine/nodes.py\", line 563, in _run_interface\n", + " return self._run_command(execute)\n", + " File \"/Users/Michael/.pyenv/versions/dwi_venv/lib/python3.7/site-packages/nipype/pipeline/engine/nodes.py\", line 631, in _run_command\n", + " cmd = self._interface.cmdline\n", + " File \"/Users/Michael/.pyenv/versions/dwi_venv/lib/python3.7/site-packages/nipype/interfaces/base/core.py\", line 674, in cmdline\n", + " allargs = [self._cmd_prefix + self.cmd] + self._parse_inputs()\n", + " File \"/Users/Michael/.pyenv/versions/dwi_venv/lib/python3.7/site-packages/nipype/interfaces/base/core.py\", line 916, in _parse_inputs\n", + " value = self._filename_from_source(name)\n", + " File \"/Users/Michael/.pyenv/versions/dwi_venv/lib/python3.7/site-packages/nipype/interfaces/base/core.py\", line 845, in _filename_from_source\n", + " source = source[0]\n", + "IndexError: list index out of range\n", + "\n", + "190708-19:55:06,211 nipype.workflow INFO:\n", + "\t ***********************************\n", + "190708-19:55:06,212 nipype.workflow ERROR:\n", + "\t could not run node: topup_wf.mergeAPPA\n", + "190708-19:55:06,213 nipype.workflow INFO:\n", + "\t crashfile: /Users/Michael/projects/pipelines/dmripreproc/notebooks/crash-20190708-195506-Michael-mergeAPPA-ce285b5c-427d-40a6-81d3-1f832935f05c.pklz\n", + "190708-19:55:06,214 nipype.workflow INFO:\n", + "\t ***********************************\n" + ] + }, + { + "ename": "RuntimeError", + "evalue": "Workflow did not execute cleanly. Check log for details", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------\u001b[0m", + "\u001b[0;31mRuntimeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mtopup_wf\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0minit_topup_wf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mepi_fmaps\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0mtopup_wf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbase_dir\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0moutput_dir\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mtopup_wf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m~/.pyenv/versions/dwi_venv/lib/python3.7/site-packages/nipype/pipeline/engine/workflows.py\u001b[0m in \u001b[0;36mrun\u001b[0;34m(self, plugin, plugin_args, updatehash)\u001b[0m\n\u001b[1;32m 597\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mstr2bool\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconfig\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'execution'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'create_report'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 598\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_write_report_info\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbase_dir\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mexecgraph\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 599\u001b[0;31m \u001b[0mrunner\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mexecgraph\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mupdatehash\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mupdatehash\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mconfig\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconfig\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 600\u001b[0m \u001b[0mdatestr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdatetime\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mutcnow\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstrftime\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'%Y%m%dT%H%M%S'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 601\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mstr2bool\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconfig\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'execution'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'write_provenance'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/.pyenv/versions/dwi_venv/lib/python3.7/site-packages/nipype/pipeline/plugins/linear.py\u001b[0m in \u001b[0;36mrun\u001b[0;34m(self, graph, config, updatehash)\u001b[0m\n\u001b[1;32m 69\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 70\u001b[0m \u001b[0mos\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mchdir\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mold_wd\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# Return wherever we were before\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 71\u001b[0;31m \u001b[0mreport_nodes_not_run\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnotrun\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m~/.pyenv/versions/dwi_venv/lib/python3.7/site-packages/nipype/pipeline/plugins/tools.py\u001b[0m in \u001b[0;36mreport_nodes_not_run\u001b[0;34m(notrun)\u001b[0m\n\u001b[1;32m 80\u001b[0m \u001b[0mlogger\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdebug\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msubnode\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_id\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 81\u001b[0m \u001b[0mlogger\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minfo\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"***********************************\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 82\u001b[0;31m raise RuntimeError(('Workflow did not execute cleanly. '\n\u001b[0m\u001b[1;32m 83\u001b[0m 'Check log for details'))\n\u001b[1;32m 84\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mRuntimeError\u001b[0m: Workflow did not execute cleanly. Check log for details" + ] + } + ], + "source": [ + "topup_wf = init_topup_wf(epi_fmaps)\n", + "topup_wf.base_dir = output_dir\n", + "topup_wf.run()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "if using single dwi image, both dwi images or synthetic b0, must register both images before fslmerge" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Topup using dwi image" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Topup using synthetic b0" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Phasediff" + ] + }, + { + "cell_type": "code", + "execution_count": 116, + "metadata": {}, + "outputs": [], + "source": [ + "subject_id = \"CMP0178\"\n", + "dwi_files = layout.get(subject=subject_id, datatype=\"dwi\", suffix=\"dwi\", extensions=[\".nii\", \".nii.gz\"], return_type=\"filename\")" + ] + }, + { + "cell_type": "code", + "execution_count": 117, + "metadata": {}, + "outputs": [], + "source": [ + "for dwi_file in dwi_files:\n", + " fmaps = get_fmaps(dwi_file)" + ] + }, + { + "cell_type": "code", + "execution_count": 118, + "metadata": {}, + "outputs": [], + "source": [ + "fmap = fmaps[0]\n", + "if fmap[\"suffix\"] == \"phasediff\":\n", + " phasediff_file = fmap[\"phasediff\"]\n", + " phasediff_metadata = fmap[\"metadata\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Phase" + ] + }, + { + "cell_type": "code", + "execution_count": 119, + "metadata": {}, + "outputs": [], + "source": [ + "subject_id = \"ZHP0130\"\n", + "dwi_files = layout.get(subject=subject_id, datatype=\"dwi\", suffix=\"dwi\", extensions=[\".nii\", \".nii.gz\"], return_type=\"filename\")" + ] + }, + { + "cell_type": "code", + "execution_count": 120, + "metadata": {}, + "outputs": [], + "source": [ + "for dwi_file in dwi_files:\n", + " fmaps = get_fmaps(dwi_file)" + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'phase1': {'AcquisitionMatrixPE': 64, 'AcquisitionNumber': 1, 'AcquisitionTime': '06:05:0.000000', 'BaseResolution': 64, 'BodyPartExamined': 'BRAIN', 'ConsistencyInfo': 'N4_VE11B_LATEST_20150530', 'ConversionSoftware': 'dcm2niix', 'ConversionSoftwareVersion': 'v1.0.20180622 (JP2:OpenJPEG) (JP-LS:CharLS) GCC5.5.0', 'DeviceSerialNumber': '66106', 'DwellTime': 1.61e-05, 'EchoTime': 0.0065, 'FlipAngle': 60, 'ImageOrientationPatientDICOM': [0.998672, 0.040619, 0.0316926, -0.0416821, 0.998565, 0.0336378], 'ImageType': ['ORIGINAL', 'PRIMARY', 'P', 'ND'], 'InPlanePhaseEncodingDirectionDICOM': 'COL', 'InstitutionAddress': 'Community_Drive_300_Manhasset_NYC_US_11030', 'InstitutionalDepartmentName': 'RADIOLOGY', 'IntendedFor': ['ses-01/dwi/sub-ZHP0130_ses-01_acq-ZHP601000_run-01_dwi.nii.gz'], 'MRAcquisitionType': '2D', 'MagneticFieldStrength': 3, 'Manufacturer': 'Siemens', 'ManufacturersModelName': 'Prisma', 'Modality': 'MR', 'PartialFourier': 1, 'PatientPosition': 'HFS', 'PercentPhaseFOV': 100, 'PhaseEncodingDirection': 'j-', 'PhaseEncodingSteps': 64, 'PhaseResolution': 1, 'PixelBandwidth': 485, 'ProcedureStepDescription': 'MR_BRAIN', 'ProtocolName': 'field_map_TE=6.5ms', 'PulseSequenceDetails': '%SiemensSeq%_gre', 'ReceiveCoilActiveElements': 'HC1-7', 'ReceiveCoilName': 'HeadNeck_64', 'ReconMatrixPE': 64, 'RepetitionTime': 1.06, 'SAR': 0.122978, 'ScanOptions': 'FS', 'ScanningSequence': 'GR', 'SequenceName': '_fl2d1', 'SequenceVariant': 'SP', 'SeriesDescription': 'field_map_TE=6.5ms', 'SeriesNumber': 17, 'ShimSetting': [-1322, -10948, 554, 258, -169, -237, 5, -7], 'SliceThickness': 4, 'SoftwareVersions': 'syngo_MR_E11', 'SpacingBetweenSlices': 4, 'StationName': 'MHMRSIEM3T', 'TxRefAmp': 286.233}, 'phase2': {'AcquisitionMatrixPE': 64, 'AcquisitionNumber': 1, 'AcquisitionTime': '06:05:0.000000', 'BaseResolution': 64, 'BodyPartExamined': 'BRAIN', 'ConsistencyInfo': 'N4_VE11B_LATEST_20150530', 'ConversionSoftware': 'dcm2niix', 'ConversionSoftwareVersion': 'v1.0.20180622 (JP2:OpenJPEG) (JP-LS:CharLS) GCC5.5.0', 'DeviceSerialNumber': '66106', 'DwellTime': 1.61e-05, 'EchoTime': 0.0085, 'FlipAngle': 60, 'ImageOrientationPatientDICOM': [0.998672, 0.040619, 0.0316926, -0.0416821, 0.998565, 0.0336378], 'ImageType': ['ORIGINAL', 'PRIMARY', 'P', 'ND'], 'InPlanePhaseEncodingDirectionDICOM': 'COL', 'InstitutionAddress': 'Community_Drive_300_Manhasset_NYC_US_11030', 'InstitutionalDepartmentName': 'RADIOLOGY', 'IntendedFor': ['ses-01/dwi/sub-ZHP0130_ses-01_acq-ZHP601000_run-01_dwi.nii.gz'], 'MRAcquisitionType': '2D', 'MagneticFieldStrength': 3, 'Manufacturer': 'Siemens', 'ManufacturersModelName': 'Prisma', 'Modality': 'MR', 'PartialFourier': 1, 'PatientPosition': 'HFS', 'PercentPhaseFOV': 100, 'PhaseEncodingDirection': 'j-', 'PhaseEncodingSteps': 64, 'PhaseResolution': 1, 'PixelBandwidth': 485, 'ProcedureStepDescription': 'MR_BRAIN', 'ProtocolName': 'field_map_TE=8.5ms', 'PulseSequenceDetails': '%SiemensSeq%_gre', 'ReceiveCoilActiveElements': 'HC1-7', 'ReceiveCoilName': 'HeadNeck_64', 'ReconMatrixPE': 64, 'RepetitionTime': 1.06, 'SAR': 0.122978, 'ScanOptions': 'FS', 'ScanningSequence': 'GR', 'SequenceName': '_fl2d1', 'SequenceVariant': 'SP', 'SeriesDescription': 'field_map_TE=8.5ms', 'SeriesNumber': 19, 'ShimSetting': [-1322, -10948, 554, 258, -169, -237, 5, -7], 'SliceThickness': 4, 'SoftwareVersions': 'syngo_MR_E11', 'SpacingBetweenSlices': 4, 'StationName': 'MHMRSIEM3T', 'TxRefAmp': 286.233}}\n" + ] + } + ], + "source": [ + "fmap = fmaps[0]\n", + "if fmap[\"suffix\"] == \"phase\":\n", + " phase1_file = fmap" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Fieldmap" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [], + "source": [ + "def to_rads(in_file, out_file):\n", + " from math import pi\n", + " import nibabel as nib\n", + " \n", + " fmap_img = nib.load(in_file)\n", + " fmap_data = fmap_img.get_data()\n", + " \n", + " fmap_range = max(abs(fmap_data.min()), fmap_data.max())\n", + " \n", + " fmap_data = fmap_data * (pi / fmap_range)\n", + " \n", + " out_img = nib.Nifti1Image(fmap_data, fmap_img.affine, fmap_img.header)\n", + " out_img.set_data_dtype('float32')\n", + " out_img.to_filename(out_file)\n", + " \n", + " return out_file, fmap_range" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [], + "source": [ + "def to_hz(in_file, range_hz, out_file):\n", + " from math import pi\n", + " import nibabel as nib\n", + " \n", + " fmap_img = nib.load(in_file)\n", + " fmap_data = fmap_img.get_data()\n", + " \n", + " fmap_data = fmap_data * (range_hz / pi)\n", + " out_img = nib.Nifti1Image(fmap_data, fmap_img.affine, fmap_img.header)\n", + " out_img.set_data_dtype('float32')\n", + " out_img.to_filename(out_file)\n", + " \n", + " return out_file" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [], + "source": [ + "fmap_file = \"{}/sub-CMH0171/ses-01/fmap/sub-CMH0171_ses-01_run-01_fieldmap.nii.gz\".format(base_dir)\n", + "\n", + "out_file, fmap_range = to_rads(fmap_file, fmap_file.replace('.nii.gz', '_rad.nii.gz'))\n", + "\n", + "new_out_file = to_hz(out_file, fmap_range, out_file.replace('_rad.nii.gz', '_hz.nii.gz'))" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2048.7444\n" + ] + } + ], + "source": [ + "fmap_img = nib.load(fmap_file)\n", + "fmap_data = fmap_img.get_data()\n", + "\n", + "fmap_range = max(abs(fmap_data.min()), fmap_data.max())\n", + "print(fmap_range)" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3.1415927\n" + ] + } + ], + "source": [ + "fmap_img = nib.load(out_file)\n", + "fmap_data = fmap_img.get_data()\n", + "\n", + "fmap_range = max(abs(fmap_data.min()), fmap_data.max())\n", + "print(fmap_range)" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2048.7446\n" + ] + } + ], + "source": [ + "fmap_img = nib.load(new_out_file)\n", + "fmap_data = fmap_img.get_data()\n", + "\n", + "fmap_range = max(abs(fmap_data.min()), fmap_data.max())\n", + "print(fmap_range)" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAekAAADJCAYAAAAHFcoVAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjAsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+17YcXAAAcr0lEQVR4nO3df2yV1R3H8U8ttFxaaIu2Fg200zkB5zCIIzNu4kLC8A8l1sUf66DbAsMl28jifmTsDyBjuiwh2f4Aw4yK6TKnYOpYAsn4w2Rb3KIb1Dk0Mhd0TMpkUPoTrLb7A8/Tc2/PfZ7n/uo99973KyFen/v8OLfP6T093+d7zqmamJiYEAAA8M5lxS4AAABwo5EGAMBTNNIAAHiKRhoAAE/RSAMA4CkaaQAAPEUjDQCAp2ikAQDwFI00AACeopEGAMBTNNIAAHiKRhoAAE/RSAMA4CkaaQAAPEUjDQCAp2ikAQDwFI00AACeopEGAMBTNNIAAHiKRhoAAE/NKHYBAMAX3d3dxS4CKlRnZ6dzOz1pAAA8RSMNAICnaKQBAPAUjTQAAJ6ikQYAwFM00gAAeIpGGgCQk+7uboavFQjjpAEgTzo6OpRIJKZsHx0d1f79+4tQounx9ttvF7sIBZXuvtoKdY/pSQNAnqT7Io/6goff4ty/Qt1jGmkAyKP29nYdPny42MVAnrW3tyuRSKi+vl6tra3q6urS0NBQwa9LIw0AQAwHDhzQ0NCQjh49qiNHjuiRRx4p+DVppAEAyEBra6tWr16to0ePFvxaNNIAAGTg5MmTOnjwoD7+8Y8X/Fo00gAAxLB27VrNmTNHCxYsUEtLi7Zt21bwa9JIAwAQQ09PjwYHB/Xiiy/qjTfe0JkzZwp+TRppAAAycPvtt6urq0sPP/xwwa/FZCYAkGdjY2O6cOFC8P8zZvBVW242b96s9vZ29fb2aunSpQW7Dj1pAMizO++8U4lEIvi3devWYhcJedbc3Kx169Zp+/btBb0Of94BQB6dOHHCuZ25rUub677u3r274NelJw0AgKdopAEgT0ZHRzPajtIQ5/4V6h6HhrsJzwBAfOW80lUlK+Z9pScNAICnaKQBAPAUjTQAAJ6ikQYAwFOMk87ByMhI8Hp8fFySdPr0aR08eFANDQ3q7OyUJM2aNStpH0kaHh6eco7GxsaClxl+6u/v1/79+zU6Oqo1a9ZIujT1oKSkheXff/99SdLExESwraqqSpL04YcfJv3XZvaxj7XPYWbEunjxYu4fBkDeFLSR7ujoUCKRcL43OjpatpmQfX19On/+fLGLUVBh91Yq7/tbKCdPntTY2FixiwHAIwUNdycSCbW3t+vw4cPO91C6zL1NJBKqr69Xa2ururq6gl4f9xcAcke4O8LAwICk5FB0dXX1lG3GvHnzVFdXp/Hx8SDE+MEHH0zZz4TAzX+lyXD44OBgsG3OnDm5foSCOnDggFatWqW+vj6tXr1ajzzyiHbs2FHsYnnHftRx2WWX/ja2F1248sorVVNTo5qaGi1evFjSpYhMKhOitsPXqe+5wt3mmum4wtymzHb9rampCT0PgPwicQx50draqtWrV+vo0aPFLgoAlI2K70mfO3cueN3Q0CApuYczd+7cKceYnoqrNyMlJ+TY+0fta87n6j3b5zh79qykS6uw+OLkyZM6ePCgPv/5zxe7KNPKvn+uXqbpwdr7pdYP6dL9Te0JZ1pv7N566n5h57LPYddp89r1uVpaWoLX//3vf0PPDSB79KSRk7Vr12rOnDlasGCBWlpatG3btmIXCQDKBo00ctLT06PBwUG9+OKLeuONN3TmzJliFwkAykZFhbvthKzZs2dLSk7+CgsburalkzoONSzJxw5buq7hCkNefvnlkpJDmGbI17x582KXM59uv/12dXV16eGHH1ZPT09RylAM9qMRw76nrjBzunufGpp2hcVdx4ZdP4wrZO5iv2fC966kNjsJ0oznBpCbaWmkx8bGdOHChcmLzpjh/HJDadu8ebPa29vV29urpUuXFrs4AFDypiXcfeeddyqRSAT/tm7dOh2XxTRrbm7WunXrtH379mIXBQDKQsG7sydOnCj0JSKZKTjtcJwJ9dkhZlfIL24YMPW8qa/D9gsTFma3z1FfXy8peQpJEwK/+uqrY10rU657u3v37oJcqxSEZVdn87jE9f9x600cYXXbZpfd9TlMCNye4taUc+bMmbkUEah4JI4BAOApGmkAADxV0HD36Oho6AIbhWCyTu2s7bCQmyt86FoxKCq0nem2TLlCnq5tdkKeyfS2J2xpamrKuSxS+L0175cLE8a165H57FH3OywEnk6m9SWfIXCXqLofFsq3s9tNWDxumB1AgRtpVkEqX9xbACg8wt0AAHiqLAYr/+9//wtemzC3K2TtEhXKM+9nE4JMDZXn47z2semum6q2tjZ4bULf+Qp7lys7U9n8vE0GveSecCRuJn6m70cdm0tdSj1H3OtmE7IOe3QEwI2eNAAAnqKRBgDAUyUd7h4YGJA0OQ+3zQ7VuRa8jxtyyzREZ4e5XWUJ25YPUeU0oW97eUF72cFKZ7LS7YlvTP2yM5U/+OCDWOfLNIztqj9Rx+QzfJyv84fNMW4/FnD9bgKYxG8IAACeopEGAMBTJRfutrNuzeQScefcjpqkJNOJS4otbma665i5c+cG2/r7+yUlTwBTqVyTwoyNjUlyLzvpOjbue3Eei6RyZXIXaj7vfGeLu84bNhc4AHrSAAB4i0YaAABPlUy4e3BwUFL00nflMFFC3PnE072fqbq6OknJjxJcGfOVwIS57eztTCePiRL2qMUnmdYpJjgB8o+eNAAAnvK6J20SmqTkqS1TZTrm2T4m7jafhE0DGTcRznWMPTbY9KoroUdtxttLkz8DO6GpUHUgl154Lgo1jWg2SBIDwvEbAgCAp2ikAQDwlNfh7qjpPl3ijpmO816xxB2nnUsymf2eCTnaocdKCHObxyn2mGjXuN1M61Q2+8WV7SMZ17jq6aj7cR+1AHCjJw0AgKdopAEA8JSX4e73339fklRdXR1sc4UhDR/HmOZL3M8WFiLPJlzrGi9cbsxns8P85mfmyu6OypzPZTrZ6VzJyrVCVabXz+bYTK/hyvwO+x4AyhE9aQAAPEUjDQCAp7wMd5upP10hx0Ivel+I8+Uil4kncvkc5no1NTXBNvMYopQdP348eN3U1CQpuZ6Z1/maZKNQIwvyfd7p+L2Km1Xu+tmbY+xHYFGrkgHlgJ40AACeopEGAMBTXoa7TRgrmwk8CqXY83nnkjkcxZXRbF77FPrPhzlz5gSvXZO4uDKfXeJm04eFePNdp+Ku1hX3EUrcEHjc/fIR3rfPceLECUnSggULsj4v4Dt60gAAeIpGGgAAT3kZ7jbihrbzNZlCLhmzhco+j/u5Mw19u/aPu62U2aHtsNDtdGTTT+e81lEh+LifN5d5v/NxrF3OtrY2SUxwgvJGTxoAAE/RSAMA4Ckvw93TGb7K97zDVVVVzkzUbOUymYmRS5a8PXd3KWd6//Wvf5Uktbe3T3nPrm9msozpmNzDxa4/ceXjUUc+zht36dSorHbXhDKuiUvM5Dr2hDtAuaEnDQCAp2ikAQDwlJfhbpd8TwqRy2QLhVqazyWXTNw45416f2xsLNhWymHFa665RpK/mcC5PCZJ3d++t66s6LB7n00oPN9Z7YbrXtnXZe5uVAJ60gAAeMrLnnSmf+lHjQHNdPxzoRKEsukNhyXbZDOtY9yfrWuq0FLrSb/yyivB60984hOS4v/M8lUHwnrHuYyTDru3UT1p1zni9rjj/i65PnfYymJxf95xIwIovHvuuSd4vWfPnqRtzz//fFHKVI7oSQMA4CkaaQAAPOVluNuMzbUXeHeJO+7S9V5Y+DHfoc5M37NlurJSVLJN3BDhjBkz0p6vVCxatCh4bY/3DpNLElRYiDcq3J1pIlamj07sfcw9zSXBzFUW1+d2rTDmOjab3+Gw8DkK77333pN06Z6MjY1pYmIi2HbrrbdKki5evBjsb+YqQGao5QAAeMrLnjQAYPqsWrUqeH3+/HlJUktLS7BtaGhI0uQsb1JydMpEZ8w28/+uZFM74YwEs2heNtIm1GoLC+Pma8xz3GOiQphmu/kcmWZZ25810wxf1/tRIXDDtUJU1CMHn7lC3Nlk/cetX2ab6+fo2j91v9R6Y0Rl3Ydl+4eFse1xxuZ89nnDMsNdPx+7roTVTdd+2YSuCXejElDLAQDwlJc9aQBA4d1yyy2SpOHh4WDbrFmzJEkDAwPBNpMAli7ZMHWGQleU8zOf+YykyXC6JK1cuTLp/JL00ksv5fKRyo6XjbQJh7meebjEDXtFZeKGnS+bsHjY+cJCk64Qsys7N+754mb22uU0+9llKeXQt/lsrlW9ouqPq664jnU9Bon72CAspByV2Z96rKt+2NtMmNv+WZhtrhB4VLjbfDb7Gq5HVq6wvOtnb87n+oxMBYpK42UjDQDIryVLlkhK/sPIJILZfzi5cjnMH0fpOkvm+LChjq5OgymLfV7Tu3/55ZfTnquS8EwaAABPedOTtv8CM3/d2aEt89qVOWsz70dNjhB2bNyweLqwd5wwathqXplmg6fbzxUiND9nVwjcVaa6urpY1/VRY2Nj8HpkZERS/IzvqPudab1w9R7SHZtpvUnt3bgytO06YH6/7BXOzGtXWNwVYrbLaELb9nCbsNEDrt9hOzxuXrsmQrHLDFQCbxppAED2brjhhuD1zJkzJcXPZbHHPxuunIB0M9KlC3fb5zDXt//oM/kHrnIuW7Zsyn6JRCLYdsUVV0gq/7HWhLsBAPCUNz3pCxcuBK9NSMuVaerKprVDZXEnmTDsrNu48yzHDXGm/pUYFQZ1bQtbetP++YSFQV0hTDts6AoBm9Cl/Rd2bW3tlP1KxenTpyVJTU1NwTbX/Q4LyUZlcsfNVHbtd9lll6XNBI+qN6mhd1e4277HZriLPezF1AdXvbC3mfPZv3Omd+PqqdmfxfW++VmYnp80Wc/MUCBpsj6+8847U86BS1y9YZurN5zLZEmufVMfjUR9V5r94y7batfZU6dOxS5fKaMnDQCAp7zpSQNAse3YsaPYRcia/bw2LPmwED3pN998U5K0cePGpO3ZTM8c9r5rP3vSlVK1ZcuWtO9500jX19cHr0+cOJF2Pzt8ZkJkrokVbK4QZti2XMPdqRmrYROxuBIrbJmGqFzs85rQpf14wWQ+29vspI1y8LGPfUxS8i+0ud92nQmbuMQW9qUR9Qglqp6l1mFXlrN9bOrSrnZdMffUlaH97rvvBq9dGdXmvPa1TEjVrlNmP/tY18IKYT8LO9xtwtxz584NtplGAFOZxwPZLDOay36uyWlS62cmDX2m5TDXsNsOsxBIOfGmkQaAYgvr0fjq6quvluR+rm+Lmjkudb+oDoTdED/00EOSpN27dye9l6+hjOYPUPsPQdfMlL29vVPOU+popAGgzJjISVRiatxwd9yecVjU0HXNuI20+TyuRMRynyrWy0a6vb1dknT8+PFgmwnb2TfJhDlcIXDXX1x2RmpYFm/cSmSz90vN0nWFIVOPk+LPye2a2MWIyqY17GPNdIGVwA6hmkUF7HoRNnVhVIa2OU9UtrhrmzmuqqpqyrzXUXPJp75v33cTOrbDzzfddJMkadeuXcG2c+fOSZJWrFgRbLMngwnzyiuvTLmuCYvPnj072GZ6d1E9pL6+vqT/ApWM7G4AADzlZU/auO6660LfP3PmjKTknpDpSdvbTC8iqiftWoXKNT7bSJdc5JrqMB27xxbW47a59gvL5nStTlTKK1rli2vKU9eSfEZUkphrnHPcbWa73ZPOdJpa1xhmE22aN29esO3VV1+VJN12221TzpGN5cuXZ32sa8w2MuP63XdN/+uSaRKqi71UZZzQczarztnXcr0uZ/SkAQDwlNc9aQBAOFfvNW4vM5OFg8LOm9qTztf469Qy2ZncdjZ7OSvpRtpMsG5PW2hCmPbA/rDwc9hqWOnEHVaQWokKGZ4xSTl2gpBJtnNdN04ovhKZsKvrCyAq+Ss1UTDdfq5wd3V19ZQxw66kqjiJaK4xzOUw4QNQiQh3AwDgKbpTAFBiTBRRci+kEyabIaZxpZYhX+cNm2AlX9fwVVk00nZoctGiRZKSw3uusa/mBsfNfsxkDlrXVIdxj00VlmUcNd7UhMBfe+21jMpRyVzj7MNCzFGhbdd+rrB4dXV1kN2dWga7HpnHFK5trnHag4ODksKn2gXgr7JopAGgkpjhp9Lk8LpspuDMZQIn13CofPSkXZ2quJ2V1tZWSeU1EQ7PpAEA8FTZ9aRNprcdDnSFtl3bssn0dkmXTZ7pcIfU8oUd6wqrInNz5syRNDmtpeSeACZdyFpyZ3xHZYHb4e7U8LVdl81re/SCeaxhpgC1F1cwiy8cO3Ys4pMD8FHZNdIAUElci2nEXUo10xB4lNRORVTOT1hoO2qbYYfYs+lU+a78PhEAAGWi7HrSJtxtz0xjuEKO2fy1GHfZNtfczGHXyvdQgnJfwq0QLr/8cknS2bNnp7znmvvdld2dTQh8xowZUyY5CQuz2yFwE+Y283Tv27cv8nOifJw/f15S8lz05rvJNdLAxVWPox4Fur4H4ySORT12dCWhuXrS5vvN3q8cH/fRkwYAwFM00gAAeKrswt1m7mU77GFCg65wc9xkC5vrWNd5UpcajBvidpUzaoIME/qxw/xMYpK506dPS5rM8pYmH6HEDXfHnfTEFca2J8MJO58t0xmnUJ6Gh4eD12YOf9djlqhEK/Nd4ppvPt13aGqI2hVuN1xhbHubqz6H7WeXaWhoaMr1Sh09aQAAPFV2PWkAqHRmnL+dYOjq3ZreqCvhyk48da3sZy9PaV6nJuza5zXvxe0h29cPSyYrx2FXtrJrpP/9739Lkq677rpgm718o+HKFsx0DdSobO3UyhMV2k4Nj6c7V2r2rzT5GV966SX3B0BGzJzX0mTo2/7CMT97VyjadU+j5gK3hX3pmC8pe3lWw2R3AygfoY30jh07pqsceWfPyBT2pZdLI53Om2++KUnauHFj0vZMh3hFve/aj3WD8y9suF7ce+Xalvre8ePHJU3Wm7gTTaQuxPHee++FlqmStbW1qbOzs9jFAGIru540AOASO+Iye/ZsSe4pb6Nm9wpbec8Od6fOfmZf3xzrSlyzrxUWFrc7XK5oUjkKbaS3bNkyXeUoqBtvvFGS+1mMa33SqNVXoubTNj2hPXv2ZFRO1yQXrkxy14QW5pftT3/6U0bXRHzNzc3Ba9f822GPKaLC3dXV1dq0aZOqqqqCeuPKAk+d8MTe7+WXX87mYwHwWGgj3dHRkRQ2LoTR0VHt37+/oNdA6Sl03aPeodKMjIxM2eaamdGwH5uYWe3soYk204O2czlS/etf/5KU/MduuvNhUmgjXegGerquYZLInn/++WDbsmXLJCWHTEyFdY1fjZpiM+zZZNS46rD9TO/e1bv+y1/+ElqmUlboepHN+e0vrfnz50tKjma4vvDi9q7D6k/U+GemfwXK17Tkrl+8eFFf/epXNXfuXLW2tmrnzp3TcVlUOOodgFI3LYljW7du1fHjx/X222+rr69Pd9xxh5YsWaIvfOEL03F5VCjqHZA9OyydD9dcc01ez1cpIhvpn/3sZ/rzn/+c9PzuW9/6lqqqqvTzn/881kX27t2rp556Sk1NTWpqatKGDRv01FNPTduXpR3mNv72t79ldI7FixcHr8MG0buGxpj9o1aEMfsfPXo0o7KVo7feeku33HKLDh8+rGXLlundd9/V0qVL9dxzz2nlypWxzlHIenfq1Km071111VXBa5PQZ49pd031GjbMyvVoxA5x//3vf8+o7ABKR2S4u7OzU4cOHVJ/f7+kS8/dnnnmGa1bt07f+MY31NjY6Pz3qU99SpJ07tw5nTp1SkuXLg3OuXTpUv3jH/8o0EdCObj22mv105/+VJ2dnRoZGdFXvvIVrV+/XitXrqTeAagYkT3p+fPn63Of+5yee+45bdiwQYcOHdIVV1yhm2++WTfffLN27doVeryZ8LyhoSHY1tDQEJoFCEjShg0bdODAAa1YsUJVVVX67W9/K0natWsX9Q5ARYj1THr9+vXavXu3NmzYoO7ubn35y1+OfQEzVeHAwECQxj8wMFByqfevv/568HrJkiWSwkOU9mpGYWOtWakq3IYNG3TXXXdpz549qq2tjX1cMetdXV1d8NrcZzvcbaSOf063epq9nzlfb29vnksNwEexsrvXrl2rV199Va+99pp+97vf6Utf+pIkadOmTaqvr3f+u+GGGyRJTU1Nmj9/ftKXSm9vb/B+Obr++ut1/fXXF7sYJW9oaEibN2/W1772NW3dulVnz56VVL71jnoDIFWsnvSsWbN077336sEHH9SnP/1pLVy4UJL02GOP6bHHHos8ft26dfrxj3+s5cuX6/Tp0/rlL3+pJ598MreSe+x73/te7Hm6kd63v/1tLV++XI8//rg2btyoTZs26dlnny3bevfd736XegMgSewhWOvXr9fjjz+uJ554IuOLbNu2TQ899JDa2tqUSCT0/e9/v6SHwSxatEiS9M9//nPKe2GLeZC1Hd8LL7ygQ4cOBZnLO3fu1E033aRf/epXQSQnSrHqnVkow2YS2qTwbH/X9KGZjkQAUD5iN9ILFy5UIpFQR0dHxhepra3VE088kVUDj8p099136+677w7+v76+3vlHURjqHYBSF+uZ9Pj4uHbu3Kn7779fc+fOLXSZAACAYvSkh4eHdeWVV6qtrU2HDh0qSCFKdX1XO4QZxyc/+ckClQTZKJV6Z0YToPC6u7uLXQQgSWQjXVdXF4w5BQAA02daFtgAAACZo5EGAMBTNNIAAHiKRhoAAE/RSAMA4CkaaQAAPEUjDQCAp2JPCwoA5a6jo0OJRKLYxQiMjo5q//79xS4GioieNAB8xKcGWvKvPJh+NNIAUCDHjh3T8uXL1dTUpKamJq1atUrHjh0L3l+zZk3Seug1NTW68cYbi1hi+IZGGgAK5KqrrtK+fft09uxZnTlzRnfddZfuv//+4P2DBw9qaGgo+Hfrrbfqi1/8YhFLDN/QSAPAR37zm98k9Wxra2u1cuXKrM/X2Nio9vZ2VVVVaWJiQtXV1WmXXD1x4oT+8Ic/aN26dVlfD+WHxDEA+Mh9992n++67T5I0MDCgFStW6IEHHtCjjz6qRx99NO1x/f39oedtbGzU0NCQxsfHtX37duc+Tz/9tD772c+qvb096/IXS1tbW7GLULZopAEgxfj4uB588EGtXLlSX//61yVJP/jBD7I+X39/v4aHh7V37960DdrTTz+tH/3oR1lfo5hKZdnXUkQjDQAptmzZosHBQf3iF7+Ifcw777yTtPZ36hK/dXV12rRpk5qbm/X666+rpaUleO+Pf/yj+vr6dO+99+ZeeJQVnkkDgOWZZ57Rr3/9a+3bt08zZ86UJP3kJz9Jelad+k+SFi5cmJQE5jI+Pq6RkRH95z//Sdq+d+9e3XPPPcG5AINGGgA+cuTIEX3zm99UT0+Pmpubg+0//OEPkxrg1H/p/P73v9eRI0f04YcfamBgQN/5znfU1NSkxYsXB/uMjo7q2WefVVdXVyE/GkoUjTQAfOSFF17QuXPndNtttwW95DVr1mR9vv7+fj3wwANqaGjQtddeq7feekuHDh3SrFmzgn16enrU2NioO+64Ix8fAWWmamJiYqLYhQAAuHV3dxe7CJgG6ZLv6EkDAOApetIAAHiKnjQAAJ6ikQYAwFM00gAAeIpGGgAAT9FIAwDgKRppAAA8RSMNAICnaKQBAPAUjTQAAJ6ikQYAwFM00gAAeIpGGgAAT9FIAwDgKRppAAA8RSMNAICnaKQBAPAUjTQAAJ6ikQYAwFM00gAAeIpGGgAAT9FIAwDgqf8DR/uDeCgxviIAAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plot_anat(fmap_file)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAekAAADJCAYAAAAHFcoVAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjAsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+17YcXAAAcr0lEQVR4nO3df2yV1R3H8U8ttFxaaIu2Fg200zkB5zCIIzNu4kLC8A8l1sUf66DbAsMl28jifmTsDyBjuiwh2f4Aw4yK6TKnYOpYAsn4w2Rb3KIb1Dk0Mhd0TMpkUPoTrLb7A8/Tc2/PfZ7n/uo99973KyFen/v8OLfP6T093+d7zqmamJiYEAAA8M5lxS4AAABwo5EGAMBTNNIAAHiKRhoAAE/RSAMA4CkaaQAAPEUjDQCAp2ikAQDwFI00AACeopEGAMBTNNIAAHiKRhoAAE/RSAMA4CkaaQAAPEUjDQCAp2ikAQDwFI00AACeopEGAMBTNNIAAHiKRhoAAE/NKHYBAMAX3d3dxS4CKlRnZ6dzOz1pAAA8RSMNAICnaKQBAPAUjTQAAJ6ikQYAwFM00gAAeIpGGgCQk+7uboavFQjjpAEgTzo6OpRIJKZsHx0d1f79+4tQounx9ttvF7sIBZXuvtoKdY/pSQNAnqT7Io/6goff4ty/Qt1jGmkAyKP29nYdPny42MVAnrW3tyuRSKi+vl6tra3q6urS0NBQwa9LIw0AQAwHDhzQ0NCQjh49qiNHjuiRRx4p+DVppAEAyEBra6tWr16to0ePFvxaNNIAAGTg5MmTOnjwoD7+8Y8X/Fo00gAAxLB27VrNmTNHCxYsUEtLi7Zt21bwa9JIAwAQQ09PjwYHB/Xiiy/qjTfe0JkzZwp+TRppAAAycPvtt6urq0sPP/xwwa/FZCYAkGdjY2O6cOFC8P8zZvBVW242b96s9vZ29fb2aunSpQW7Dj1pAMizO++8U4lEIvi3devWYhcJedbc3Kx169Zp+/btBb0Of94BQB6dOHHCuZ25rUub677u3r274NelJw0AgKdopAEgT0ZHRzPajtIQ5/4V6h6HhrsJzwBAfOW80lUlK+Z9pScNAICnaKQBAPAUjTQAAJ6ikQYAwFOMk87ByMhI8Hp8fFySdPr0aR08eFANDQ3q7OyUJM2aNStpH0kaHh6eco7GxsaClxl+6u/v1/79+zU6Oqo1a9ZIujT1oKSkheXff/99SdLExESwraqqSpL04YcfJv3XZvaxj7XPYWbEunjxYu4fBkDeFLSR7ujoUCKRcL43OjpatpmQfX19On/+fLGLUVBh91Yq7/tbKCdPntTY2FixiwHAIwUNdycSCbW3t+vw4cPO91C6zL1NJBKqr69Xa2ururq6gl4f9xcAcke4O8LAwICk5FB0dXX1lG3GvHnzVFdXp/Hx8SDE+MEHH0zZz4TAzX+lyXD44OBgsG3OnDm5foSCOnDggFatWqW+vj6tXr1ajzzyiHbs2FHsYnnHftRx2WWX/ja2F1248sorVVNTo5qaGi1evFjSpYhMKhOitsPXqe+5wt3mmum4wtymzHb9rampCT0PgPwicQx50draqtWrV+vo0aPFLgoAlI2K70mfO3cueN3Q0CApuYczd+7cKceYnoqrNyMlJ+TY+0fta87n6j3b5zh79qykS6uw+OLkyZM6ePCgPv/5zxe7KNPKvn+uXqbpwdr7pdYP6dL9Te0JZ1pv7N566n5h57LPYddp89r1uVpaWoLX//3vf0PPDSB79KSRk7Vr12rOnDlasGCBWlpatG3btmIXCQDKBo00ctLT06PBwUG9+OKLeuONN3TmzJliFwkAykZFhbvthKzZs2dLSk7+CgsburalkzoONSzJxw5buq7hCkNefvnlkpJDmGbI17x582KXM59uv/12dXV16eGHH1ZPT09RylAM9qMRw76nrjBzunufGpp2hcVdx4ZdP4wrZO5iv2fC966kNjsJ0oznBpCbaWmkx8bGdOHChcmLzpjh/HJDadu8ebPa29vV29urpUuXFrs4AFDypiXcfeeddyqRSAT/tm7dOh2XxTRrbm7WunXrtH379mIXBQDKQsG7sydOnCj0JSKZKTjtcJwJ9dkhZlfIL24YMPW8qa/D9gsTFma3z1FfXy8peQpJEwK/+uqrY10rU657u3v37oJcqxSEZVdn87jE9f9x600cYXXbZpfd9TlMCNye4taUc+bMmbkUEah4JI4BAOApGmkAADxV0HD36Oho6AIbhWCyTu2s7bCQmyt86FoxKCq0nem2TLlCnq5tdkKeyfS2J2xpamrKuSxS+L0175cLE8a165H57FH3OywEnk6m9SWfIXCXqLofFsq3s9tNWDxumB1AgRtpVkEqX9xbACg8wt0AAHiqLAYr/+9//wtemzC3K2TtEhXKM+9nE4JMDZXn47z2semum6q2tjZ4bULf+Qp7lys7U9n8vE0GveSecCRuJn6m70cdm0tdSj1H3OtmE7IOe3QEwI2eNAAAnqKRBgDAUyUd7h4YGJA0OQ+3zQ7VuRa8jxtyyzREZ4e5XWUJ25YPUeU0oW97eUF72cFKZ7LS7YlvTP2yM5U/+OCDWOfLNIztqj9Rx+QzfJyv84fNMW4/FnD9bgKYxG8IAACeopEGAMBTJRfutrNuzeQScefcjpqkJNOJS4otbma665i5c+cG2/r7+yUlTwBTqVyTwoyNjUlyLzvpOjbue3Eei6RyZXIXaj7vfGeLu84bNhc4AHrSAAB4i0YaAABPlUy4e3BwUFL00nflMFFC3PnE072fqbq6OknJjxJcGfOVwIS57eztTCePiRL2qMUnmdYpJjgB8o+eNAAAnvK6J20SmqTkqS1TZTrm2T4m7jafhE0DGTcRznWMPTbY9KoroUdtxttLkz8DO6GpUHUgl154Lgo1jWg2SBIDwvEbAgCAp2ikAQDwlNfh7qjpPl3ijpmO816xxB2nnUsymf2eCTnaocdKCHObxyn2mGjXuN1M61Q2+8WV7SMZ17jq6aj7cR+1AHCjJw0AgKdopAEA8JSX4e73339fklRdXR1sc4UhDR/HmOZL3M8WFiLPJlzrGi9cbsxns8P85mfmyu6OypzPZTrZ6VzJyrVCVabXz+bYTK/hyvwO+x4AyhE9aQAAPEUjDQCAp7wMd5upP10hx0Ivel+I8+Uil4kncvkc5no1NTXBNvMYopQdP348eN3U1CQpuZ6Z1/maZKNQIwvyfd7p+L2Km1Xu+tmbY+xHYFGrkgHlgJ40AACeopEGAMBTXoa7TRgrmwk8CqXY83nnkjkcxZXRbF77FPrPhzlz5gSvXZO4uDKfXeJm04eFePNdp+Ku1hX3EUrcEHjc/fIR3rfPceLECUnSggULsj4v4Dt60gAAeIpGGgAAT3kZ7jbihrbzNZlCLhmzhco+j/u5Mw19u/aPu62U2aHtsNDtdGTTT+e81lEh+LifN5d5v/NxrF3OtrY2SUxwgvJGTxoAAE/RSAMA4Ckvw93TGb7K97zDVVVVzkzUbOUymYmRS5a8PXd3KWd6//Wvf5Uktbe3T3nPrm9msozpmNzDxa4/ceXjUUc+zht36dSorHbXhDKuiUvM5Dr2hDtAuaEnDQCAp2ikAQDwlJfhbpd8TwqRy2QLhVqazyWXTNw45416f2xsLNhWymHFa665RpK/mcC5PCZJ3d++t66s6LB7n00oPN9Z7YbrXtnXZe5uVAJ60gAAeMrLnnSmf+lHjQHNdPxzoRKEsukNhyXbZDOtY9yfrWuq0FLrSb/yyivB60984hOS4v/M8lUHwnrHuYyTDru3UT1p1zni9rjj/i65PnfYymJxf95xIwIovHvuuSd4vWfPnqRtzz//fFHKVI7oSQMA4CkaaQAAPOVluNuMzbUXeHeJO+7S9V5Y+DHfoc5M37NlurJSVLJN3BDhjBkz0p6vVCxatCh4bY/3DpNLElRYiDcq3J1pIlamj07sfcw9zSXBzFUW1+d2rTDmOjab3+Gw8DkK77333pN06Z6MjY1pYmIi2HbrrbdKki5evBjsb+YqQGao5QAAeMrLnjQAYPqsWrUqeH3+/HlJUktLS7BtaGhI0uQsb1JydMpEZ8w28/+uZFM74YwEs2heNtIm1GoLC+Pma8xz3GOiQphmu/kcmWZZ25810wxf1/tRIXDDtUJU1CMHn7lC3Nlk/cetX2ab6+fo2j91v9R6Y0Rl3Ydl+4eFse1xxuZ89nnDMsNdPx+7roTVTdd+2YSuCXejElDLAQDwlJc9aQBA4d1yyy2SpOHh4WDbrFmzJEkDAwPBNpMAli7ZMHWGQleU8zOf+YykyXC6JK1cuTLp/JL00ksv5fKRyo6XjbQJh7meebjEDXtFZeKGnS+bsHjY+cJCk64Qsys7N+754mb22uU0+9llKeXQt/lsrlW9ouqPq664jnU9Bon72CAspByV2Z96rKt+2NtMmNv+WZhtrhB4VLjbfDb7Gq5HVq6wvOtnb87n+oxMBYpK42UjDQDIryVLlkhK/sPIJILZfzi5cjnMH0fpOkvm+LChjq5OgymLfV7Tu3/55ZfTnquS8EwaAABPedOTtv8CM3/d2aEt89qVOWsz70dNjhB2bNyweLqwd5wwathqXplmg6fbzxUiND9nVwjcVaa6urpY1/VRY2Nj8HpkZERS/IzvqPudab1w9R7SHZtpvUnt3bgytO06YH6/7BXOzGtXWNwVYrbLaELb9nCbsNEDrt9hOzxuXrsmQrHLDFQCbxppAED2brjhhuD1zJkzJcXPZbHHPxuunIB0M9KlC3fb5zDXt//oM/kHrnIuW7Zsyn6JRCLYdsUVV0gq/7HWhLsBAPCUNz3pCxcuBK9NSMuVaerKprVDZXEnmTDsrNu48yzHDXGm/pUYFQZ1bQtbetP++YSFQV0hTDts6AoBm9Cl/Rd2bW3tlP1KxenTpyVJTU1NwTbX/Q4LyUZlcsfNVHbtd9lll6XNBI+qN6mhd1e4277HZriLPezF1AdXvbC3mfPZv3Omd+PqqdmfxfW++VmYnp80Wc/MUCBpsj6+8847U86BS1y9YZurN5zLZEmufVMfjUR9V5r94y7batfZU6dOxS5fKaMnDQCAp7zpSQNAse3YsaPYRcia/bw2LPmwED3pN998U5K0cePGpO3ZTM8c9r5rP3vSlVK1ZcuWtO9500jX19cHr0+cOJF2Pzt8ZkJkrokVbK4QZti2XMPdqRmrYROxuBIrbJmGqFzs85rQpf14wWQ+29vspI1y8LGPfUxS8i+0ud92nQmbuMQW9qUR9Qglqp6l1mFXlrN9bOrSrnZdMffUlaH97rvvBq9dGdXmvPa1TEjVrlNmP/tY18IKYT8LO9xtwtxz584NtplGAFOZxwPZLDOay36uyWlS62cmDX2m5TDXsNsOsxBIOfGmkQaAYgvr0fjq6quvluR+rm+Lmjkudb+oDoTdED/00EOSpN27dye9l6+hjOYPUPsPQdfMlL29vVPOU+popAGgzJjISVRiatxwd9yecVjU0HXNuI20+TyuRMRynyrWy0a6vb1dknT8+PFgmwnb2TfJhDlcIXDXX1x2RmpYFm/cSmSz90vN0nWFIVOPk+LPye2a2MWIyqY17GPNdIGVwA6hmkUF7HoRNnVhVIa2OU9UtrhrmzmuqqpqyrzXUXPJp75v33cTOrbDzzfddJMkadeuXcG2c+fOSZJWrFgRbLMngwnzyiuvTLmuCYvPnj072GZ6d1E9pL6+vqT/ApWM7G4AADzlZU/auO6660LfP3PmjKTknpDpSdvbTC8iqiftWoXKNT7bSJdc5JrqMB27xxbW47a59gvL5nStTlTKK1rli2vKU9eSfEZUkphrnHPcbWa73ZPOdJpa1xhmE22aN29esO3VV1+VJN12221TzpGN5cuXZ32sa8w2MuP63XdN/+uSaRKqi71UZZzQczarztnXcr0uZ/SkAQDwlNc9aQBAOFfvNW4vM5OFg8LOm9qTztf469Qy2ZncdjZ7OSvpRtpMsG5PW2hCmPbA/rDwc9hqWOnEHVaQWokKGZ4xSTl2gpBJtnNdN04ovhKZsKvrCyAq+Ss1UTDdfq5wd3V19ZQxw66kqjiJaK4xzOUw4QNQiQh3AwDgKbpTAFBiTBRRci+kEyabIaZxpZYhX+cNm2AlX9fwVVk00nZoctGiRZKSw3uusa/mBsfNfsxkDlrXVIdxj00VlmUcNd7UhMBfe+21jMpRyVzj7MNCzFGhbdd+rrB4dXV1kN2dWga7HpnHFK5trnHag4ODksKn2gXgr7JopAGgkpjhp9Lk8LpspuDMZQIn13CofPSkXZ2quJ2V1tZWSeU1EQ7PpAEA8FTZ9aRNprcdDnSFtl3bssn0dkmXTZ7pcIfU8oUd6wqrInNz5syRNDmtpeSeACZdyFpyZ3xHZYHb4e7U8LVdl81re/SCeaxhpgC1F1cwiy8cO3Ys4pMD8FHZNdIAUElci2nEXUo10xB4lNRORVTOT1hoO2qbYYfYs+lU+a78PhEAAGWi7HrSJtxtz0xjuEKO2fy1GHfZNtfczGHXyvdQgnJfwq0QLr/8cknS2bNnp7znmvvdld2dTQh8xowZUyY5CQuz2yFwE+Y283Tv27cv8nOifJw/f15S8lz05rvJNdLAxVWPox4Fur4H4ySORT12dCWhuXrS5vvN3q8cH/fRkwYAwFM00gAAeKrswt1m7mU77GFCg65wc9xkC5vrWNd5UpcajBvidpUzaoIME/qxw/xMYpK506dPS5rM8pYmH6HEDXfHnfTEFca2J8MJO58t0xmnUJ6Gh4eD12YOf9djlqhEK/Nd4ppvPt13aGqI2hVuN1xhbHubqz6H7WeXaWhoaMr1Sh09aQAAPFV2PWkAqHRmnL+dYOjq3ZreqCvhyk48da3sZy9PaV6nJuza5zXvxe0h29cPSyYrx2FXtrJrpP/9739Lkq677rpgm718o+HKFsx0DdSobO3UyhMV2k4Nj6c7V2r2rzT5GV966SX3B0BGzJzX0mTo2/7CMT97VyjadU+j5gK3hX3pmC8pe3lWw2R3AygfoY30jh07pqsceWfPyBT2pZdLI53Om2++KUnauHFj0vZMh3hFve/aj3WD8y9suF7ce+Xalvre8ePHJU3Wm7gTTaQuxPHee++FlqmStbW1qbOzs9jFAGIru540AOASO+Iye/ZsSe4pb6Nm9wpbec8Od6fOfmZf3xzrSlyzrxUWFrc7XK5oUjkKbaS3bNkyXeUoqBtvvFGS+1mMa33SqNVXoubTNj2hPXv2ZFRO1yQXrkxy14QW5pftT3/6U0bXRHzNzc3Ba9f822GPKaLC3dXV1dq0aZOqqqqCeuPKAk+d8MTe7+WXX87mYwHwWGgj3dHRkRQ2LoTR0VHt37+/oNdA6Sl03aPeodKMjIxM2eaamdGwH5uYWe3soYk204O2czlS/etf/5KU/MduuvNhUmgjXegGerquYZLInn/++WDbsmXLJCWHTEyFdY1fjZpiM+zZZNS46rD9TO/e1bv+y1/+ElqmUlboepHN+e0vrfnz50tKjma4vvDi9q7D6k/U+GemfwXK17Tkrl+8eFFf/epXNXfuXLW2tmrnzp3TcVlUOOodgFI3LYljW7du1fHjx/X222+rr69Pd9xxh5YsWaIvfOEL03F5VCjqHZA9OyydD9dcc01ez1cpIhvpn/3sZ/rzn/+c9PzuW9/6lqqqqvTzn/881kX27t2rp556Sk1NTWpqatKGDRv01FNPTduXpR3mNv72t79ldI7FixcHr8MG0buGxpj9o1aEMfsfPXo0o7KVo7feeku33HKLDh8+rGXLlundd9/V0qVL9dxzz2nlypWxzlHIenfq1Km071111VXBa5PQZ49pd031GjbMyvVoxA5x//3vf8+o7ABKR2S4u7OzU4cOHVJ/f7+kS8/dnnnmGa1bt07f+MY31NjY6Pz3qU99SpJ07tw5nTp1SkuXLg3OuXTpUv3jH/8o0EdCObj22mv105/+VJ2dnRoZGdFXvvIVrV+/XitXrqTeAagYkT3p+fPn63Of+5yee+45bdiwQYcOHdIVV1yhm2++WTfffLN27doVeryZ8LyhoSHY1tDQEJoFCEjShg0bdODAAa1YsUJVVVX67W9/K0natWsX9Q5ARYj1THr9+vXavXu3NmzYoO7ubn35y1+OfQEzVeHAwECQxj8wMFByqfevv/568HrJkiWSwkOU9mpGYWOtWakq3IYNG3TXXXdpz549qq2tjX1cMetdXV1d8NrcZzvcbaSOf063epq9nzlfb29vnksNwEexsrvXrl2rV199Va+99pp+97vf6Utf+pIkadOmTaqvr3f+u+GGGyRJTU1Nmj9/ftKXSm9vb/B+Obr++ut1/fXXF7sYJW9oaEibN2/W1772NW3dulVnz56VVL71jnoDIFWsnvSsWbN077336sEHH9SnP/1pLVy4UJL02GOP6bHHHos8ft26dfrxj3+s5cuX6/Tp0/rlL3+pJ598MreSe+x73/te7Hm6kd63v/1tLV++XI8//rg2btyoTZs26dlnny3bevfd736XegMgSewhWOvXr9fjjz+uJ554IuOLbNu2TQ899JDa2tqUSCT0/e9/v6SHwSxatEiS9M9//nPKe2GLeZC1Hd8LL7ygQ4cOBZnLO3fu1E033aRf/epXQSQnSrHqnVkow2YS2qTwbH/X9KGZjkQAUD5iN9ILFy5UIpFQR0dHxhepra3VE088kVUDj8p099136+677w7+v76+3vlHURjqHYBSF+uZ9Pj4uHbu3Kn7779fc+fOLXSZAACAYvSkh4eHdeWVV6qtrU2HDh0qSCFKdX1XO4QZxyc/+ckClQTZKJV6Z0YToPC6u7uLXQQgSWQjXVdXF4w5BQAA02daFtgAAACZo5EGAMBTNNIAAHiKRhoAAE/RSAMA4CkaaQAAPEUjDQCAp2JPCwoA5a6jo0OJRKLYxQiMjo5q//79xS4GioieNAB8xKcGWvKvPJh+NNIAUCDHjh3T8uXL1dTUpKamJq1atUrHjh0L3l+zZk3Seug1NTW68cYbi1hi+IZGGgAK5KqrrtK+fft09uxZnTlzRnfddZfuv//+4P2DBw9qaGgo+Hfrrbfqi1/8YhFLDN/QSAPAR37zm98k9Wxra2u1cuXKrM/X2Nio9vZ2VVVVaWJiQtXV1WmXXD1x4oT+8Ic/aN26dVlfD+WHxDEA+Mh9992n++67T5I0MDCgFStW6IEHHtCjjz6qRx99NO1x/f39oedtbGzU0NCQxsfHtX37duc+Tz/9tD772c+qvb096/IXS1tbW7GLULZopAEgxfj4uB588EGtXLlSX//61yVJP/jBD7I+X39/v4aHh7V37960DdrTTz+tH/3oR1lfo5hKZdnXUkQjDQAptmzZosHBQf3iF7+Ifcw777yTtPZ36hK/dXV12rRpk5qbm/X666+rpaUleO+Pf/yj+vr6dO+99+ZeeJQVnkkDgOWZZ57Rr3/9a+3bt08zZ86UJP3kJz9Jelad+k+SFi5cmJQE5jI+Pq6RkRH95z//Sdq+d+9e3XPPPcG5AINGGgA+cuTIEX3zm99UT0+Pmpubg+0//OEPkxrg1H/p/P73v9eRI0f04YcfamBgQN/5znfU1NSkxYsXB/uMjo7q2WefVVdXVyE/GkoUjTQAfOSFF17QuXPndNtttwW95DVr1mR9vv7+fj3wwANqaGjQtddeq7feekuHDh3SrFmzgn16enrU2NioO+64Ix8fAWWmamJiYqLYhQAAuHV3dxe7CJgG6ZLv6EkDAOApetIAAHiKnjQAAJ6ikQYAwFM00gAAeIpGGgAAT9FIAwDgKRppAAA8RSMNAICnaKQBAPAUjTQAAJ6ikQYAwFM00gAAeIpGGgAAT9FIAwDgKRppAAA8RSMNAICnaKQBAPAUjTQAAJ6ikQYAwFM00gAAeIpGGgAAT9FIAwDgqf8DR/uDeCgxviIAAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plot_anat(out_file)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAekAAADJCAYAAAAHFcoVAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjAsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+17YcXAAAcr0lEQVR4nO3df2yV1R3H8U8ttFxaaIu2Fg200zkB5zCIIzNu4kLC8A8l1sUf66DbAsMl28jifmTsDyBjuiwh2f4Aw4yK6TKnYOpYAsn4w2Rb3KIb1Dk0Mhd0TMpkUPoTrLb7A8/Tc2/PfZ7n/uo99973KyFen/v8OLfP6T093+d7zqmamJiYEAAA8M5lxS4AAABwo5EGAMBTNNIAAHiKRhoAAE/RSAMA4CkaaQAAPEUjDQCAp2ikAQDwFI00AACeopEGAMBTNNIAAHiKRhoAAE/RSAMA4CkaaQAAPEUjDQCAp2ikAQDwFI00AACeopEGAMBTNNIAAHiKRhoAAE/NKHYBAMAX3d3dxS4CKlRnZ6dzOz1pAAA8RSMNAICnaKQBAPAUjTQAAJ6ikQYAwFM00gAAeIpGGgCQk+7uboavFQjjpAEgTzo6OpRIJKZsHx0d1f79+4tQounx9ttvF7sIBZXuvtoKdY/pSQNAnqT7Io/6goff4ty/Qt1jGmkAyKP29nYdPny42MVAnrW3tyuRSKi+vl6tra3q6urS0NBQwa9LIw0AQAwHDhzQ0NCQjh49qiNHjuiRRx4p+DVppAEAyEBra6tWr16to0ePFvxaNNIAAGTg5MmTOnjwoD7+8Y8X/Fo00gAAxLB27VrNmTNHCxYsUEtLi7Zt21bwa9JIAwAQQ09PjwYHB/Xiiy/qjTfe0JkzZwp+TRppAAAycPvtt6urq0sPP/xwwa/FZCYAkGdjY2O6cOFC8P8zZvBVW242b96s9vZ29fb2aunSpQW7Dj1pAMizO++8U4lEIvi3devWYhcJedbc3Kx169Zp+/btBb0Of94BQB6dOHHCuZ25rUub677u3r274NelJw0AgKdopAEgT0ZHRzPajtIQ5/4V6h6HhrsJzwBAfOW80lUlK+Z9pScNAICnaKQBAPAUjTQAAJ6ikQYAwFOMk87ByMhI8Hp8fFySdPr0aR08eFANDQ3q7OyUJM2aNStpH0kaHh6eco7GxsaClxl+6u/v1/79+zU6Oqo1a9ZIujT1oKSkheXff/99SdLExESwraqqSpL04YcfJv3XZvaxj7XPYWbEunjxYu4fBkDeFLSR7ujoUCKRcL43OjpatpmQfX19On/+fLGLUVBh91Yq7/tbKCdPntTY2FixiwHAIwUNdycSCbW3t+vw4cPO91C6zL1NJBKqr69Xa2ururq6gl4f9xcAcke4O8LAwICk5FB0dXX1lG3GvHnzVFdXp/Hx8SDE+MEHH0zZz4TAzX+lyXD44OBgsG3OnDm5foSCOnDggFatWqW+vj6tXr1ajzzyiHbs2FHsYnnHftRx2WWX/ja2F1248sorVVNTo5qaGi1evFjSpYhMKhOitsPXqe+5wt3mmum4wtymzHb9rampCT0PgPwicQx50draqtWrV+vo0aPFLgoAlI2K70mfO3cueN3Q0CApuYczd+7cKceYnoqrNyMlJ+TY+0fta87n6j3b5zh79qykS6uw+OLkyZM6ePCgPv/5zxe7KNPKvn+uXqbpwdr7pdYP6dL9Te0JZ1pv7N566n5h57LPYddp89r1uVpaWoLX//3vf0PPDSB79KSRk7Vr12rOnDlasGCBWlpatG3btmIXCQDKBo00ctLT06PBwUG9+OKLeuONN3TmzJliFwkAykZFhbvthKzZs2dLSk7+CgsburalkzoONSzJxw5buq7hCkNefvnlkpJDmGbI17x582KXM59uv/12dXV16eGHH1ZPT09RylAM9qMRw76nrjBzunufGpp2hcVdx4ZdP4wrZO5iv2fC966kNjsJ0oznBpCbaWmkx8bGdOHChcmLzpjh/HJDadu8ebPa29vV29urpUuXFrs4AFDypiXcfeeddyqRSAT/tm7dOh2XxTRrbm7WunXrtH379mIXBQDKQsG7sydOnCj0JSKZKTjtcJwJ9dkhZlfIL24YMPW8qa/D9gsTFma3z1FfXy8peQpJEwK/+uqrY10rU657u3v37oJcqxSEZVdn87jE9f9x600cYXXbZpfd9TlMCNye4taUc+bMmbkUEah4JI4BAOApGmkAADxV0HD36Oho6AIbhWCyTu2s7bCQmyt86FoxKCq0nem2TLlCnq5tdkKeyfS2J2xpamrKuSxS+L0175cLE8a165H57FH3OywEnk6m9SWfIXCXqLofFsq3s9tNWDxumB1AgRtpVkEqX9xbACg8wt0AAHiqLAYr/+9//wtemzC3K2TtEhXKM+9nE4JMDZXn47z2semum6q2tjZ4bULf+Qp7lys7U9n8vE0GveSecCRuJn6m70cdm0tdSj1H3OtmE7IOe3QEwI2eNAAAnqKRBgDAUyUd7h4YGJA0OQ+3zQ7VuRa8jxtyyzREZ4e5XWUJ25YPUeU0oW97eUF72cFKZ7LS7YlvTP2yM5U/+OCDWOfLNIztqj9Rx+QzfJyv84fNMW4/FnD9bgKYxG8IAACeopEGAMBTJRfutrNuzeQScefcjpqkJNOJS4otbma665i5c+cG2/r7+yUlTwBTqVyTwoyNjUlyLzvpOjbue3Eei6RyZXIXaj7vfGeLu84bNhc4AHrSAAB4i0YaAABPlUy4e3BwUFL00nflMFFC3PnE072fqbq6OknJjxJcGfOVwIS57eztTCePiRL2qMUnmdYpJjgB8o+eNAAAnvK6J20SmqTkqS1TZTrm2T4m7jafhE0DGTcRznWMPTbY9KoroUdtxttLkz8DO6GpUHUgl154Lgo1jWg2SBIDwvEbAgCAp2ikAQDwlNfh7qjpPl3ijpmO816xxB2nnUsymf2eCTnaocdKCHObxyn2mGjXuN1M61Q2+8WV7SMZ17jq6aj7cR+1AHCjJw0AgKdopAEA8JSX4e73339fklRdXR1sc4UhDR/HmOZL3M8WFiLPJlzrGi9cbsxns8P85mfmyu6OypzPZTrZ6VzJyrVCVabXz+bYTK/hyvwO+x4AyhE9aQAAPEUjDQCAp7wMd5upP10hx0Ivel+I8+Uil4kncvkc5no1NTXBNvMYopQdP348eN3U1CQpuZ6Z1/maZKNQIwvyfd7p+L2Km1Xu+tmbY+xHYFGrkgHlgJ40AACeopEGAMBTXoa7TRgrmwk8CqXY83nnkjkcxZXRbF77FPrPhzlz5gSvXZO4uDKfXeJm04eFePNdp+Ku1hX3EUrcEHjc/fIR3rfPceLECUnSggULsj4v4Dt60gAAeIpGGgAAT3kZ7jbihrbzNZlCLhmzhco+j/u5Mw19u/aPu62U2aHtsNDtdGTTT+e81lEh+LifN5d5v/NxrF3OtrY2SUxwgvJGTxoAAE/RSAMA4Ckvw93TGb7K97zDVVVVzkzUbOUymYmRS5a8PXd3KWd6//Wvf5Uktbe3T3nPrm9msozpmNzDxa4/ceXjUUc+zht36dSorHbXhDKuiUvM5Dr2hDtAuaEnDQCAp2ikAQDwlJfhbpd8TwqRy2QLhVqazyWXTNw45416f2xsLNhWymHFa665RpK/mcC5PCZJ3d++t66s6LB7n00oPN9Z7YbrXtnXZe5uVAJ60gAAeMrLnnSmf+lHjQHNdPxzoRKEsukNhyXbZDOtY9yfrWuq0FLrSb/yyivB60984hOS4v/M8lUHwnrHuYyTDru3UT1p1zni9rjj/i65PnfYymJxf95xIwIovHvuuSd4vWfPnqRtzz//fFHKVI7oSQMA4CkaaQAAPOVluNuMzbUXeHeJO+7S9V5Y+DHfoc5M37NlurJSVLJN3BDhjBkz0p6vVCxatCh4bY/3DpNLElRYiDcq3J1pIlamj07sfcw9zSXBzFUW1+d2rTDmOjab3+Gw8DkK77333pN06Z6MjY1pYmIi2HbrrbdKki5evBjsb+YqQGao5QAAeMrLnjQAYPqsWrUqeH3+/HlJUktLS7BtaGhI0uQsb1JydMpEZ8w28/+uZFM74YwEs2heNtIm1GoLC+Pma8xz3GOiQphmu/kcmWZZ25810wxf1/tRIXDDtUJU1CMHn7lC3Nlk/cetX2ab6+fo2j91v9R6Y0Rl3Ydl+4eFse1xxuZ89nnDMsNdPx+7roTVTdd+2YSuCXejElDLAQDwlJc9aQBA4d1yyy2SpOHh4WDbrFmzJEkDAwPBNpMAli7ZMHWGQleU8zOf+YykyXC6JK1cuTLp/JL00ksv5fKRyo6XjbQJh7meebjEDXtFZeKGnS+bsHjY+cJCk64Qsys7N+754mb22uU0+9llKeXQt/lsrlW9ouqPq664jnU9Bon72CAspByV2Z96rKt+2NtMmNv+WZhtrhB4VLjbfDb7Gq5HVq6wvOtnb87n+oxMBYpK42UjDQDIryVLlkhK/sPIJILZfzi5cjnMH0fpOkvm+LChjq5OgymLfV7Tu3/55ZfTnquS8EwaAABPedOTtv8CM3/d2aEt89qVOWsz70dNjhB2bNyweLqwd5wwathqXplmg6fbzxUiND9nVwjcVaa6urpY1/VRY2Nj8HpkZERS/IzvqPudab1w9R7SHZtpvUnt3bgytO06YH6/7BXOzGtXWNwVYrbLaELb9nCbsNEDrt9hOzxuXrsmQrHLDFQCbxppAED2brjhhuD1zJkzJcXPZbHHPxuunIB0M9KlC3fb5zDXt//oM/kHrnIuW7Zsyn6JRCLYdsUVV0gq/7HWhLsBAPCUNz3pCxcuBK9NSMuVaerKprVDZXEnmTDsrNu48yzHDXGm/pUYFQZ1bQtbetP++YSFQV0hTDts6AoBm9Cl/Rd2bW3tlP1KxenTpyVJTU1NwTbX/Q4LyUZlcsfNVHbtd9lll6XNBI+qN6mhd1e4277HZriLPezF1AdXvbC3mfPZv3Omd+PqqdmfxfW++VmYnp80Wc/MUCBpsj6+8847U86BS1y9YZurN5zLZEmufVMfjUR9V5r94y7batfZU6dOxS5fKaMnDQCAp7zpSQNAse3YsaPYRcia/bw2LPmwED3pN998U5K0cePGpO3ZTM8c9r5rP3vSlVK1ZcuWtO9500jX19cHr0+cOJF2Pzt8ZkJkrokVbK4QZti2XMPdqRmrYROxuBIrbJmGqFzs85rQpf14wWQ+29vspI1y8LGPfUxS8i+0ud92nQmbuMQW9qUR9Qglqp6l1mFXlrN9bOrSrnZdMffUlaH97rvvBq9dGdXmvPa1TEjVrlNmP/tY18IKYT8LO9xtwtxz584NtplGAFOZxwPZLDOay36uyWlS62cmDX2m5TDXsNsOsxBIOfGmkQaAYgvr0fjq6quvluR+rm+Lmjkudb+oDoTdED/00EOSpN27dye9l6+hjOYPUPsPQdfMlL29vVPOU+popAGgzJjISVRiatxwd9yecVjU0HXNuI20+TyuRMRynyrWy0a6vb1dknT8+PFgmwnb2TfJhDlcIXDXX1x2RmpYFm/cSmSz90vN0nWFIVOPk+LPye2a2MWIyqY17GPNdIGVwA6hmkUF7HoRNnVhVIa2OU9UtrhrmzmuqqpqyrzXUXPJp75v33cTOrbDzzfddJMkadeuXcG2c+fOSZJWrFgRbLMngwnzyiuvTLmuCYvPnj072GZ6d1E9pL6+vqT/ApWM7G4AADzlZU/auO6660LfP3PmjKTknpDpSdvbTC8iqiftWoXKNT7bSJdc5JrqMB27xxbW47a59gvL5nStTlTKK1rli2vKU9eSfEZUkphrnHPcbWa73ZPOdJpa1xhmE22aN29esO3VV1+VJN12221TzpGN5cuXZ32sa8w2MuP63XdN/+uSaRKqi71UZZzQczarztnXcr0uZ/SkAQDwlNc9aQBAOFfvNW4vM5OFg8LOm9qTztf469Qy2ZncdjZ7OSvpRtpMsG5PW2hCmPbA/rDwc9hqWOnEHVaQWokKGZ4xSTl2gpBJtnNdN04ovhKZsKvrCyAq+Ss1UTDdfq5wd3V19ZQxw66kqjiJaK4xzOUw4QNQiQh3AwDgKbpTAFBiTBRRci+kEyabIaZxpZYhX+cNm2AlX9fwVVk00nZoctGiRZKSw3uusa/mBsfNfsxkDlrXVIdxj00VlmUcNd7UhMBfe+21jMpRyVzj7MNCzFGhbdd+rrB4dXV1kN2dWga7HpnHFK5trnHag4ODksKn2gXgr7JopAGgkpjhp9Lk8LpspuDMZQIn13CofPSkXZ2quJ2V1tZWSeU1EQ7PpAEA8FTZ9aRNprcdDnSFtl3bssn0dkmXTZ7pcIfU8oUd6wqrInNz5syRNDmtpeSeACZdyFpyZ3xHZYHb4e7U8LVdl81re/SCeaxhpgC1F1cwiy8cO3Ys4pMD8FHZNdIAUElci2nEXUo10xB4lNRORVTOT1hoO2qbYYfYs+lU+a78PhEAAGWi7HrSJtxtz0xjuEKO2fy1GHfZNtfczGHXyvdQgnJfwq0QLr/8cknS2bNnp7znmvvdld2dTQh8xowZUyY5CQuz2yFwE+Y283Tv27cv8nOifJw/f15S8lz05rvJNdLAxVWPox4Fur4H4ySORT12dCWhuXrS5vvN3q8cH/fRkwYAwFM00gAAeKrswt1m7mU77GFCg65wc9xkC5vrWNd5UpcajBvidpUzaoIME/qxw/xMYpK506dPS5rM8pYmH6HEDXfHnfTEFca2J8MJO58t0xmnUJ6Gh4eD12YOf9djlqhEK/Nd4ppvPt13aGqI2hVuN1xhbHubqz6H7WeXaWhoaMr1Sh09aQAAPFV2PWkAqHRmnL+dYOjq3ZreqCvhyk48da3sZy9PaV6nJuza5zXvxe0h29cPSyYrx2FXtrJrpP/9739Lkq677rpgm718o+HKFsx0DdSobO3UyhMV2k4Nj6c7V2r2rzT5GV966SX3B0BGzJzX0mTo2/7CMT97VyjadU+j5gK3hX3pmC8pe3lWw2R3AygfoY30jh07pqsceWfPyBT2pZdLI53Om2++KUnauHFj0vZMh3hFve/aj3WD8y9suF7ce+Xalvre8ePHJU3Wm7gTTaQuxPHee++FlqmStbW1qbOzs9jFAGIru540AOASO+Iye/ZsSe4pb6Nm9wpbec8Od6fOfmZf3xzrSlyzrxUWFrc7XK5oUjkKbaS3bNkyXeUoqBtvvFGS+1mMa33SqNVXoubTNj2hPXv2ZFRO1yQXrkxy14QW5pftT3/6U0bXRHzNzc3Ba9f822GPKaLC3dXV1dq0aZOqqqqCeuPKAk+d8MTe7+WXX87mYwHwWGgj3dHRkRQ2LoTR0VHt37+/oNdA6Sl03aPeodKMjIxM2eaamdGwH5uYWe3soYk204O2czlS/etf/5KU/MduuvNhUmgjXegGerquYZLInn/++WDbsmXLJCWHTEyFdY1fjZpiM+zZZNS46rD9TO/e1bv+y1/+ElqmUlboepHN+e0vrfnz50tKjma4vvDi9q7D6k/U+GemfwXK17Tkrl+8eFFf/epXNXfuXLW2tmrnzp3TcVlUOOodgFI3LYljW7du1fHjx/X222+rr69Pd9xxh5YsWaIvfOEL03F5VCjqHZA9OyydD9dcc01ez1cpIhvpn/3sZ/rzn/+c9PzuW9/6lqqqqvTzn/881kX27t2rp556Sk1NTWpqatKGDRv01FNPTduXpR3mNv72t79ldI7FixcHr8MG0buGxpj9o1aEMfsfPXo0o7KVo7feeku33HKLDh8+rGXLlundd9/V0qVL9dxzz2nlypWxzlHIenfq1Km071111VXBa5PQZ49pd031GjbMyvVoxA5x//3vf8+o7ABKR2S4u7OzU4cOHVJ/f7+kS8/dnnnmGa1bt07f+MY31NjY6Pz3qU99SpJ07tw5nTp1SkuXLg3OuXTpUv3jH/8o0EdCObj22mv105/+VJ2dnRoZGdFXvvIVrV+/XitXrqTeAagYkT3p+fPn63Of+5yee+45bdiwQYcOHdIVV1yhm2++WTfffLN27doVeryZ8LyhoSHY1tDQEJoFCEjShg0bdODAAa1YsUJVVVX67W9/K0natWsX9Q5ARYj1THr9+vXavXu3NmzYoO7ubn35y1+OfQEzVeHAwECQxj8wMFByqfevv/568HrJkiWSwkOU9mpGYWOtWakq3IYNG3TXXXdpz549qq2tjX1cMetdXV1d8NrcZzvcbaSOf063epq9nzlfb29vnksNwEexsrvXrl2rV199Va+99pp+97vf6Utf+pIkadOmTaqvr3f+u+GGGyRJTU1Nmj9/ftKXSm9vb/B+Obr++ut1/fXXF7sYJW9oaEibN2/W1772NW3dulVnz56VVL71jnoDIFWsnvSsWbN077336sEHH9SnP/1pLVy4UJL02GOP6bHHHos8ft26dfrxj3+s5cuX6/Tp0/rlL3+pJ598MreSe+x73/te7Hm6kd63v/1tLV++XI8//rg2btyoTZs26dlnny3bevfd736XegMgSewhWOvXr9fjjz+uJ554IuOLbNu2TQ899JDa2tqUSCT0/e9/v6SHwSxatEiS9M9//nPKe2GLeZC1Hd8LL7ygQ4cOBZnLO3fu1E033aRf/epXQSQnSrHqnVkow2YS2qTwbH/X9KGZjkQAUD5iN9ILFy5UIpFQR0dHxhepra3VE088kVUDj8p099136+677w7+v76+3vlHURjqHYBSF+uZ9Pj4uHbu3Kn7779fc+fOLXSZAACAYvSkh4eHdeWVV6qtrU2HDh0qSCFKdX1XO4QZxyc/+ckClQTZKJV6Z0YToPC6u7uLXQQgSWQjXVdXF4w5BQAA02daFtgAAACZo5EGAMBTNNIAAHiKRhoAAE/RSAMA4CkaaQAAPEUjDQCAp2JPCwoA5a6jo0OJRKLYxQiMjo5q//79xS4GioieNAB8xKcGWvKvPJh+NNIAUCDHjh3T8uXL1dTUpKamJq1atUrHjh0L3l+zZk3Seug1NTW68cYbi1hi+IZGGgAK5KqrrtK+fft09uxZnTlzRnfddZfuv//+4P2DBw9qaGgo+Hfrrbfqi1/8YhFLDN/QSAPAR37zm98k9Wxra2u1cuXKrM/X2Nio9vZ2VVVVaWJiQtXV1WmXXD1x4oT+8Ic/aN26dVlfD+WHxDEA+Mh9992n++67T5I0MDCgFStW6IEHHtCjjz6qRx99NO1x/f39oedtbGzU0NCQxsfHtX37duc+Tz/9tD772c+qvb096/IXS1tbW7GLULZopAEgxfj4uB588EGtXLlSX//61yVJP/jBD7I+X39/v4aHh7V37960DdrTTz+tH/3oR1lfo5hKZdnXUkQjDQAptmzZosHBQf3iF7+Ifcw777yTtPZ36hK/dXV12rRpk5qbm/X666+rpaUleO+Pf/yj+vr6dO+99+ZeeJQVnkkDgOWZZ57Rr3/9a+3bt08zZ86UJP3kJz9Jelad+k+SFi5cmJQE5jI+Pq6RkRH95z//Sdq+d+9e3XPPPcG5AINGGgA+cuTIEX3zm99UT0+Pmpubg+0//OEPkxrg1H/p/P73v9eRI0f04YcfamBgQN/5znfU1NSkxYsXB/uMjo7q2WefVVdXVyE/GkoUjTQAfOSFF17QuXPndNtttwW95DVr1mR9vv7+fj3wwANqaGjQtddeq7feekuHDh3SrFmzgn16enrU2NioO+64Ix8fAWWmamJiYqLYhQAAuHV3dxe7CJgG6ZLv6EkDAOApetIAAHiKnjQAAJ6ikQYAwFM00gAAeIpGGgAAT9FIAwDgKRppAAA8RSMNAICnaKQBAPAUjTQAAJ6ikQYAwFM00gAAeIpGGgAAT9FIAwDgKRppAAA8RSMNAICnaKQBAPAUjTQAAJ6ikQYAwFM00gAAeIpGGgAAT9FIAwDgqf8DR/uDeCgxviIAAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plot_anat(new_out_file)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "dwi_venv", + "language": "python", + "name": "dwi_venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.1" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebooks/make_qc.ipynb b/notebooks/make_qc.ipynb new file mode 100644 index 00000000..96691f85 --- /dev/null +++ b/notebooks/make_qc.ipynb @@ -0,0 +1,395 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 67, + "metadata": {}, + "outputs": [], + "source": [ + "from docopt import docopt\n", + "import pandas as pd\n", + "import os\n", + "import tempfile\n", + "import shutil\n", + "from glob import glob\n", + "import sys\n", + "import subprocess as proc\n", + "\n", + "import datman as dm\n", + "import datman.utils" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": {}, + "outputs": [], + "source": [ + "output_dir = '/scratch/smansour/CARTBIND/combined/dmriprep'\n", + "dtifit_dir = '/scratch/mjoseph/test/dtifit'\n", + "QCdir = os.path.join(dtifit_dir, 'QC')\n", + "tmpdirbase = os.path.join(QCdir,'tmp')\n", + "QC_bet_dir = os.path.join(QCdir,'BET')\n", + "QC_V1_dir = os.path.join(QCdir, 'directions')\n", + "QC_FM_dir = os.path.join(QCdir, 'FM')\n", + "QC_Mag_dir = os.path.join(QCdir, 'Mag')\n", + "QC_res_dir = os.path.join(QCdir, 'res')\n", + "QC_SH_dir = os.path.join(QCdir, 'SH')\n", + "\n", + "dir_list = [tmpdirbase, QC_bet_dir, QC_V1_dir, QC_res_dir, QC_SH_dir]" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": {}, + "outputs": [], + "source": [ + "for qc_dir in dir_list:\n", + " if not os.path.exists(qc_dir):\n", + " os.makedirs(qc_dir)" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "grad_out = os.path.join(tmpdirbase,'ramp.gif')\n", + "create_gradient_file(grad_out,'red-yellow')" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": {}, + "outputs": [], + "source": [ + "maskpics = []\n", + "V1pics = []\n", + "Respics = []\n", + "SHpics = []" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": {}, + "outputs": [], + "source": [ + "allFAmaps = glob('{}/sub-*/ses-*/dwi/dtifit__FA*'.format(output_dir))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for FAmap in allFAmaps:\n", + " subid = FAmap.split('/')[-4]\n", + " sesid = FAmap.split('/')[-3]\n", + " tmpdir = os.path.join(tmpdirbase, subid, sesid)\n", + " if not os.path.exists(tmpdir):\n", + " os.makedirs(tmpdir)\n", + " basename = '{}_{}_'.format(subid, sesid)\n", + " pathbase = FAmap.replace('dtifit__FA.nii.gz','')\n", + " pathdir = os.path.dirname(pathbase)\n", + " SHtmp = os.path.join(tmpdir,'SHmap.gif')\n", + " SHpic = os.path.join(QC_SH_dir,basename + 'SH.gif')\n", + " SHpics.append(SHpic)\n", + " SH_overlay(os.path.join(pathdir,'eddy_corrected_noise.nii.gz'), SHtmp, grad_out)\n", + " if os.path.exists(os.path.join(pathdir,'eddy_corrected_noise.nii.gz')):\n", + " gif_gridtoline(SHtmp,SHpic)" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "run(slices /scratch/smansour/CARTBIND/combined/dmriprep/sub-034/ses-01/dwi/sub-034_ses-01_dwi_denoised_unr_avg_b0_brain.nii.gz /scratch/smansour/CARTBIND/combined/dmriprep/sub-034/ses-01/dwi/sub-034_ses-01_dwi_denoised_unr_avg_b0_brain_mask.nii.gz -o /scratch/mjoseph/test/dtifit/QC/tmp/sub-034/ses-01/B0masked.gif) failed with returncode 1. STDERR: b''\n", + "run(convert /scratch/mjoseph/test/dtifit/QC/tmp/sub-034/ses-01/B0masked.gif -resize 384x384 /scratch/mjoseph/test/dtifit/QC/tmp/sub-034/ses-01/B0masked.gif) failed with returncode 1. STDERR: b\"convert: unable to open image `/scratch/mjoseph/test/dtifit/QC/tmp/sub-034/ses-01/B0masked.gif': No such file or directory @ error/blob.c/OpenBlob/2712.\\nconvert: no images defined `/scratch/mjoseph/test/dtifit/QC/tmp/sub-034/ses-01/B0masked.gif' @ error/convert.c/ConvertImageCommand/3210.\\n\"\n", + "run(convert /scratch/mjoseph/test/dtifit/QC/tmp/sub-034/ses-01/B0masked.gif -crop 100x33%+0+0 /scratch/mjoseph/test/dtifit/QC/tmp/sub-034/ses-01/sag.gif) failed with returncode 1. STDERR: b\"convert: unable to open image `/scratch/mjoseph/test/dtifit/QC/tmp/sub-034/ses-01/B0masked.gif': No such file or directory @ error/blob.c/OpenBlob/2712.\\nconvert: no images defined `/scratch/mjoseph/test/dtifit/QC/tmp/sub-034/ses-01/sag.gif' @ error/convert.c/ConvertImageCommand/3210.\\n\"\n", + "run(convert /scratch/mjoseph/test/dtifit/QC/tmp/sub-034/ses-01/B0masked.gif -crop 100x33%+0+128 /scratch/mjoseph/test/dtifit/QC/tmp/sub-034/ses-01/cor.gif) failed with returncode 1. STDERR: b\"convert: unable to open image `/scratch/mjoseph/test/dtifit/QC/tmp/sub-034/ses-01/B0masked.gif': No such file or directory @ error/blob.c/OpenBlob/2712.\\nconvert: no images defined `/scratch/mjoseph/test/dtifit/QC/tmp/sub-034/ses-01/cor.gif' @ error/convert.c/ConvertImageCommand/3210.\\n\"\n", + "run(convert /scratch/mjoseph/test/dtifit/QC/tmp/sub-034/ses-01/B0masked.gif -crop 100x33%+0+256 /scratch/mjoseph/test/dtifit/QC/tmp/sub-034/ses-01/ax.gif) failed with returncode 1. STDERR: b\"convert: unable to open image `/scratch/mjoseph/test/dtifit/QC/tmp/sub-034/ses-01/B0masked.gif': No such file or directory @ error/blob.c/OpenBlob/2712.\\nconvert: no images defined `/scratch/mjoseph/test/dtifit/QC/tmp/sub-034/ses-01/ax.gif' @ error/convert.c/ConvertImageCommand/3210.\\n\"\n", + "run(slices /scratch/smansour/CARTBIND/combined/dmriprep/sub-098/ses-01/dwi/sub-098_ses-01_dwi_denoised_unr_avg_b0_brain.nii.gz /scratch/smansour/CARTBIND/combined/dmriprep/sub-098/ses-01/dwi/sub-098_ses-01_dwi_denoised_unr_avg_b0_brain_mask.nii.gz -o /scratch/mjoseph/test/dtifit/QC/tmp/sub-098/ses-01/B0masked.gif) failed with returncode 1. STDERR: b''\n", + "run(convert /scratch/mjoseph/test/dtifit/QC/tmp/sub-098/ses-01/B0masked.gif -resize 384x384 /scratch/mjoseph/test/dtifit/QC/tmp/sub-098/ses-01/B0masked.gif) failed with returncode 1. STDERR: b\"convert: unable to open image `/scratch/mjoseph/test/dtifit/QC/tmp/sub-098/ses-01/B0masked.gif': No such file or directory @ error/blob.c/OpenBlob/2712.\\nconvert: no images defined `/scratch/mjoseph/test/dtifit/QC/tmp/sub-098/ses-01/B0masked.gif' @ error/convert.c/ConvertImageCommand/3210.\\n\"\n", + "run(convert /scratch/mjoseph/test/dtifit/QC/tmp/sub-098/ses-01/B0masked.gif -crop 100x33%+0+0 /scratch/mjoseph/test/dtifit/QC/tmp/sub-098/ses-01/sag.gif) failed with returncode 1. STDERR: b\"convert: unable to open image `/scratch/mjoseph/test/dtifit/QC/tmp/sub-098/ses-01/B0masked.gif': No such file or directory @ error/blob.c/OpenBlob/2712.\\nconvert: no images defined `/scratch/mjoseph/test/dtifit/QC/tmp/sub-098/ses-01/sag.gif' @ error/convert.c/ConvertImageCommand/3210.\\n\"\n", + "run(convert /scratch/mjoseph/test/dtifit/QC/tmp/sub-098/ses-01/B0masked.gif -crop 100x33%+0+128 /scratch/mjoseph/test/dtifit/QC/tmp/sub-098/ses-01/cor.gif) failed with returncode 1. STDERR: b\"convert: unable to open image `/scratch/mjoseph/test/dtifit/QC/tmp/sub-098/ses-01/B0masked.gif': No such file or directory @ error/blob.c/OpenBlob/2712.\\nconvert: no images defined `/scratch/mjoseph/test/dtifit/QC/tmp/sub-098/ses-01/cor.gif' @ error/convert.c/ConvertImageCommand/3210.\\n\"\n", + "run(convert /scratch/mjoseph/test/dtifit/QC/tmp/sub-098/ses-01/B0masked.gif -crop 100x33%+0+256 /scratch/mjoseph/test/dtifit/QC/tmp/sub-098/ses-01/ax.gif) failed with returncode 1. STDERR: b\"convert: unable to open image `/scratch/mjoseph/test/dtifit/QC/tmp/sub-098/ses-01/B0masked.gif': No such file or directory @ error/blob.c/OpenBlob/2712.\\nconvert: no images defined `/scratch/mjoseph/test/dtifit/QC/tmp/sub-098/ses-01/ax.gif' @ error/convert.c/ConvertImageCommand/3210.\\n\"\n" + ] + } + ], + "source": [ + "for FAmap in allFAmaps:\n", + " subid = FAmap.split('/')[-4]\n", + " sesid = FAmap.split('/')[-3]\n", + " tmpdir = os.path.join(tmpdirbase, subid, sesid)\n", + " if not os.path.exists(tmpdir):\n", + " os.makedirs(tmpdir)\n", + " basename = '{}_{}_'.format(subid, sesid)\n", + " pathbase = FAmap.replace('dtifit__FA.nii.gz','')\n", + " pathdir = os.path.dirname(pathbase)\n", + "\n", + " maskpic = os.path.join(QC_bet_dir,basename + 'b0_bet_mask.gif')\n", + " maskpics.append(maskpic)\n", + " mask_overlay(os.path.join(pathdir,'{}_{}_dwi_denoised_unr_avg_b0_brain.nii.gz'.format(subid, sesid)), \n", + " os.path.join(pathdir,'{}_{}_dwi_denoised_unr_avg_b0_brain_mask.nii.gz'.format(subid, sesid)), maskpic)\n", + " V1pic = os.path.join(QC_V1_dir,basename + 'dtifit_V1.gif')\n", + " V1pics.append(V1pic)\n", + " V1_overlay(FAmap,pathbase + 'dtifit__V1.nii.gz', V1pic)\n", + "\n", + "# SHtmp = os.path.join(tmpdir,'SHmap.gif')\n", + "# SHpic = os.path.join(QC_SH_dir,basename + 'SH.gif')\n", + "# SHpics.append(SHpic)\n", + "# SH_overlay(os.path.join(pathdir,'eddy_corrected_noise.nii.gz'), SHtmp, grad_out)\n", + "# gif_gridtoline(SHtmp,SHpic)" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": {}, + "outputs": [], + "source": [ + "qchtml = open(os.path.join(QCdir,'qc_BET.html'),'w')\n", + "qchtml.write('DTIFIT BET QC page')\n", + "qchtml.write('\\n')\n", + "qchtml.write('

DTIFIT BET QC page

')\n", + "for pic in maskpics:\n", + " relpath = os.path.relpath(pic,QCdir)\n", + " qchtml.write('')\n", + " qchtml.write(' ')\n", + " qchtml.write(relpath + '
\\n')\n", + "qchtml.write('\\n')\n", + "qchtml.close() # you can omit in most cases as the destructor will call it\n", + "\n", + "## write an html page that shows all the V1 pics\n", + "qchtml = open(os.path.join(QCdir,'qc_directions.html'),'w')\n", + "qchtml.write('DTIFIT directions QC page')\n", + "qchtml.write('\\n')\n", + "qchtml.write('

DTIFIT directions QC page

')\n", + "for pic in V1pics:\n", + " relpath = os.path.relpath(pic,QCdir)\n", + " qchtml.write('')\n", + " qchtml.write(' ')\n", + " qchtml.write(relpath + '
\\n')\n", + "qchtml.write('\\n')\n", + "qchtml.close() # you can omit in most cases as the destructor will call it\n", + "\n", + "# write an html page that shows all the SH residual pics\n", + "qchtml = open(os.path.join(QCdir,'qc_SH.html'),'w')\n", + "qchtml.write('SH residual QC page')\n", + "qchtml.write('\\n')\n", + "qchtml.write('

SH residual QC page

')\n", + "for pic in SHpics:\n", + " relpath = os.path.relpath(pic,QCdir)\n", + " qchtml.write('')\n", + " qchtml.write(' ')\n", + " qchtml.write(relpath + '
\\n')\n", + "qchtml.write('\\n')\n", + "qchtml.close() # you can omit in most cases as the destructor will call it\n", + "\n", + "## write an html page that shows all the Res pics\n", + "qchtml = open(os.path.join(QCdir,'qc_res.html'),'w')\n", + "qchtml.write('DTIFIT residual QC page')\n", + "qchtml.write('\\n')\n", + "qchtml.write('

DTIFIT residual QC page

')\n", + "for pic in Respics:\n", + " relpath = os.path.relpath(pic,QCdir)\n", + " qchtml.write('')\n", + " qchtml.write(' ')\n", + " qchtml.write(relpath + '
\\n')\n", + "qchtml.write('\\n')\n", + "qchtml.close() # you can omit in most cases as the destructor will call it" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": {}, + "outputs": [], + "source": [ + "def gif_gridtoline(input_gif,output_gif):\n", + " '''\n", + " uses imagemagick to take a grid from fsl slices and convert to one line (like in slicesdir)\n", + " '''\n", + " dm.utils.run(['convert',input_gif, '-resize', '384x384',input_gif])\n", + " dm.utils.run(['convert', input_gif,\\\n", + " '-crop', '100x33%+0+0', os.path.join(tmpdir,'sag.gif')])\n", + " dm.utils.run(['convert', input_gif,\\\n", + " '-crop', '100x33%+0+128', os.path.join(tmpdir,'cor.gif')])\n", + " dm.utils.run(['convert', input_gif,\\\n", + " '-crop', '100x33%+0+256', os.path.join(tmpdir,'ax.gif')])\n", + " dm.utils.run(['montage', '-mode', 'concatenate', '-tile', '3x1', \\\n", + " os.path.join(tmpdir,'sag.gif'),\\\n", + " os.path.join(tmpdir,'cor.gif'),\\\n", + " os.path.join(tmpdir,'ax.gif'),\\\n", + " os.path.join(output_gif)])" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": {}, + "outputs": [], + "source": [ + "def mask_overlay(background_nii,mask_nii, overlay_gif):\n", + " '''\n", + " use slices from fsl to overlay the mask on the background (both nii)\n", + " then make the grid to a line for easier scrolling during QC\n", + " '''\n", + " dm.utils.run(['slices', background_nii, mask_nii, '-o', os.path.join(tmpdir,'B0masked.gif')])\n", + " gif_gridtoline(os.path.join(tmpdir,'B0masked.gif'),overlay_gif)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "def V1_overlay(background_nii,V1_nii, overlay_gif):\n", + " '''\n", + " use fslsplit to split the V1 image and take pictures of each direction\n", + " use slices from fsl to get the background and V1 picks (both nii)\n", + " recolor the V1 image using imagemagick\n", + " then make the grid to a line for easier scrolling during QC\n", + " '''\n", + " dm.utils.run(['slices',background_nii,'-o',os.path.join(tmpdir,\"background.gif\")])\n", + " dm.utils.run(['fslmaths',background_nii,'-thr','0.15','-bin',os.path.join(tmpdir,'FAmask.nii.gz')])\n", + " dm.utils.run(['fslsplit', V1_nii, os.path.join(tmpdir,\"V1\")])\n", + " for axis in ['0000','0001','0002']:\n", + " dm.utils.run(['fslmaths',os.path.join(tmpdir,'V1'+axis+'.nii.gz'), '-abs', \\\n", + " '-mul', os.path.join(tmpdir,'FAmask.nii.gz'), os.path.join(tmpdir,'V1'+axis+'abs.nii.gz')])\n", + " dm.utils.run(['slices',os.path.join(tmpdir,'V1'+axis+'abs.nii.gz'),'-o',os.path.join(tmpdir,'V1'+axis+'abs.gif')])\n", + " # docmd(['convert', os.path.join(tmpdir,'V1'+axis+'abs.gif'),\\\n", + " # '-fuzz', '15%', '-transparent', 'black', os.path.join(tmpdir,'V1'+axis+'set.gif')])\n", + " dm.utils.run(['convert', os.path.join(tmpdir,'V10000abs.gif'),\\\n", + " os.path.join(tmpdir,'V10001abs.gif'), os.path.join(tmpdir,'V10002abs.gif'),\\\n", + " '-set', 'colorspace', 'RGB', '-combine', '-set', 'colorspace', 'sRGB',\\\n", + " os.path.join(tmpdir,'dirmap.gif')])\n", + " gif_gridtoline(os.path.join(tmpdir,'dirmap.gif'),overlay_gif)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "def SSE_overlay(sse,out,grad):\n", + " '''\n", + " Arguments:\n", + " sse Full path to SSE file\n", + " out Full path to output\n", + " grad Full path to gradient look-up map\n", + "\n", + " Steps:\n", + " 1. Clever/Hacky thresholding so maximum intensity is 2\n", + " 2. Generate slices\n", + " 3. Use gradient map to color greyscale image\n", + " 4. Background filling with 0 fuzziness to prevent leakage\n", + " '''\n", + " slice_out = out.replace('.nii.gz','.gif')\n", + " cmd1 = 'fslmaths {} -sub 3 -mul -1 -thr 0 -mul -1 -add 3 {}'.format(sse,out)\n", + " cmd2 = 'slices {} -o {}'.format(out, slice_out)\n", + " cmd3 = 'convert {} {} -clut {}'.format(slice_out, grad, slice_out)\n", + " cmd4 = 'convert {} -fill black -draw \"color 0,0 floodfill\" {}'.format(slice_out,slice_out)\n", + " cmdlist = [cmd1, cmd2, cmd3, cmd4]\n", + " outputs = [call(c) for c in cmdlist]\n", + " return" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": {}, + "outputs": [], + "source": [ + "def SH_overlay(SH,out,grad):\n", + " '''\n", + " Arguments:\n", + " SH Full path to SSE file\n", + " out Full path to output\n", + " grad Full path to gradient look-up map\n", + "\n", + " Steps:\n", + " 1. Clever/Hacky thresholding so maximum intensity is 2\n", + " 2. Generate slices\n", + " 3. Use gradient map to color greyscale image\n", + " 4. Background filling with 0 fuzziness to prevent leakage\n", + " '''\n", + " slice_out = out.replace('.nii.gz','.gif')\n", + " cmd1 = 'fslmaths {} -sub 0.3 -mul -1 -thr 0 -mul -1 -add 0.3 {}'.format(SH,out)\n", + " cmd2 = 'slices {} -o {}'.format(out, slice_out)\n", + " cmd3 = 'convert {} {} -clut {}'.format(slice_out, grad, slice_out)\n", + " cmd4 = 'convert {} -fill black -draw \"color 0,0 floodfill\" {}'.format(slice_out,slice_out)\n", + " cmdlist = [cmd1, cmd2, cmd3, cmd4]\n", + " outputs = [call(c) for c in cmdlist]\n", + " return" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [], + "source": [ + "def create_gradient_file(output,color):\n", + " '''\n", + " Arguments:\n", + " output Full path to output file\n", + " color String argument of Image-Magick 'color:color'\n", + " '''\n", + "\n", + " cmd = 'convert -size 10x20 gradient:{} {}'.format(color,output)\n", + " call(cmd)\n", + " return" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "def call(cmd):\n", + " p = proc.Popen(cmd,shell=True,stdin=proc.PIPE, stderr=proc.PIPE)\n", + " std, err = p.communicate()\n", + "\n", + " if p.returncode:\n", + " print('{} failed with error {}'.format(cmd,err))\n", + " return" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "def get_sse(sub):\n", + " sse = '{}_dtifit_sse.nii.gz'.format(sub)\n", + " return os.path.join(dtifitdir,sub,sse)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tigrlab_venv", + "language": "python", + "name": "tigrlab_venv" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebooks/slice_timing.ipynb b/notebooks/slice_timing.ipynb new file mode 100644 index 00000000..fdda1ad4 --- /dev/null +++ b/notebooks/slice_timing.ipynb @@ -0,0 +1,939 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 89, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import json\n", + "from glob import glob\n", + "\n", + "from collections import defaultdict\n", + "\n", + "import nibabel as nib" + ] + }, + { + "cell_type": "code", + "execution_count": 120, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/archive/data/SPINS/data/nii/SPN01_CMH_0001_01/SPN01_CMH_0001_01_01_DTI60-1000_20_Ax-DTI-60plus5.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0002_01/SPN01_CMH_0002_01_02_DTI60-1000_05_Ax-DTI-60plus5.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0004_01/SPN01_CMH_0004_01_02_DTI60-1000_05_Ax-DTI-60plus5.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0005_01/SPN01_CMH_0005_01_01_DTI60-1000_22_Ax-DTI-60plus5.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0007_01/SPN01_CMH_0007_01_01_DTI60-1000_17_Ax-DTI-60plus5.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0008_01/SPN01_CMH_0008_01_01_DTI60-1000_19_Ax-DTI-60plus5.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0009_01/SPN01_CMH_0009_01_01_DTI60-1000_21_Ax-DTI-60plus5.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0011_01/SPN01_CMH_0011_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0012_01/SPN01_CMH_0012_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0013_01/SPN01_CMH_0013_01_01_DTI60-1000_18_Ax-DTI-60plus5.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0014_01/SPN01_CMH_0014_01_01_DTI60-1000_15_Ax-DTI-60plus5.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0015_01/SPN01_CMH_0015_01_01_DTI60-1000_15_Ax-DTI-60plus5.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0016_01/SPN01_CMH_0016_01_01_DTI60-1000_15_Ax-DTI-60plus5.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0017_01/SPN01_CMH_0017_01_01_DTI60-1000_16_Ax-DTI-60plus5.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0020_01/SPN01_CMH_0020_01_01_DTI60-1000_15_Ax-DTI-60plus5.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0021_01/SPN01_CMH_0021_01_01_DTI60-1000_15_Ax-DTI-60plus5.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0023_01/SPN01_CMH_0023_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0024_01/SPN01_CMH_0024_01_01_DTI60-1000_16_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0026_01/SPN01_CMH_0026_01_01_DTI60-1000_17_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0027_01/SPN01_CMH_0027_01_01_DTI60-1000_18_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0028_01/SPN01_CMH_0028_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0029_01/SPN01_CMH_0029_01_01_DTI60-1000_20_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0030_01/SPN01_CMH_0030_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0031_01/SPN01_CMH_0031_01_01_DTI60-1000_19_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0033_01/SPN01_CMH_0033_01_01_DTI60-1000_17_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0034_01/SPN01_CMH_0034_01_01_DTI60-1000_18_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0036_01/SPN01_CMH_0036_01_01_DTI60-1000_18_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0038_01/SPN01_CMH_0038_01_01_DTI60-1000_18_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0039_01/SPN01_CMH_0039_01_01_DTI60-1000_17_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0040_01/SPN01_CMH_0040_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0041_01/SPN01_CMH_0041_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0042_01/SPN01_CMH_0042_01_01_DTI60-1000_16_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0043_01/SPN01_CMH_0043_01_01_DTI60-1000_17_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0044_01/SPN01_CMH_0044_01_01_DTI60-1000_16_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0046_01/SPN01_CMH_0046_01_01_DTI60-1000_16_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0047_01/SPN01_CMH_0047_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0048_01/SPN01_CMH_0048_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0050_01/SPN01_CMH_0050_01_01_DTI60-1000_17_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0051_01/SPN01_CMH_0051_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0052_01/SPN01_CMH_0052_01_01_DTI60-1000_16_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0054_01/SPN01_CMH_0054_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0055_01/SPN01_CMH_0055_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0056_01/SPN01_CMH_0056_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0057_01/SPN01_CMH_0057_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0058_01/SPN01_CMH_0058_01_01_DTI60-1000_18_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0059_01/SPN01_CMH_0059_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0061_01/SPN01_CMH_0061_01_01_DTI60-1000_16_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0062_01/SPN01_CMH_0062_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0063_01/SPN01_CMH_0063_01_01_DTI60-1000_17_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0065_01/SPN01_CMH_0065_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0066_01/SPN01_CMH_0066_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0067_01/SPN01_CMH_0067_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0068_01/SPN01_CMH_0068_01_01_DTI60-1000_16_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0069_01/SPN01_CMH_0069_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0070_01/SPN01_CMH_0070_01_01_DTI60-1000_22_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0072_01/SPN01_CMH_0072_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0073_01/SPN01_CMH_0073_01_01_DTI60-1000_16_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0075_01/SPN01_CMH_0075_01_01_DTI60-1000_16_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0076_01/SPN01_CMH_0076_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0079_01/SPN01_CMH_0079_01_01_DTI60-1000_17_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0080_01/SPN01_CMH_0080_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0086_01/SPN01_CMH_0086_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0087_01/SPN01_CMH_0087_01_01_DTI60-1000_16_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0091_01/SPN01_CMH_0091_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0093_01/SPN01_CMH_0093_01_01_DTI60-1000_17_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0094_01/SPN01_CMH_0094_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0095_01/SPN01_CMH_0095_01_01_DTI60-1000_06_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0096_01/SPN01_CMH_0096_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0097_01/SPN01_CMH_0097_01_01_DTI60-1000_16_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0100_01/SPN01_CMH_0100_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0101_01/SPN01_CMH_0101_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0103_01/SPN01_CMH_0103_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0104_01/SPN01_CMH_0104_01_02_DTI60-1000_05_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0105_01/SPN01_CMH_0105_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0106_01/SPN01_CMH_0106_01_01_DTI60-1000_12_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0107_01/SPN01_CMH_0107_01_01_DTI60-1000_12_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0109_01/SPN01_CMH_0109_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0110_01/SPN01_CMH_0110_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0111_01/SPN01_CMH_0111_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0113_01/SPN01_CMH_0113_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0114_01/SPN01_CMH_0114_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0117_01/SPN01_CMH_0117_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0119_01/SPN01_CMH_0119_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0120_01/SPN01_CMH_0120_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0123_01/SPN01_CMH_0123_01_01_DTI60-1000_18_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0125_01/SPN01_CMH_0125_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0126_01/SPN01_CMH_0126_01_01_DTI60-1000_16_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0128_01/SPN01_CMH_0128_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0129_01/SPN01_CMH_0129_01_01_DTI60-1000_08_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0130_01/SPN01_CMH_0130_01_01_DTI60-1000_16_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0131_01/SPN01_CMH_0131_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0133_01/SPN01_CMH_0133_01_01_DTI60-1000_20_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0134_01/SPN01_CMH_0134_01_01_DTI60-1000_16_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0135_01/SPN01_CMH_0135_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0136_01/SPN01_CMH_0136_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0137_01/SPN01_CMH_0137_01_01_DTI60-1000_16_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0139_01/SPN01_CMH_0139_01_01_DTI60-1000_16_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0140_01/SPN01_CMH_0140_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0142_01/SPN01_CMH_0142_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0143_01/SPN01_CMH_0143_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0144_01/SPN01_CMH_0144_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0145_01/SPN01_CMH_0145_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0146_01/SPN01_CMH_0146_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0147_01/SPN01_CMH_0147_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0148_01/SPN01_CMH_0148_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0149_01/SPN01_CMH_0149_01_01_DTI60-1000_16_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0150_01/SPN01_CMH_0150_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0151_01/SPN01_CMH_0151_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0152_01/SPN01_CMH_0152_01_01_DTI60-1000_16_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0153_01/SPN01_CMH_0153_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0155_01/SPN01_CMH_0155_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0156_01/SPN01_CMH_0156_01_01_DTI60-1000_16_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0157_01/SPN01_CMH_0157_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0158_01/SPN01_CMH_0158_01_01_DTI60-1000_16_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0159_01/SPN01_CMH_0159_01_01_DTI60-1000_16_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0160_01/SPN01_CMH_0160_01_01_DTI60-1000_16_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0162_01/SPN01_CMH_0162_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0164_01/SPN01_CMH_0164_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0165_01/SPN01_CMH_0165_01_01_DTI60-1000_18_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0167_01/SPN01_CMH_0167_01_01_DTI60-1000_08_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0168_01/SPN01_CMH_0168_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0169_01/SPN01_CMH_0169_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0170_01/SPN01_CMH_0170_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0171_01/SPN01_CMH_0171_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0176_01/SPN01_CMH_0176_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0180_01/SPN01_CMH_0180_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0181_01/SPN01_CMH_0181_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0184_01/SPN01_CMH_0184_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0191_01/SPN01_CMH_0191_01_01_DTI60-1000_16_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0194_01/SPN01_CMH_0194_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0196_01/SPN01_CMH_0196_01_01_DTI60-1000_16_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0197_01/SPN01_CMH_0197_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0198_01/SPN01_CMH_0198_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0200_01/SPN01_CMH_0200_01_01_DTI60-1000_16_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_P001_01/SPN01_CMH_P001_01_01_DTI60-1000_17_Ax-DTI-60plus5.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_P001_02/SPN01_CMH_P001_02_01_DTI60-1000_08_Ax-DTI-60plus5-20iso.json\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/archive/data/SPINS/data/nii/SPN01_CMH_P001_03/SPN01_CMH_P001_03_01_DTI60-1000_10_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_P002_01/SPN01_CMH_P002_01_01_DTI60-1000_16_Ax-DTI-60plus5.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_P002_02/SPN01_CMH_P002_02_01_DTI60-1000_08_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_P002_03/SPN01_CMH_P002_03_01_DTI60-1000_10_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_P003_01/SPN01_CMH_P003_01_01_DTI60-1000_16_Ax-DTI-60plus5.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_P003_02/SPN01_CMH_P003_02_01_DTI60-1000_08_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_P003_03/SPN01_CMH_P003_03_01_DTI60-1000_10_Ax-DTI-60plus5-20iso.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMP_0172_01/SPN01_CMP_0172_01_01_DTI60-1000_17_ep2d-diff-4scan-trace-p2-2mm.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMP_0175_01/SPN01_CMP_0175_01_01_DTI60-1000_17_ep2d-diff-4scan-trace-p2-2mm.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMP_0178_01/SPN01_CMP_0178_01_01_DTI60-1000_17_ep2d-diff-4scan-trace-p2-2mm.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMP_0179_01/SPN01_CMP_0179_01_01_DTI60-1000_18_ep2d-diff-4scan-trace-p2-2mm.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMP_0180_02/SPN01_CMP_0180_02_01_DTI60-1000_17_ep2d-diff-4scan-trace-p2-2mm.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMP_0182_02/SPN01_CMP_0182_02_01_DTI60-1000_17_ep2d-diff-4scan-trace-p2-2mm.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMP_0183_01/SPN01_CMP_0183_01_01_DTI60-1000_18_ep2d-diff-4scan-trace-p2-2mm.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMP_0185_01/SPN01_CMP_0185_01_01_DTI60-1000_18_ep2d-diff-4scan-trace-p2-2mm.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMP_0186_01/SPN01_CMP_0186_01_01_DTI60-1000_18_ep2d-diff-4scan-trace-p2-2mm.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMP_0187_01/SPN01_CMP_0187_01_01_DTI60-1000_19_ep2d-diff-4scan-trace-p2-2mm.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMP_0188_01/SPN01_CMP_0188_01_01_DTI60-1000_28_ep2d-diff-4scan-trace-p2-2mm.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMP_0190_01/SPN01_CMP_0190_01_01_DTI60-1000_20_ep2d-diff-4scan-trace-p2-2mm.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMP_0191_02/SPN01_CMP_0191_02_01_DTI60-1000_21_ep2d-diff-4scan-trace-p2-2mm.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMP_0192_01/SPN01_CMP_0192_01_01_DTI60-1000_18_ep2d-diff-4scan-trace-p2-2mm.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMP_0193_01/SPN01_CMP_0193_01_01_DTI60-1000_18_ep2d-diff-4scan-trace-p2-2mm.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMP_0196_02/SPN01_CMP_0196_02_01_DTI60-1000_18_ep2d-diff-4scan-trace-p2-2mm.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMP_0198_02/SPN01_CMP_0198_02_01_DTI60-1000_21_ep2d-diff-4scan-trace-p2-2mm.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMP_0199_01/SPN01_CMP_0199_01_01_DTI60-1000_18_ep2d-diff-4scan-trace-p2-2mm.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMP_0201_01/SPN01_CMP_0201_01_01_DTI60-1000_18_ep2d-diff-4scan-trace-p2-2mm.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMP_P001_04/SPN01_CMP_P001_04_02_DTI60-1000_07_ep2d-diff-4scan-trace-p2-2mm.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMP_P002_04/SPN01_CMP_P002_04_01_DTI60-1000_10_ep2d-diff-4scan-trace-p2-2mm.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMP_P003_04/SPN01_CMP_P003_04_01_DTI60-1000_10_ep2d-diff-4scan-trace-p2-2mm.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMP_P998_01/SPN01_CMP_P998_01_01_DTI60-1000_12_ep2d-diff-4scan-trace-p2-2mm.json\n", + "/archive/data/SPINS/data/nii/SPN01_CMP_P999_01/SPN01_CMP_P999_01_01_DTI60-1000_19_ep2d-diff-4scan-trace-p2-2mm.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0019_01/SPN01_MRP_0019_01_01_DTI60-1000_32_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0067_01/SPN01_MRP_0067_01_01_DTI60-1000_28_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0076_01/SPN01_MRP_0076_01_02_DTI60-1000_12_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0080_01/SPN01_MRP_0080_01_01_DTI60-1000_23_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0081_01/SPN01_MRP_0081_01_01_DTI60-1000_25_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0082_01/SPN01_MRP_0082_01_02_DTI60-1000_13_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0083_01/SPN01_MRP_0083_01_01_DTI60-1000_23_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0084_01/SPN01_MRP_0084_01_01_DTI60-1000_24_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0085_01/SPN01_MRP_0085_01_01_DTI60-1000_23_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0086_01/SPN01_MRP_0086_01_01_DTI60-1000_23_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0088_01/SPN01_MRP_0088_01_01_DTI60-1000_23_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0091_01/SPN01_MRP_0091_01_01_DTI60-1000_23_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0092_01/SPN01_MRP_0092_01_01_DTI60-1000_25_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0093_01/SPN01_MRP_0093_01_01_DTI60-1000_23_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0094_01/SPN01_MRP_0094_01_01_DTI60-1000_23_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0095_01/SPN01_MRP_0095_01_01_DTI60-1000_23_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0096_01/SPN01_MRP_0096_01_01_DTI60-1000_30_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0097_01/SPN01_MRP_0097_01_01_DTI60-1000_24_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0098_01/SPN01_MRP_0098_01_01_DTI60-1000_24_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0099_01/SPN01_MRP_0099_01_01_DTI60-1000_27_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0100_01/SPN01_MRP_0100_01_01_DTI60-1000_25_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0101_01/SPN01_MRP_0101_01_01_DTI60-1000_25_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0102_01/SPN01_MRP_0102_01_01_DTI60-1000_24_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0103_01/SPN01_MRP_0103_01_01_DTI60-1000_28_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0104_01/SPN01_MRP_0104_01_03_DTI60-1000_24_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0105_01/SPN01_MRP_0105_01_01_DTI60-1000_26_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0106_01/SPN01_MRP_0106_01_01_DTI60-1000_24_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0107_01/SPN01_MRP_0107_01_01_DTI60-1000_26_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0109_01/SPN01_MRP_0109_01_01_DTI60-1000_26_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0110_01/SPN01_MRP_0110_01_01_DTI60-1000_26_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0112_01/SPN01_MRP_0112_01_01_DTI60-1000_24_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0113_01/SPN01_MRP_0113_01_01_DTI60-1000_24_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0115_01/SPN01_MRP_0115_01_01_DTI60-1000_25_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0116_01/SPN01_MRP_0116_01_01_DTI60-1000_25_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0117_01/SPN01_MRP_0117_01_01_DTI60-1000_24_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0118_01/SPN01_MRP_0118_01_01_DTI60-1000_23_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0120_01/SPN01_MRP_0120_01_01_DTI60-1000_23_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0121_01/SPN01_MRP_0121_01_01_DTI60-1000_23_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0122_01/SPN01_MRP_0122_01_01_DTI60-1000_23_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0123_01/SPN01_MRP_0123_01_01_DTI60-1000_23_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0125_01/SPN01_MRP_0125_01_01_DTI60-1000_23_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0127_01/SPN01_MRP_0127_01_01_DTI60-1000_25_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0128_01/SPN01_MRP_0128_01_01_DTI60-1000_23_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0132_01/SPN01_MRP_0132_01_01_DTI60-1000_23_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0134_01/SPN01_MRP_0134_01_01_DTI60-1000_23_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0135_01/SPN01_MRP_0135_01_01_DTI60-1000_23_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0136_01/SPN01_MRP_0136_01_01_DTI60-1000_30_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0137_01/SPN01_MRP_0137_01_01_DTI60-1000_23_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0139_01/SPN01_MRP_0139_01_01_DTI60-1000_25_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_0140_01/SPN01_MRP_0140_01_01_DTI60-1000_24_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_P001_03/SPN01_MRP_P001_03_01_DTI60-1000_14_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_P003_03/SPN01_MRP_P003_03_02_DTI60-1000_14_DTI-60Dir-5B0.json\n", + "/archive/data/SPINS/data/nii/SPN01_MRP_P005_03/SPN01_MRP_P005_03_01_DTI60-1000_14_DTI-60Dir-5B0.json\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/archive/data/SPINS/data/nii/SPN01_ZHH_0001_01/SPN01_ZHH_0001_01_01_DTI60-1000_16_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0002_01/SPN01_ZHH_0002_01_01_DTI60-1000_20_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0003_01/SPN01_ZHH_0003_01_01_DTI60-1000_20_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0004_01/SPN01_ZHH_0004_01_01_DTI60-1000_23_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0005_01/SPN01_ZHH_0005_01_01_DTI60-1000_16_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0006_01/SPN01_ZHH_0006_01_01_DTI60-1000_17_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0007_01/SPN01_ZHH_0007_01_01_DTI60-1000_15_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0008_01/SPN01_ZHH_0008_01_01_DTI60-1000_17_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0009_01/SPN01_ZHH_0009_01_01_DTI60-1000_15_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0010_01/SPN01_ZHH_0010_01_01_DTI60-1000_19_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0011_01/SPN01_ZHH_0011_01_01_DTI60-1000_16_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0012_01/SPN01_ZHH_0012_01_01_DTI60-1000_16_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0013_01/SPN01_ZHH_0013_01_01_DTI60-1000_16_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0014_01/SPN01_ZHH_0014_01_01_DTI60-1000_21_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0015_01/SPN01_ZHH_0015_01_01_DTI60-1000_17_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0017_01/SPN01_ZHH_0017_01_01_DTI60-1000_16_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0018_01/SPN01_ZHH_0018_01_01_DTI60-1000_15_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0019_01/SPN01_ZHH_0019_01_01_DTI60-1000_20_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0020_01/SPN01_ZHH_0020_01_01_DTI60-1000_18_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0021_01/SPN01_ZHH_0021_01_01_DTI60-1000_16_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0022_01/SPN01_ZHH_0022_01_01_DTI60-1000_23_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0023_01/SPN01_ZHH_0023_01_01_DTI60-1000_21_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0024_01/SPN01_ZHH_0024_01_01_DTI60-1000_17_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0025_01/SPN01_ZHH_0025_01_01_DTI60-1000_15_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0028_01/SPN01_ZHH_0028_01_01_DTI60-1000_18_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0029_01/SPN01_ZHH_0029_01_01_DTI60-1000_17_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0030_01/SPN01_ZHH_0030_01_01_DTI60-1000_21_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0031_01/SPN01_ZHH_0031_01_01_DTI60-1000_13_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0032_01/SPN01_ZHH_0032_01_01_DTI60-1000_25_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0033_01/SPN01_ZHH_0033_01_01_DTI60-1000_15_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0037_01/SPN01_ZHH_0037_01_01_DTI60-1000_20_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0038_01/SPN01_ZHH_0038_01_01_DTI60-1000_18_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0039_01/SPN01_ZHH_0039_01_01_DTI60-1000_22_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0040_01/SPN01_ZHH_0040_01_01_DTI60-1000_16_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0041_01/SPN01_ZHH_0041_01_01_DTI60-1000_18_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0043_01/SPN01_ZHH_0043_01_01_DTI60-1000_16_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0045_01/SPN01_ZHH_0045_01_01_DTI60-1000_16_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0047_01/SPN01_ZHH_0047_01_01_DTI60-1000_17_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0048_01/SPN01_ZHH_0048_01_01_DTI60-1000_14_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0052_01/SPN01_ZHH_0052_01_01_DTI60-1000_21_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0053_01/SPN01_ZHH_0053_01_01_DTI60-1000_17_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0054_01/SPN01_ZHH_0054_01_01_DTI60-1000_17_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0056_01/SPN01_ZHH_0056_01_01_DTI60-1000_16_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0057_01/SPN01_ZHH_0057_01_01_DTI60-1000_14_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_0060_01/SPN01_ZHH_0060_01_01_DTI60-1000_18_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_9999_01/SPN01_ZHH_9999_01_01_DTI60-1000_19_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_P001_01/SPN01_ZHH_P001_01_01_DTI60-1000_15_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_P001_02/SPN01_ZHH_P001_02_01_DTI60-1000_11_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_P002_01/SPN01_ZHH_P002_01_01_DTI60-1000_15_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_P002_02/SPN01_ZHH_P002_02_01_DTI60-1000_14_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_P003_01/SPN01_ZHH_P003_01_01_DTI60-1000_16_DTI-B-1000-60+5.json\n", + "/archive/data/SPINS/data/nii/SPN01_ZHH_P003_02/SPN01_ZHH_P003_02_01_DTI60-1000_10_DTI-B-1000-60+5.json\n" + ] + } + ], + "source": [ + "dwi_jsons = sorted(glob('/archive/data/SPINS/data/nii/SPN01_*_0*/*DTI*json'))\n", + "\n", + "slice_timings = []\n", + "\n", + "for i in dwi_jsons:\n", + " with open(i, 'r') as f:\n", + " json_dict = json.load(f)\n", + " try:\n", + " timing = json_dict['SliceTiming']\n", + " if not timing in slice_timings:\n", + " slice_timings.append(timing)\n", + " except:\n", + " print(i)\n", + " continue" + ] + }, + { + "cell_type": "code", + "execution_count": 136, + "metadata": {}, + "outputs": [], + "source": [ + "dwi_jsons = sorted(glob('/archive/data/SPINS/data/nii/SPN01_*_0*/*DTI*json'))\n", + "\n", + "slice_orders = []\n", + "\n", + "for i in dwi_jsons:\n", + " with open(i, 'r') as f:\n", + " json_dict = json.load(f)\n", + " try:\n", + " timing = json_dict['SliceTiming']\n", + " slice_order = sorted(range(len(timing)),key=timing.__getitem__)\n", + " if not slice_order in slice_orders:\n", + " slice_orders.append(slice_order)\n", + " except:\n", + " continue" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "slice_order" + ] + }, + { + "cell_type": "code", + "execution_count": 138, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[[1,\n", + " 3,\n", + " 5,\n", + " 7,\n", + " 9,\n", + " 11,\n", + " 13,\n", + " 15,\n", + " 17,\n", + " 19,\n", + " 21,\n", + " 23,\n", + " 25,\n", + " 27,\n", + " 29,\n", + " 31,\n", + " 33,\n", + " 35,\n", + " 37,\n", + " 39,\n", + " 41,\n", + " 43,\n", + " 45,\n", + " 47,\n", + " 49,\n", + " 51,\n", + " 53,\n", + " 55,\n", + " 57,\n", + " 59,\n", + " 61,\n", + " 63,\n", + " 65,\n", + " 0,\n", + " 2,\n", + " 4,\n", + " 6,\n", + " 8,\n", + " 10,\n", + " 12,\n", + " 14,\n", + " 16,\n", + " 18,\n", + " 20,\n", + " 22,\n", + " 24,\n", + " 26,\n", + " 28,\n", + " 30,\n", + " 32,\n", + " 34,\n", + " 36,\n", + " 38,\n", + " 40,\n", + " 42,\n", + " 44,\n", + " 46,\n", + " 48,\n", + " 50,\n", + " 52,\n", + " 54,\n", + " 56,\n", + " 58,\n", + " 60,\n", + " 62,\n", + " 64],\n", + " [1,\n", + " 3,\n", + " 5,\n", + " 7,\n", + " 9,\n", + " 11,\n", + " 13,\n", + " 15,\n", + " 17,\n", + " 19,\n", + " 21,\n", + " 23,\n", + " 25,\n", + " 27,\n", + " 29,\n", + " 31,\n", + " 33,\n", + " 35,\n", + " 37,\n", + " 39,\n", + " 41,\n", + " 43,\n", + " 45,\n", + " 47,\n", + " 49,\n", + " 51,\n", + " 53,\n", + " 55,\n", + " 57,\n", + " 59,\n", + " 61,\n", + " 63,\n", + " 65,\n", + " 67,\n", + " 69,\n", + " 71,\n", + " 73,\n", + " 0,\n", + " 2,\n", + " 4,\n", + " 6,\n", + " 8,\n", + " 10,\n", + " 12,\n", + " 14,\n", + " 16,\n", + " 18,\n", + " 20,\n", + " 22,\n", + " 24,\n", + " 26,\n", + " 28,\n", + " 30,\n", + " 32,\n", + " 34,\n", + " 36,\n", + " 38,\n", + " 40,\n", + " 42,\n", + " 44,\n", + " 46,\n", + " 48,\n", + " 50,\n", + " 52,\n", + " 54,\n", + " 56,\n", + " 58,\n", + " 60,\n", + " 62,\n", + " 64,\n", + " 66,\n", + " 68,\n", + " 70,\n", + " 72]]" + ] + }, + "execution_count": 138, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "slice_orders" + ] + }, + { + "cell_type": "code", + "execution_count": 135, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[2,\n", + " 4,\n", + " 6,\n", + " 8,\n", + " 10,\n", + " 12,\n", + " 14,\n", + " 16,\n", + " 18,\n", + " 20,\n", + " 22,\n", + " 24,\n", + " 26,\n", + " 28,\n", + " 30,\n", + " 32,\n", + " 34,\n", + " 36,\n", + " 38,\n", + " 40,\n", + " 42,\n", + " 44,\n", + " 46,\n", + " 48,\n", + " 50,\n", + " 52,\n", + " 54,\n", + " 56,\n", + " 58,\n", + " 60,\n", + " 62,\n", + " 64,\n", + " 66,\n", + " 1,\n", + " 3,\n", + " 5,\n", + " 7,\n", + " 9,\n", + " 11,\n", + " 13,\n", + " 15,\n", + " 17,\n", + " 19,\n", + " 21,\n", + " 23,\n", + " 25,\n", + " 27,\n", + " 29,\n", + " 31,\n", + " 33,\n", + " 35,\n", + " 37,\n", + " 39,\n", + " 41,\n", + " 43,\n", + " 45,\n", + " 47,\n", + " 49,\n", + " 51,\n", + " 53,\n", + " 55,\n", + " 57,\n", + " 59,\n", + " 61,\n", + " 63,\n", + " 65]" + ] + }, + "execution_count": 135, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "slice_order" + ] + }, + { + "cell_type": "code", + "execution_count": 132, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[[2,\n", + " 4,\n", + " 6,\n", + " 8,\n", + " 10,\n", + " 12,\n", + " 14,\n", + " 16,\n", + " 18,\n", + " 20,\n", + " 22,\n", + " 24,\n", + " 26,\n", + " 28,\n", + " 30,\n", + " 32,\n", + " 34,\n", + " 36,\n", + " 38,\n", + " 40,\n", + " 42,\n", + " 44,\n", + " 46,\n", + " 48,\n", + " 50,\n", + " 52,\n", + " 54,\n", + " 56,\n", + " 58,\n", + " 60,\n", + " 62,\n", + " 64,\n", + " 66,\n", + " 1,\n", + " 3,\n", + " 5,\n", + " 7,\n", + " 9,\n", + " 11,\n", + " 13,\n", + " 15,\n", + " 17,\n", + " 19,\n", + " 21,\n", + " 23,\n", + " 25,\n", + " 27,\n", + " 29,\n", + " 31,\n", + " 33,\n", + " 35,\n", + " 37,\n", + " 39,\n", + " 41,\n", + " 43,\n", + " 45,\n", + " 47,\n", + " 49,\n", + " 51,\n", + " 53,\n", + " 55,\n", + " 57,\n", + " 59,\n", + " 61,\n", + " 63,\n", + " 65],\n", + " [2,\n", + " 4,\n", + " 6,\n", + " 8,\n", + " 10,\n", + " 12,\n", + " 14,\n", + " 16,\n", + " 18,\n", + " 20,\n", + " 22,\n", + " 24,\n", + " 26,\n", + " 28,\n", + " 30,\n", + " 32,\n", + " 34,\n", + " 36,\n", + " 38,\n", + " 40,\n", + " 42,\n", + " 44,\n", + " 46,\n", + " 48,\n", + " 50,\n", + " 52,\n", + " 54,\n", + " 56,\n", + " 58,\n", + " 60,\n", + " 62,\n", + " 64,\n", + " 66,\n", + " 68,\n", + " 70,\n", + " 72,\n", + " 74,\n", + " 1,\n", + " 3,\n", + " 5,\n", + " 7,\n", + " 9,\n", + " 11,\n", + " 13,\n", + " 15,\n", + " 17,\n", + " 19,\n", + " 21,\n", + " 23,\n", + " 25,\n", + " 27,\n", + " 29,\n", + " 31,\n", + " 33,\n", + " 35,\n", + " 37,\n", + " 39,\n", + " 41,\n", + " 43,\n", + " 45,\n", + " 47,\n", + " 49,\n", + " 51,\n", + " 53,\n", + " 55,\n", + " 57,\n", + " 59,\n", + " 61,\n", + " 63,\n", + " 65,\n", + " 67,\n", + " 69,\n", + " 71,\n", + " 73]]" + ] + }, + "execution_count": 132, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "slice_orders" + ] + }, + { + "cell_type": "code", + "execution_count": 142, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/archive/data/SPINS/data/nii/SPN01_CMH_0001_01/SPN01_CMH_0001_01_01_DTI60-1000_20_Ax-DTI-60plus5.nii.gz\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0023_01/SPN01_CMH_0023_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.nii.gz\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0026_01/SPN01_CMH_0026_01_01_DTI60-1000_17_Ax-DTI-60plus5-20iso.nii.gz\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0134_01/SPN01_CMH_0134_01_01_DTI60-1000_16_Ax-DTI-60plus5-20iso.nii.gz\n", + "/archive/data/SPINS/data/nii/SPN01_CMH_0164_01/SPN01_CMH_0164_01_01_DTI60-1000_15_Ax-DTI-60plus5-20iso.nii.gz\n", + "/archive/data/SPINS/data/nii/SPN01_CMP_0191_02/SPN01_CMP_0191_02_01_DTI60-1000_21_ep2d-diff-4scan-trace-p2-2mm.nii.gz\n", + "/archive/data/SPINS/data/nii/SPN01_ZHP_P001_03/SPN01_ZHP_P001_03_01_DTI60-1000_11_DTI-60Dir-5B0.nii.gz\n", + "/archive/data/SPINS/data/nii/SPN01_ZHP_P002_03/SPN01_ZHP_P002_03_01_DTI60-1000_11_DTI-60Dir-5B0.nii.gz\n" + ] + } + ], + "source": [ + "dwi_niis = sorted(glob('/archive/data/SPINS/data/nii/SPN01_*_0*/*DTI*nii.gz'))\n", + "\n", + "unique_shapes = defaultdict(set)\n", + "\n", + "for i in dwi_niis:\n", + " site = i.split('/')[6].split('_')[1]\n", + " data = nib.load(i)\n", + " if not data.shape[2] == 66:\n", + " print(i)\n", + " unique_shapes[site].add(data.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": 140, + "metadata": {}, + "outputs": [], + "source": [ + "dwi_niis = sorted(glob('/archive/data/SPINS/data/nii/SPN01_*_0*/*DTI*nii.gz'))\n", + "\n", + "for i in dwi_niis:\n", + " data = nib.load(i)\n", + " slice_num = data.shape[2]\n", + " slices = list(range(1, slice_num))\n", + " even_slices = [slice for slice in slices if slice % 2 == 0]\n", + " odd_slices = [slice for slice in slices if slice % 2 == 1]\n", + " slice_reorder = odd_slices + even_slices\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "slice_reorder" + ] + }, + { + "cell_type": "code", + "execution_count": 98, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "defaultdict(set,\n", + " {'CMH': {(128, 128, 66, 65), (128, 128, 71, 65)},\n", + " 'CMP': {(128, 128, 66, 66), (128, 128, 70, 66)},\n", + " 'MRC': {(128, 128, 66, 66)},\n", + " 'MRP': {(128, 128, 66, 66)},\n", + " 'ZHH': {(128, 128, 66, 65)},\n", + " 'ZHP': {(128, 128, 66, 61),\n", + " (128, 128, 66, 66),\n", + " (128, 128, 74, 66)}})" + ] + }, + "execution_count": 98, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "unique_shapes" + ] + }, + { + "cell_type": "code", + "execution_count": 102, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/archive/data/SPINS/data/nii/SPN01_ZHP_P001_03/SPN01_ZHP_P001_03_01_DTI60-1000_11_DTI-60Dir-5B0.nii.gz\n", + "/archive/data/SPINS/data/nii/SPN01_ZHP_P002_03/SPN01_ZHP_P002_03_01_DTI60-1000_11_DTI-60Dir-5B0.nii.gz\n" + ] + } + ], + "source": [ + "dwi_niis = sorted(glob('/archive/data/SPINS/data/nii/SPN01_*_0*/*DTI*nii.gz'))\n", + "\n", + "for i in dwi_niis:\n", + " data = nib.load(i)\n", + " if data.shape[2] == 74:\n", + " print(i)" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[(128, 128, 71, 65),\n", + " (128, 128, 66, 65),\n", + " (128, 128, 66, 66),\n", + " (128, 128, 70, 66),\n", + " (128, 128, 66, 61),\n", + " (128, 128, 74, 66)]" + ] + }, + "execution_count": 82, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "unique_shapes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "71, 65\n", + "66, 65\n", + "66, 66\n", + "70, 66\n", + "66, 61\n", + "74, 66" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "dwi_venv", + "language": "python", + "name": "dwi_venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.1" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebooks/test_wf.ipynb b/notebooks/test_wf.ipynb new file mode 100644 index 00000000..8aebdd41 --- /dev/null +++ b/notebooks/test_wf.ipynb @@ -0,0 +1,348 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from nipype.pipeline import engine as pe\n", + "from nipype.interfaces import utility as niu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "dwi_file = '/projects/mjoseph/pipelines/testing/data/sub-0880002/ses-01/dwi/sub-0880002_ses-01_acq-singleshell19dir_dwi.nii.gz'" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [], + "source": [ + "def init_test_wf():\n", + " wf = pe.Workflow(name=\"test\")\n", + "\n", + " inputnode = pe.Node(niu.IdentityInterface(fields=[\"dwi_file\"]), name=\"inputnode\")\n", + "\n", + " outputnode = pe.Node(niu.IdentityInterface(fields=[\"out_file\"]), name=\"outputnode\")\n", + "\n", + " denoise = pe.Node(DWIDenoise(), name=\"denoise\")\n", + " \n", + " unring = pe.Node(MRDeGibbs(), name=\"unring\")\n", + "\n", + " wf.connect([(inputnode, denoise, [(\"dwi_file\", \"in_file\")]),\n", + " (denoise, unring, [(\"out_file\", \"in_file\")]),\n", + " (unring, outputnode, [(\"out_file\", \"out_file\")])])\n", + " \n", + " return wf" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "190627-17:38:59,948 nipype.workflow INFO:\n", + "\t Workflow test settings: ['check', 'execution', 'logging', 'monitoring']\n", + "190627-17:39:00,35 nipype.workflow INFO:\n", + "\t Running serially.\n", + "190627-17:39:00,37 nipype.workflow INFO:\n", + "\t [Node] Setting-up \"test.denoise\" in \"/tmp/tmpbinecjkb/test/denoise\".\n", + "190627-17:39:00,42 nipype.workflow INFO:\n", + "\t [Node] Running \"denoise\" (\"__main__.DWIDenoise\"), a CommandLine Interface with command:\n", + "dwidenoise -noise sub-0880002_ses-01_acq-singleshell19dir_dwi_noise.nii.gz /projects/mjoseph/pipelines/testing/data/sub-0880002/ses-01/dwi/sub-0880002_ses-01_acq-singleshell19dir_dwi.nii.gz sub-0880002_ses-01_acq-singleshell19dir_dwi_denoised.nii.gz\n", + "190627-17:39:00,529 nipype.interface INFO:\n", + "dwidenoise: [100%] uncompressing image \"/projects/mjoseph/pipelines/testing/data/sub-0880002/ses-01/dwi/sub-0880002_ses-01_acq-singleshell19dir_dwi.nii.gz\"\u001b[0K\u001b[0K\n", + "190627-17:39:00,823 nipype.interface INFO:\n", + "dwidenoise: [100%] preloading data for \"/projects/mjoseph/pipelines/testing/data/sub-0880002/ses-01/dwi/sub-0880002_ses-01_acq-singleshell19dir_dwi.nii.gz\"\u001b[0K\u001b[0K\n", + "190627-17:39:03,881 nipype.interface INFO:\n", + "dwidenoise: [100%] running MP-PCA denoising\u001b[0K\u001b[0K\n", + "190627-17:39:04,110 nipype.interface INFO:\n", + "dwidenoise: [100%] compressing image \"sub-0880002_ses-01_acq-singleshell19dir_dwi_noise.nii.gz\"\u001b[0K\u001b[0K\n", + "190627-17:39:08,812 nipype.interface INFO:\n", + "dwidenoise: [100%] compressing image \"sub-0880002_ses-01_acq-singleshell19dir_dwi_denoised.nii.gz\"\u001b[0K\u001b[0K\n", + "190627-17:39:08,932 nipype.workflow INFO:\n", + "\t [Node] Finished \"test.denoise\".\n", + "190627-17:39:08,933 nipype.workflow INFO:\n", + "\t [Node] Setting-up \"test.unring\" in \"/tmp/tmpi_3ouhxh/test/unring\".\n", + "190627-17:39:08,937 nipype.workflow INFO:\n", + "\t [Node] Running \"unring\" (\"__main__.MRDeGibbs\"), a CommandLine Interface with command:\n", + "mrdegibbs -axes 0,1 -maxW 3 -minW 1 -nshifts 20 /tmp/tmpbinecjkb/test/denoise/sub-0880002_ses-01_acq-singleshell19dir_dwi_denoised.nii.gz sub-0880002_ses-01_acq-singleshell19dir_dwi_denoised_unr.nii.gz\n", + "190627-17:39:09,795 nipype.interface INFO:\n", + "mrdegibbs: [100%] uncompressing image \"/tmp/tmpbinecjkb/test/denoise/sub-0880002_ses-01_acq-singleshell19dir_dwi_denoised.nii.gz\"\u001b[0K\u001b[0K\n", + "190627-17:39:16,918 nipype.interface INFO:\n", + "mrdegibbs: [100%] performing Gibbs ringing removal\u001b[0K\u001b[0K\n", + "190627-17:39:21,387 nipype.interface INFO:\n", + "mrdegibbs: [100%] compressing image \"sub-0880002_ses-01_acq-singleshell19dir_dwi_denoised_unr.nii.gz\"\u001b[0K\u001b[0K\n", + "190627-17:39:21,492 nipype.workflow INFO:\n", + "\t [Node] Finished \"test.unring\".\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "test_wf = init_test_wf()\n", + "inputspec = test_wf.get_node(\"inputnode\")\n", + "inputspec.inputs.dwi_file = dwi_file\n", + "test_wf.run()" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import (print_function, division, unicode_literals,\n", + " absolute_import)\n", + "\n", + "import os\n", + "\n", + "from nipype.interfaces.base import (CommandLineInputSpec, CommandLine, traits, TraitedSpec,\n", + " File, isdefined, Undefined, InputMultiObject)\n", + "from nipype.interfaces.mrtrix3.base import MRTrix3BaseInputSpec, MRTrix3Base\n", + "\n", + "\n", + "class DWIDenoiseInputSpec(MRTrix3BaseInputSpec):\n", + " in_file = File(\n", + " exists=True,\n", + " argstr='%s',\n", + " position=-2,\n", + " mandatory=True,\n", + " desc='input DWI image')\n", + " mask = File(\n", + " exists=True,\n", + " argstr='-mask %s',\n", + " position=1,\n", + " desc='mask image')\n", + " extent = traits.Tuple((traits.Int, traits.Int, traits.Int),\n", + " argstr='-extent %d,%d,%d',\n", + " desc='set the window size of the denoising filter. (default = 5,5,5)')\n", + " noise = File(\n", + " argstr='-noise %s',\n", + " name_template='%s_noise',\n", + " name_source=['in_file'],\n", + " keep_extension=True,\n", + " desc='the output noise map')\n", + " out_file = File(\n", + " argstr='%s',\n", + " name_template='%s_denoised',\n", + " name_source=['in_file'],\n", + " keep_extension=True,\n", + " position=-1,\n", + " desc='the output denoised DWI image')\n", + "\n", + "class DWIDenoiseOutputSpec(TraitedSpec):\n", + " out_file = File(desc='the output denoised DWI image', exists=True)\n", + " noise = File(desc='the output noise map (if generated)', exists=True)\n", + "\n", + "class DWIDenoise(MRTrix3Base):\n", + " \"\"\"\n", + " Denoise DWI data and estimate the noise level based on the optimal\n", + " threshold for PCA.\n", + " DWI data denoising and noise map estimation by exploiting data redundancy\n", + " in the PCA domain using the prior knowledge that the eigenspectrum of\n", + " random covariance matrices is described by the universal Marchenko Pastur\n", + " distribution.\n", + " Important note: image denoising must be performed as the first step of the\n", + " image processing pipeline. The routine will fail if interpolation or\n", + " smoothing has been applied to the data prior to denoising.\n", + " Note that this function does not correct for non-Gaussian noise biases.\n", + " For more information, see\n", + " \n", + " Example\n", + " -------\n", + " >>> import nipype.interfaces.mrtrix3 as mrt\n", + " >>> denoise = mrt.DWIDenoise()\n", + " >>> denoise.inputs.in_file = 'dwi.mif'\n", + " >>> denoise.inputs.mask = 'mask.mif'\n", + " >>> denoise.cmdline # doctest: +ELLIPSIS\n", + " 'dwidenoise -mask mask.mif dwi.mif dwi_denoised.mif'\n", + " >>> denoise.run() # doctest: +SKIP\n", + " \"\"\"\n", + "\n", + " _cmd = 'dwidenoise'\n", + " input_spec = DWIDenoiseInputSpec\n", + " output_spec = DWIDenoiseOutputSpec" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [], + "source": [ + "class MRDeGibbsInputSpec(MRTrix3BaseInputSpec):\n", + " in_file = File(\n", + " exists=True,\n", + " argstr='%s',\n", + " position=-2,\n", + " mandatory=True,\n", + " desc='input DWI image')\n", + " axes = traits.ListInt(\n", + " default_value=[0,1],\n", + " usedefault=True,\n", + " sep=',',\n", + " minlen=2,\n", + " maxlen=2,\n", + " argstr='-axes %s',\n", + " desc='indicate the plane in which the data was acquired (axial = 0,1; '\n", + " 'coronal = 0,2; sagittal = 1,2')\n", + " nshifts = traits.Int(\n", + " default_value=20,\n", + " usedefault=True,\n", + " argstr='-nshifts %d',\n", + " desc='discretization of subpixel spacing (default = 20)')\n", + " minW = traits.Int(\n", + " default_value=1,\n", + " usedefault=True,\n", + " argstr='-minW %d',\n", + " desc='left border of window used for total variation (TV) computation '\n", + " '(default = 1)')\n", + " maxW = traits.Int(\n", + " default_value=3,\n", + " usedefault=True,\n", + " argstr='-maxW %d',\n", + " desc='right border of window used for total variation (TV) computation '\n", + " '(default = 3)')\n", + " out_file = File(name_template='%s_unr',\n", + " name_source='in_file',\n", + " keep_extension=True,\n", + " argstr='%s',\n", + " position=-1,\n", + " desc='the output unringed DWI image',\n", + " genfile=True)\n", + "\n", + "class MRDeGibbsOutputSpec(TraitedSpec):\n", + " out_file = File(desc='the output unringed DWI image', exists=True)\n", + "\n", + "class MRDeGibbs(MRTrix3Base):\n", + " \"\"\"\n", + " Remove Gibbs ringing artifacts.\n", + " This application attempts to remove Gibbs ringing artefacts from MRI images\n", + " using the method of local subvoxel-shifts proposed by Kellner et al.\n", + " This command is designed to run on data directly after it has been\n", + " reconstructed by the scanner, before any interpolation of any kind has\n", + " taken place. You should not run this command after any form of motion\n", + " correction (e.g. not after dwipreproc). Similarly, if you intend running\n", + " dwidenoise, you should run this command afterwards, since it has the\n", + " potential to alter the noise structure, which would impact on dwidenoise's\n", + " performance.\n", + " Note that this method is designed to work on images acquired with full\n", + " k-space coverage. Running this method on partial Fourier ('half-scan') data\n", + " may lead to suboptimal and/or biased results, as noted in the original\n", + " reference below. There is currently no means of dealing with this; users\n", + " should exercise caution when using this method on partial Fourier data, and\n", + " inspect its output for any obvious artefacts.\n", + " For more information, see\n", + " \n", + " Example\n", + " -------\n", + " >>> import nipype.interfaces.mrtrix3 as mrt\n", + " >>> unring = mrt.MRDeGibbs()\n", + " >>> unring.inputs.in_file = 'dwi.mif'\n", + " >>> unring.cmdline\n", + " 'mrdegibbs -axes 0,1 -maxW 3 -minW 1 -nshifts 20 dwi.mif dwi_unr.mif'\n", + " >>> unring.run() # doctest: +SKIP\n", + " \"\"\"\n", + "\n", + " _cmd = 'mrdegibbs'\n", + " input_spec = MRDeGibbsInputSpec\n", + " output_spec = MRDeGibbsOutputSpec" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + " def _gen_outfilename(self):\n", + " out_file = self.inputs.out_file\n", + " if not isdefined(out_file) and isdefined(self.inputs.in_file):\n", + " out_file = self._gen_fname(self.inputs.in_file, suffix='_denoised')\n", + " return os.path.abspath(out_file)\n", + " \n", + " def _gen_fname(self,\n", + " basename,\n", + " cwd=None,\n", + " suffix=None,\n", + " change_ext=True,\n", + " ext=None):\n", + "\n", + " if basename == '':\n", + " msg = 'Unable to generate filename for command %s. ' % self.cmd\n", + " msg += 'basename is not set!'\n", + " raise ValueError(msg)\n", + " if cwd is None:\n", + " cwd = os.getcwd()\n", + " if ext is None:\n", + " ext = Info.output_type_to_ext(self.inputs.output_type)\n", + " if change_ext:\n", + " if suffix:\n", + " suffix = ''.join((suffix, ext))\n", + " else:\n", + " suffix = ext\n", + " if suffix is None:\n", + " suffix = ''\n", + " fname = fname_presuffix(\n", + " basename, suffix=suffix, use_ext=False, newpath=cwd)\n", + " return fname\n", + "\n", + " def _list_outputs(self):\n", + " outputs = self.output_spec().get()\n", + " outputs['out_file'] = self._gen_outfilename()\n", + " if (isdefined(self.inputs.noise) and self.inputs.noise):\n", + " outputs['noise_file'] = self._gen_fname(outputs['out_file'], suffix='_noise')\n", + " return outputs" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "dwi_venv", + "language": "python", + "name": "dwi_venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.1" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 2fb1a5d07c3b5a06d990a44bfd22669111a976f5 Mon Sep 17 00:00:00 2001 From: Salim Mansour Date: Tue, 9 Jul 2019 12:08:55 -0400 Subject: [PATCH 059/156] Updated documentation page generation --- AUTHORS.rst | 10 +++++++--- docs/conf.py | 5 +---- docs/index.rst | 1 - setup.py | 1 + 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index bf162e51..017e8668 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -1,10 +1,14 @@ -# Credits +============ +Credits +============ -## Development Leads +Development Leads +----------------- - Michael Joseph [Corresponding developer] - Salim Mansour -## Contributors +Contributors +------------ None yet. Why not be the first? diff --git a/docs/conf.py b/docs/conf.py index 97b2a08e..227430ee 100755 --- a/docs/conf.py +++ b/docs/conf.py @@ -95,7 +95,7 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = [] # -- Options for HTMLHelp output --------------------------------------- @@ -158,6 +158,3 @@ 'One line description of project.', 'Miscellaneous'), ] - - - diff --git a/docs/index.rst b/docs/index.rst index d654265b..abb3d69a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -7,7 +7,6 @@ Welcome to dmriprep's documentation! installation usage - modules contributing authors history diff --git a/setup.py b/setup.py index b4add0ae..a951356e 100644 --- a/setup.py +++ b/setup.py @@ -21,6 +21,7 @@ "pybids", "matplotlib", "numba", + "sphinx", ] extras_require = {"dev": ["flake8", "pytest", "pytest-cov", "pre-commit"]} From e7a9bad3c7c6217d315f88b72113982c28c70223 Mon Sep 17 00:00:00 2001 From: Salim Mansour Date: Thu, 11 Jul 2019 10:42:10 -0400 Subject: [PATCH 060/156] Added ignore_nodes parameter and fixed imports for node functions --- dmriprep/cli.py | 13 +++++ dmriprep/workflows/base.py | 8 +-- dmriprep/workflows/dwi/__init__.py | 1 + dmriprep/workflows/dwi/base.py | 41 +++++++++++---- dmriprep/workflows/dwi/dwiprep.py | 82 ++++++++++++++++++++++++++++++ 5 files changed, 132 insertions(+), 13 deletions(-) create mode 100644 dmriprep/workflows/dwi/dwiprep.py diff --git a/dmriprep/cli.py b/dmriprep/cli.py index 338dd4bb..0f7e25ea 100644 --- a/dmriprep/cli.py +++ b/dmriprep/cli.py @@ -62,6 +62,17 @@ default=None, type=(float), ) +@click.option( + "--ignore_nodes", + help="Specify which node(s) to skip during the preprocessing of the dwi." + "Example: If you want to skip unring and resize, use '--ignore_nodes ur'." + "Options are: \n" + " d: denoise \n" + " u: unring \n" + " r: resize (upsample)", + default=None, + type=(str), +) @click.argument("bids_dir") @click.argument("output_dir") @click.argument( @@ -77,6 +88,7 @@ def main( bet_dwi=0.3, bet_mag=0.3, total_readout=None, + ignore_nodes='', analysis_level="participant", ): """ @@ -112,6 +124,7 @@ def main( bet_dwi, bet_mag, total_readout, + ignore_nodes, ) wf.write_graph(graph2use="colored") wf.config["execution"]["remove_unnecessary_outputs"] = False diff --git a/dmriprep/workflows/base.py b/dmriprep/workflows/base.py index 42d0a1e1..03d44a26 100644 --- a/dmriprep/workflows/base.py +++ b/dmriprep/workflows/base.py @@ -9,7 +9,7 @@ def init_dmriprep_wf( layout, subject_list, work_dir, output_dir, - bet_dwi, bet_mag, total_readout + bet_dwi, bet_mag, total_readout, ignore_nodes ): dmriprep_wf = pe.Workflow(name="dmriprep_wf") dmriprep_wf.base_dir = work_dir @@ -25,6 +25,7 @@ def init_dmriprep_wf( bet_dwi=bet_dwi, bet_mag=bet_mag, total_readout=total_readout, + ignore_nodes=ignore_nodes, ) single_subject_wf.config["execution"]["crashdump_dir"] = os.path.join( @@ -41,7 +42,7 @@ def init_dmriprep_wf( def init_single_subject_wf( layout, subject_id, name, work_dir, output_dir, - bet_dwi, bet_mag, total_readout + bet_dwi, bet_mag, total_readout, ignore_nodes ): dwi_files = layout.get( @@ -66,7 +67,8 @@ def init_single_subject_wf( metadata = layout.get_metadata(dwi_file) dwi_preproc_wf = init_dwi_preproc_wf( subject_id=subject_id, dwi_file=dwi_file, metadata=metadata, layout=layout, - bet_dwi_frac=bet_dwi, bet_mag_frac=bet_mag, total_readout=total_readout + bet_dwi_frac=bet_dwi, bet_mag_frac=bet_mag, total_readout=total_readout, + ignore_nodes=ignore_nodes ) datasink_wf = init_output_wf( subject_id=subject_id, session_id=session_id, output_folder=output_dir diff --git a/dmriprep/workflows/dwi/__init__.py b/dmriprep/workflows/dwi/__init__.py index 24c6efe7..5e2e06f4 100644 --- a/dmriprep/workflows/dwi/__init__.py +++ b/dmriprep/workflows/dwi/__init__.py @@ -2,3 +2,4 @@ from .base import init_dwi_preproc_wf from .outputs import init_output_wf +from .dwiprep import init_dwiprep_wf diff --git a/dmriprep/workflows/dwi/base.py b/dmriprep/workflows/dwi/base.py index 43d829fb..fc9e2b83 100755 --- a/dmriprep/workflows/dwi/base.py +++ b/dmriprep/workflows/dwi/base.py @@ -14,7 +14,7 @@ from ...interfaces import mrtrix3 from ..fieldmap.base import init_sdc_prep_wf - +from .dwiprep import init_dwiprep_wf def init_dwi_preproc_wf( subject_id, @@ -24,6 +24,7 @@ def init_dwi_preproc_wf( bet_dwi_frac, bet_mag_frac, total_readout, + ignore_nodes, ): fmaps = [] @@ -62,13 +63,17 @@ def init_dwi_preproc_wf( name="outputnode", ) - denoise = pe.Node(mrtrix3.DWIDenoise(), name="denoise") - - unring = pe.Node(mrtrix3.MRDeGibbs(), name="unring") - - resize = pe.Node(mrtrix3.MRResize(), name="resize") + # Create the dwi prep workflow + dwi_prep_wf = init_dwiprep_wf(ignore_nodes) def gen_index(in_file): + import os + import numpy as np + import nibabel as nib + from nipype.pipeline import engine as pe + from nipype.interfaces import fsl, utility as niu + from nipype.utils import NUMPY_MMAP + from nipype.utils.filemanip import fname_presuffix out_file = fname_presuffix( in_file, @@ -92,6 +97,10 @@ def gen_index(in_file): ) def gen_acqparams(in_file, metadata, total_readout_time): + import os + import numpy as np + import nibabel as nib + from nipype.utils.filemanip import fname_presuffix out_file = fname_presuffix( in_file, @@ -142,6 +151,13 @@ def b0_average(in_dwi, in_bval, b0_thresh=10.0, out_file=None): .. warning:: *b0* should be already registered (head motion artifact should be corrected). """ + import os + import numpy as np + import nibabel as nib + from nipype.pipeline import engine as pe + from nipype.interfaces import fsl, utility as niu + from nipype.utils import NUMPY_MMAP + from nipype.utils.filemanip import fname_presuffix if out_file is None: out_file = fname_presuffix( @@ -202,6 +218,12 @@ def b0_average(in_dwi, in_bval, b0_thresh=10.0, out_file=None): fslroi = pe.Node(fsl.ExtractROI(t_min=0, t_size=1), name="fslroi") def get_b0_mask_fn(b0_file): + import os + import nibabel as nib + from nipype.pipeline import engine as pe + from nipype.interfaces import fsl, utility as niu + from nipype.utils.filemanip import fname_presuffix + from dipy.segment.mask import median_otsu mask_file = fname_presuffix( b0_file, suffix="_mask", newpath=os.path.abspath(".") @@ -225,10 +247,9 @@ def get_b0_mask_fn(b0_file): dwi_wf.connect( [ - (inputnode, denoise, [("dwi_file", "in_file")]), - (denoise, unring, [("out_file", "in_file")]), + (inputnode, dwi_prep_wf, [("dwi_file", "dwi_prep_inputnode.dwi_file")]), + (dwi_prep_wf, avg_b0_0, [("dwi_prep_outputnode.out_file", "in_dwi")]), (inputnode, avg_b0_0, [("bval_file", "in_bval")]), - (unring, avg_b0_0, [("out_file", "in_dwi")]), (avg_b0_0, bet_dwi0, [("out_file", "in_file")]), (inputnode, gen_idx, [("dwi_file", "in_file")]), ( @@ -236,7 +257,7 @@ def get_b0_mask_fn(b0_file): acqp, [("dwi_file", "in_file"), ("metadata", "metadata")], ), - (unring, ecc, [("out_file", "in_file")]), + (dwi_prep_wf, ecc, [("dwi_prep_outputnode.out_file", "in_file")]), ( inputnode, ecc, diff --git a/dmriprep/workflows/dwi/dwiprep.py b/dmriprep/workflows/dwi/dwiprep.py new file mode 100644 index 00000000..8334cbed --- /dev/null +++ b/dmriprep/workflows/dwi/dwiprep.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python + +def init_dwiprep_wf(ignore_nodes): + from ...interfaces import mrtrix3 + from nipype.pipeline import engine as pe + from nipype.interfaces import fsl, utility as niu + + # Generate the prep workflow for dwi (denoise, unring, resize) + + dwi_prep_wf = pe.Workflow(name="dwi_prep_wf") + + denoise = pe.Node(mrtrix3.DWIDenoise(), name="denoise") + + unring = pe.Node(mrtrix3.MRDeGibbs(), name="unring") + + resize = pe.Node(mrtrix3.MRResize(voxel_size=[1]), name="resize") + + dwi_prep_inputnode = pe.Node( + niu.IdentityInterface( + fields=["dwi_file"] + ), + name="dwi_prep_inputnode", + ) + + dwi_prep_outputnode = pe.Node( + niu.IdentityInterface(fields=["out_file"]), + name="dwi_prep_outputnode", + ) + + # Turn the string input of nodes into a list + ignore_nodes_list = [option.lower() for option in ignore_nodes.strip()] + + prep_full = ['d', 'u', 'r'] + prep_wanted_str = [node for node in prep_full if not(node in ignore_nodes_list)] + + # Translate string input to node names + str2node = { + 'd': denoise, + 'u': unring, + 'r': resize + } + + # Convert the string list to a node list + prep_wanted = [str2node[str_node] for str_node in prep_wanted_str] + + # If no steps selected, just connect input to output + if not(prep_wanted): + dwi_prep_wf.connect( + [ + (dwi_prep_inputnode, dwi_prep_outputnode, [("dwi_file", "out_file")]) + ] + ) + + # If there are steps + else: + # Must at least connect input node to first node + first_node = prep_wanted[0] + dwi_prep_wf.connect( + [ + (dwi_prep_inputnode, first_node, [("dwi_file", "in_file")]), + ] + ) + # Loop through the prep order + # Note: only works if each node has in_file and out_file + # Can work around this by wrapping in node/workflow with in_file+out_file + prev = first_node + for curr_node in prep_wanted[1:]: + dwi_prep_wf.connect( + [ + (prev, curr_node, [("out_file", "in_file")]), + ] + ) + prev = curr_node + # Connect last node to output node + last_node = prep_wanted[-1] + dwi_prep_wf.connect( + [ + (last_node, dwi_prep_outputnode, [("out_file", "out_file")]), + ] + ) + + return dwi_prep_wf From bd7ceb57b602a33b8913ed9e7bd9214aed87e15f Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Fri, 12 Jul 2019 11:13:25 -0400 Subject: [PATCH 061/156] convert to rst and add codecov badge --- README.md => README.rst | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) rename README.md => README.rst (82%) diff --git a/README.md b/README.rst similarity index 82% rename from README.md rename to README.rst index ed752966..fab22ba7 100644 --- a/README.md +++ b/README.rst @@ -1,9 +1,7 @@ -[![Known Vulnerabilities](https://snyk.io/test/github/nipy/dmriprep/badge.svg)](https://snyk.io/test/github/nipy/dmriprep) -[![Documentation Status](https://readthedocs.org/projects/dmriprep/badge/?version=latest)](httpsL//dmriprep.readthredocs.io/en/latest/?badge=latest) -[![Build Status](https://travis-ci.org/nipy/dmriprep.png?branch=master)](https://travis-ci.org/nipy/dmriprep) -[![Coverage Status](https://coveralls.io/repos/github/nipy/dmriprep/badge.svg?branch=master)](https://coveralls.io/github/nipy/dmriprep?branch=master) -[![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/python/black) +.. image:: https://codecov.io/gh/TIGRLab/dmripreproc/branch/master/graph/badge.svg + :target: https://codecov.io/gh/TIGRLab/dmripreproc +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/python/black # dmriprep dmriprep From 1b1e8da62f79ecbb99601a363e2f01e942efbbdf Mon Sep 17 00:00:00 2001 From: Salim Mansour Date: Fri, 12 Jul 2019 12:27:32 -0400 Subject: [PATCH 062/156] Added parameters object for easier design --- dmriprep/cli.py | 25 ++++++++++++------- dmriprep/utils.py | 14 +++++++++++ dmriprep/workflows/base.py | 44 +++++++++++++--------------------- dmriprep/workflows/dwi/base.py | 18 ++++++-------- 4 files changed, 55 insertions(+), 46 deletions(-) diff --git a/dmriprep/cli.py b/dmriprep/cli.py index 0f7e25ea..ab38b93e 100644 --- a/dmriprep/cli.py +++ b/dmriprep/cli.py @@ -116,15 +116,24 @@ def main( ) work_dir = os.path.join(output_dir, "scratch") + + # Set parameters based on CLI, pass through object + parameters = utils.Parameters() + parameters.participant_label = participant_label + parameters.layout = layout + parameters.subject_list = subject_list + parameters.bids_dir = bids_dir + parameters.work_dir = work_dir + parameters.output_dir = output_dir + parameters.eddy_niter = eddy_niter + parameters.bet_dwi = bet_dwi + parameters.bet_mag = bet_mag + parameters.total_readout = total_readout + parameters.ignore_nodes = ignore_nodes + parameters.analysis_level = analysis_level + wf = init_dmriprep_wf( - layout, - subject_list, - work_dir, - output_dir, - bet_dwi, - bet_mag, - total_readout, - ignore_nodes, + parameters ) wf.write_graph(graph2use="colored") wf.config["execution"]["remove_unnecessary_outputs"] = False diff --git a/dmriprep/utils.py b/dmriprep/utils.py index abda5f3c..3914aa67 100644 --- a/dmriprep/utils.py +++ b/dmriprep/utils.py @@ -88,6 +88,20 @@ def __init__(self, message, bids_root): class BIDSWarning(RuntimeWarning): pass +class Parameters: + def __init__(self): + self.participant_label = '' + self.bids_dir = '' + self.work_dir = '' + self.layout = '' + self.subject_list = '' + self.output_dir = '' + self.eddy_niter = 5 + self.bet_dwi = 0.3 + self.bet_mag = 0.3 + self.total_readout = None + self.ignore_nodes = '' + self.analysis_level = 'participant' def collect_participants( bids_dir, participant_label=None, strict=False, bids_validate=True diff --git a/dmriprep/workflows/base.py b/dmriprep/workflows/base.py index 03d44a26..6c544039 100644 --- a/dmriprep/workflows/base.py +++ b/dmriprep/workflows/base.py @@ -6,30 +6,22 @@ from nipype.pipeline import engine as pe from .dwi import init_dwi_preproc_wf, init_output_wf - def init_dmriprep_wf( - layout, subject_list, work_dir, output_dir, - bet_dwi, bet_mag, total_readout, ignore_nodes + parameters ): dmriprep_wf = pe.Workflow(name="dmriprep_wf") - dmriprep_wf.base_dir = work_dir + dmriprep_wf.base_dir = parameters.work_dir - for subject_id in subject_list: + for subject_id in parameters.subject_list: single_subject_wf = init_single_subject_wf( - layout=layout, subject_id=subject_id, name="single_subject_" + subject_id + "_wf", - work_dir=work_dir, - output_dir=output_dir, - bet_dwi=bet_dwi, - bet_mag=bet_mag, - total_readout=total_readout, - ignore_nodes=ignore_nodes, + parameters=parameters ) single_subject_wf.config["execution"]["crashdump_dir"] = os.path.join( - output_dir, "dmriprep", "sub-" + subject_id, "log" + parameters.output_dir, "dmriprep", "sub-" + subject_id, "log" ) for node in single_subject_wf._get_all_nodes(): @@ -41,11 +33,10 @@ def init_dmriprep_wf( def init_single_subject_wf( - layout, subject_id, name, work_dir, output_dir, - bet_dwi, bet_mag, total_readout, ignore_nodes + subject_id, name, parameters ): - dwi_files = layout.get( + dwi_files = parameters.layout.get( subject=subject_id, datatype="dwi", suffix="dwi", @@ -62,32 +53,31 @@ def init_single_subject_wf( subject_wf = pe.Workflow(name=name) for dwi_file in dwi_files: - entities = layout.parse_file_entities(dwi_file) + entities = parameters.layout.parse_file_entities(dwi_file) session_id = entities["session"] - metadata = layout.get_metadata(dwi_file) + metadata = parameters.layout.get_metadata(dwi_file) dwi_preproc_wf = init_dwi_preproc_wf( - subject_id=subject_id, dwi_file=dwi_file, metadata=metadata, layout=layout, - bet_dwi_frac=bet_dwi, bet_mag_frac=bet_mag, total_readout=total_readout, - ignore_nodes=ignore_nodes + subject_id=subject_id, dwi_file=dwi_file, metadata=metadata, + parameters=parameters ) datasink_wf = init_output_wf( - subject_id=subject_id, session_id=session_id, output_folder=output_dir + subject_id=subject_id, session_id=session_id, output_folder=parameters.output_dir ) - dwi_preproc_wf.base_dir = os.path.join(os.path.abspath(work_dir), subject_id) + dwi_preproc_wf.base_dir = os.path.join(os.path.abspath(parameters.work_dir), subject_id) inputspec = dwi_preproc_wf.get_node("inputnode") inputspec.inputs.subject_id = subject_id inputspec.inputs.dwi_file = dwi_file inputspec.inputs.metadata = metadata - inputspec.inputs.bvec_file = layout.get_bvec(dwi_file) - inputspec.inputs.bval_file = layout.get_bval(dwi_file) - inputspec.inputs.out_dir = os.path.abspath(output_dir) + inputspec.inputs.bvec_file = parameters.layout.get_bvec(dwi_file) + inputspec.inputs.bval_file = parameters.layout.get_bval(dwi_file) + inputspec.inputs.out_dir = os.path.abspath(parameters.output_dir) ds_inputspec = datasink_wf.get_node("inputnode") ds_inputspec.inputs.subject_id = subject_id ds_inputspec.inputs.session_id = session_id - ds_inputspec.inputs.output_folder = output_dir + ds_inputspec.inputs.output_folder = parameters.output_dir ds_inputspec.inputs.metadata = metadata wf_name = "sub_" + subject_id + "_ses_" + session_id + "_preproc_wf" diff --git a/dmriprep/workflows/dwi/base.py b/dmriprep/workflows/dwi/base.py index fc9e2b83..7788996a 100755 --- a/dmriprep/workflows/dwi/base.py +++ b/dmriprep/workflows/dwi/base.py @@ -20,15 +20,11 @@ def init_dwi_preproc_wf( subject_id, dwi_file, metadata, - layout, - bet_dwi_frac, - bet_mag_frac, - total_readout, - ignore_nodes, + parameters ): fmaps = [] - fmaps = layout.get_fieldmap(dwi_file, return_list=True) + fmaps = parameters.layout.get_fieldmap(dwi_file, return_list=True) if not fmaps: raise Exception( @@ -37,9 +33,9 @@ def init_dwi_preproc_wf( ) for fmap in fmaps: - fmap["metadata"] = layout.get_metadata(fmap[fmap["suffix"]]) + fmap["metadata"] = parameters.layout.get_metadata(fmap[fmap["suffix"]]) - sdc_wf = init_sdc_prep_wf(fmaps, metadata, layout, bet_mag_frac) + sdc_wf = init_sdc_prep_wf(fmaps, metadata, parameters.layout, parameters.bet_mag) dwi_wf = pe.Workflow(name="dwi_preproc_wf") @@ -64,7 +60,7 @@ def init_dwi_preproc_wf( ) # Create the dwi prep workflow - dwi_prep_wf = init_dwiprep_wf(ignore_nodes) + dwi_prep_wf = init_dwiprep_wf(parameters.ignore_nodes) def gen_index(in_file): import os @@ -141,7 +137,7 @@ def gen_acqparams(in_file, metadata, total_readout_time): name="acqp", ) - acqp.inputs.total_readout_time = total_readout + acqp.inputs.total_readout_time = parameters.total_readout def b0_average(in_dwi, in_bval, b0_thresh=10.0, out_file=None): """ @@ -189,7 +185,7 @@ def b0_average(in_dwi, in_bval, b0_thresh=10.0, out_file=None): # dilate mask bet_dwi0 = pe.Node( - fsl.BET(frac=bet_dwi_frac, mask=True, robust=True), name="bet_dwi_pre" + fsl.BET(frac=parameters.bet_dwi, mask=True, robust=True), name="bet_dwi_pre" ) # mrtrix3.MaskFilter From 7dca015bc87ae5c5405f65ef4e384d156fffe542 Mon Sep 17 00:00:00 2001 From: Salim Mansour Date: Fri, 12 Jul 2019 12:39:48 -0400 Subject: [PATCH 063/156] point setup.py to new README name --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a951356e..150c07f2 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ from setuptools import setup, find_packages -with open("README.md") as readme_file: +with open("README.rst") as readme_file: readme = readme_file.read() with open("HISTORY.rst") as history_file: From 2b34b0af3e9f52156775a39ff09b77c7ae19fd69 Mon Sep 17 00:00:00 2001 From: mjoseph Date: Tue, 16 Jul 2019 11:22:05 -0400 Subject: [PATCH 064/156] add wip topup wf --- dmripreproc/workflows/fieldmap/pepolar.py | 79 +++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 dmripreproc/workflows/fieldmap/pepolar.py diff --git a/dmripreproc/workflows/fieldmap/pepolar.py b/dmripreproc/workflows/fieldmap/pepolar.py new file mode 100644 index 00000000..34ca318c --- /dev/null +++ b/dmripreproc/workflows/fieldmap/pepolar.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python + +from nipype.pipeline import engine as pe +from nipype.interfaces import fsl, utility as niu + + +def init_pepolar_wf(subject_id, dwi_meta, epi_fmaps): + + dwi_file_pe = dwi_meta["PhaseEncodingDirection"] + + usable_fieldmaps_matching_pe = [] + usable_fieldmaps_opposite_pe = [] + + for fmap, pe_dir in epi_fmaps: + if pe_dir == dwi_file_pe: + usable_fieldmaps_matching_pe.append(fmap) + elif pe_dir[0] == dwi_file_pe[0]: + usable_fieldmaps_opposite_pe.append(fmap) + + if not usable_fieldmaps_opposite_pe: + raise Exception("None of the discovered fieldmaps for " + "participant {} has the right phase " + "encoding direction".format(subject_id)) + + wf = pe.Workflow(name="pepolar_wf") + + inputnode = pe.Node(niu.IdentityInterface(fields=["b0_stripped"])) + + outputnode = pe.Node(niu.IdentityInterface(fields=["out_topup"])) + + topup_wf = init_topup_wf() + topup_wf.inputnode.altepi_file = usable_fieldmaps_opposite_pe[0] + + if not usable_fieldmaps_matching_pe: + wf.connect( + [ + (inputnode, topup_wf, [("b0_stripped", "inputnode.epi_file")]) + ] + ) + else: + topup_wf.inputnode.epi_file = usable_fieldmaps_matching_pe[0] + + return wf + + +def init_topup_wf(): + + wf = pe.Workflow(name="topup_wf") + + inputnode = pe.Node( + niu.IdentityInterface(fields=["epi_file", "altepi_file", "encoding_directions"]), + name="inputnode") + + outputnode = pe.Node( + niu.IdentityInterface(fields=["out_fmap"]), + name="outputnode") + + list_merge = pe.Node(niu.Merge(numinputs=2), name="list_merge") + + merge = pe.Node(fsl.Merge(dimension="t"), name="mergeAPPA") + + topup = pe.Node(fsl.TOPUP(), name="topup") + topup.inputs.readout_times = [0.05, 0.05] + + wf.connect( + [ + ( + inputnode, + list_merge, + [("epi_file", "in1"), ("altepi_file", "in2")] + ), + (list_merge, merge, [("out", "in_files")]), + (merge, topup, [("merged_file", "in_file")]), + (inputnode, topup, [("encoding_directions", "encoding_direction")]), + (topup, outputnode, [("out_field", "out_fmap")]), + ] + ) + + return wf From 904ed08b3166c38bbb563e123bf27d1da99b0b53 Mon Sep 17 00:00:00 2001 From: Salim Mansour Date: Tue, 16 Jul 2019 12:19:44 -0400 Subject: [PATCH 065/156] Added Dockerfile to generate container for dmripreproc --- Dockerfile | 196 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..490e5845 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,196 @@ +FROM poldracklab/fmriprep:1.3.2 + +# Used command: +# neurodocker generate docker --base=debian:stretch --pkg-manager=apt +# --ants version=latest method=source --mrtrix3 version=3.0_RC3 +# --freesurfer version=6.0.0 method=binaries --fsl version=6.0.1 method=binaries + +# Getting required installation tools +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + bc \ + libtool \ + tar \ + dpkg \ + curl \ + wget \ + unzip \ + gcc \ + git \ + libstdc++6 + +# Neurodocker Setup +ARG DEBIAN_FRONTEND="noninteractive" + +ENV LANG="en_US.UTF-8" \ + LC_ALL="en_US.UTF-8" \ + ND_ENTRYPOINT="/neurodocker/startup.sh" +RUN export ND_ENTRYPOINT="/neurodocker/startup.sh" \ + && apt-get update -qq \ + && apt-get install -y -q --no-install-recommends \ + apt-utils \ + bzip2 \ + ca-certificates \ + curl \ + locales \ + unzip \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* \ + && sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen \ + && dpkg-reconfigure --frontend=noninteractive locales \ + && update-locale LANG="en_US.UTF-8" \ + && chmod 777 /opt && chmod a+s /opt \ + && mkdir -p /neurodocker \ + && if [ ! -f "$ND_ENTRYPOINT" ]; then \ + echo '#!/usr/bin/env bash' >> "$ND_ENTRYPOINT" \ + && echo 'set -e' >> "$ND_ENTRYPOINT" \ + && echo 'export USER="${USER:=`whoami`}"' >> "$ND_ENTRYPOINT" \ + && echo 'if [ -n "$1" ]; then "$@"; else /usr/bin/env bash; fi' >> "$ND_ENTRYPOINT"; \ + fi \ + && chmod -R 777 /neurodocker && chmod a+s /neurodocker + +ENTRYPOINT ["/neurodocker/startup.sh"] + +# ANTS (used from BIDS-Apps https://github.com/BIDS-Apps/dockerfile-templates/blob/master/ANTs/Dockerfile) +RUN apt-get update && \ + apt-get install -y curl && \ + mkdir -p /opt/ants && \ + curl -sSL "https://github.com/stnava/ANTs/releases/download/v2.1.0/Linux_Ubuntu14.04.tar.bz2" \ + | tar -xjC /opt/ants --strip-components 1 && \ + rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +ENV ANTSPATH /opt/ants +ENV PATH $ANTSPATH:$PATH + +# MRtrix3 generated from Neurodocker +ENV PATH="/opt/mrtrix3-3.0_RC3/bin:$PATH" +RUN echo "Downloading MRtrix3 ..." \ + && mkdir -p /opt/mrtrix3-3.0_RC3 \ + && curl -fsSL --retry 5 https://dl.dropbox.com/s/2oh339ehcxcf8xf/mrtrix3-3.0_RC3-Linux-centos6.9-x86_64.tar.gz \ + | tar -xz -C /opt/mrtrix3-3.0_RC3 --strip-components 1 + +# Freesurfer generated from Neurodocker +ENV FREESURFER_HOME="/opt/freesurfer-6.0.0" \ + PATH="/opt/freesurfer-6.0.0/bin:$PATH" +RUN apt-get update -qq \ + && apt-get install -y -q --no-install-recommends \ + bc \ + libgomp1 \ + libxmu6 \ + libxt6 \ + perl \ + tcsh \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* \ + && echo "Downloading FreeSurfer ..." \ + && mkdir -p /opt/freesurfer-6.0.0 \ + && curl -fsSL --retry 5 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/freesurfer-6.0.0 --strip-components 1 \ + --exclude='freesurfer/average/mult-comp-cor' \ + --exclude='freesurfer/lib/cuda' \ + --exclude='freesurfer/lib/qt' \ + --exclude='freesurfer/subjects/V1_average' \ + --exclude='freesurfer/subjects/bert' \ + --exclude='freesurfer/subjects/cvs_avg35' \ + --exclude='freesurfer/subjects/cvs_avg35_inMNI152' \ + --exclude='freesurfer/subjects/fsaverage3' \ + --exclude='freesurfer/subjects/fsaverage4' \ + --exclude='freesurfer/subjects/fsaverage5' \ + --exclude='freesurfer/subjects/fsaverage6' \ + --exclude='freesurfer/subjects/fsaverage_sym' \ + --exclude='freesurfer/trctrain' \ + && sed -i '$isource "/opt/freesurfer-6.0.0/SetUpFreeSurfer.sh"' "$ND_ENTRYPOINT" + +ENV FSLDIR="/opt/fsl-6.0.1" \ + PATH="/opt/fsl-6.0.1/bin:$PATH" +RUN apt-get update -qq \ + && apt-get install -y -q --no-install-recommends \ + bc \ + dc \ + file \ + libfontconfig1 \ + libfreetype6 \ + libgl1-mesa-dev \ + libglu1-mesa-dev \ + libgomp1 \ + libice6 \ + libxcursor1 \ + libxft2 \ + libxinerama1 \ + libxrandr2 \ + libxrender1 \ + libxt6 \ + wget \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* \ + && echo "Downloading FSL ..." \ + && mkdir -p /opt/fsl-6.0.1 \ + && curl -fsSL --retry 5 https://fsl.fmrib.ox.ac.uk/fsldownloads/fsl-6.0.1-centos6_64.tar.gz \ + | tar -xz -C /opt/fsl-6.0.1 --strip-components 1 \ + && sed -i '$iecho Some packages in this Docker container are non-free' $ND_ENTRYPOINT \ + && sed -i '$iecho If you are considering commercial use of this container, please consult the relevant license:' $ND_ENTRYPOINT \ + && sed -i '$iecho https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/Licence' $ND_ENTRYPOINT \ + && sed -i '$isource $FSLDIR/etc/fslconf/fsl.sh' $ND_ENTRYPOINT + +RUN echo '{ \ + \n "pkg_manager": "apt", \ + \n "instructions": [ \ + \n [ \ + \n "base", \ + \n "debian:stretch" \ + \n ], \ + \n [ \ + \n "ants", \ + \n { \ + \n "version": "latest", \ + \n "method": "source" \ + \n } \ + \n ], \ + \n [ \ + \n "mrtrix3", \ + \n { \ + \n "version": "3.0_RC3" \ + \n } \ + \n ], \ + \n [ \ + \n "freesurfer", \ + \n { \ + \n "version": "6.0.0", \ + \n "method": "binaries" \ + \n } \ + \n ], \ + \n [ \ + \n "fsl", \ + \n { \ + \n "version": "6.0.1", \ + \n "method": "binaries" \ + \n } \ + \n ] \ + \n ] \ + \n}' > /neurodocker/neurodocker_specs.json + +#All of the examples below use debian:stretch as the base image, but any Docker image can be used as a base. +#Common base images (and their packages managers) are ubuntu:16.04 (apt), centos:7 (yum), neurodebian:nd16.04 (apt), and neurodebian:stretch (apt). + +# FSL 6.0.1 +# Freesurfer 6.0.0 +# MRtrix3 +# ANTS +# Python 3 + +# add credentials on build +RUN mkdir ~/.ssh && ln -s /run/secrets/host_ssh_key ~/.ssh/id_rsa +# Getting required installation tools +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + libopenblas-base + +# setting up an install of dmripreproc (manual version) inside the container +ADD https://api.github.com/repos/TIGRLab/dmripreproc/git/refs/heads/master version.json +RUN git clone -b master https://github.com/TIGRLab/dmripreproc.git +#RUN mkdir dmripreproc +#COPY ./ dmripreproc/ +RUN cd dmripreproc && ls && python setup.py install && pip list +RUN pip install pybids==0.9.1 && pip list + +ENTRYPOINT ["dmriprep"] From ce154b405e6a839d340da974f20fb7c26666c490 Mon Sep 17 00:00:00 2001 From: Salim Mansour Date: Tue, 16 Jul 2019 12:20:42 -0400 Subject: [PATCH 066/156] Minor requirements and setup changes --- dmriprep/utils.py | 4 ++-- dmriprep/workflows/base.py | 2 +- requirements.txt | 1 + setup.py | 4 ++-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/dmriprep/utils.py b/dmriprep/utils.py index 3914aa67..fe55506f 100644 --- a/dmriprep/utils.py +++ b/dmriprep/utils.py @@ -91,10 +91,10 @@ class BIDSWarning(RuntimeWarning): class Parameters: def __init__(self): self.participant_label = '' + self.layout = None + self.subject_list = '' self.bids_dir = '' self.work_dir = '' - self.layout = '' - self.subject_list = '' self.output_dir = '' self.eddy_niter = 5 self.bet_dwi = 0.3 diff --git a/dmriprep/workflows/base.py b/dmriprep/workflows/base.py index 6c544039..8e6d86d3 100644 --- a/dmriprep/workflows/base.py +++ b/dmriprep/workflows/base.py @@ -41,7 +41,7 @@ def init_single_subject_wf( datatype="dwi", suffix="dwi", extensions=[".nii", ".nii.gz"], - return_type="filename", + return_type="filename" ) if not dwi_files: diff --git a/requirements.txt b/requirements.txt index 29ffc900..7a1dd3ee 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,4 @@ tqdm==4.32.1 pybids==0.9.1 matplotlib==3.1.0 numba==0.44.0 +Sphinx==2.1.2 diff --git a/setup.py b/setup.py index 150c07f2..1223a736 100644 --- a/setup.py +++ b/setup.py @@ -14,11 +14,11 @@ requirements = [ "Click>=6.0", "dipy", - "nipype", + "nipype>=1.2.0", "pandas", "parse", "tqdm", - "pybids", + "pybids>=0.9.1", "matplotlib", "numba", "sphinx", From e824ebc883cb7f9e6242ade7457d8239e11cafec Mon Sep 17 00:00:00 2001 From: mjoseph Date: Tue, 16 Jul 2019 16:46:16 -0400 Subject: [PATCH 067/156] add pe direction to topup workflow --- dmriprep/workflows/dwi/dwiprep.py | 82 -------------------------- dmripreproc/workflows/dwi/base.py | 2 +- dmripreproc/workflows/fieldmap/base.py | 20 ++++++- 3 files changed, 19 insertions(+), 85 deletions(-) delete mode 100644 dmriprep/workflows/dwi/dwiprep.py diff --git a/dmriprep/workflows/dwi/dwiprep.py b/dmriprep/workflows/dwi/dwiprep.py deleted file mode 100644 index 8334cbed..00000000 --- a/dmriprep/workflows/dwi/dwiprep.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env python - -def init_dwiprep_wf(ignore_nodes): - from ...interfaces import mrtrix3 - from nipype.pipeline import engine as pe - from nipype.interfaces import fsl, utility as niu - - # Generate the prep workflow for dwi (denoise, unring, resize) - - dwi_prep_wf = pe.Workflow(name="dwi_prep_wf") - - denoise = pe.Node(mrtrix3.DWIDenoise(), name="denoise") - - unring = pe.Node(mrtrix3.MRDeGibbs(), name="unring") - - resize = pe.Node(mrtrix3.MRResize(voxel_size=[1]), name="resize") - - dwi_prep_inputnode = pe.Node( - niu.IdentityInterface( - fields=["dwi_file"] - ), - name="dwi_prep_inputnode", - ) - - dwi_prep_outputnode = pe.Node( - niu.IdentityInterface(fields=["out_file"]), - name="dwi_prep_outputnode", - ) - - # Turn the string input of nodes into a list - ignore_nodes_list = [option.lower() for option in ignore_nodes.strip()] - - prep_full = ['d', 'u', 'r'] - prep_wanted_str = [node for node in prep_full if not(node in ignore_nodes_list)] - - # Translate string input to node names - str2node = { - 'd': denoise, - 'u': unring, - 'r': resize - } - - # Convert the string list to a node list - prep_wanted = [str2node[str_node] for str_node in prep_wanted_str] - - # If no steps selected, just connect input to output - if not(prep_wanted): - dwi_prep_wf.connect( - [ - (dwi_prep_inputnode, dwi_prep_outputnode, [("dwi_file", "out_file")]) - ] - ) - - # If there are steps - else: - # Must at least connect input node to first node - first_node = prep_wanted[0] - dwi_prep_wf.connect( - [ - (dwi_prep_inputnode, first_node, [("dwi_file", "in_file")]), - ] - ) - # Loop through the prep order - # Note: only works if each node has in_file and out_file - # Can work around this by wrapping in node/workflow with in_file+out_file - prev = first_node - for curr_node in prep_wanted[1:]: - dwi_prep_wf.connect( - [ - (prev, curr_node, [("out_file", "in_file")]), - ] - ) - prev = curr_node - # Connect last node to output node - last_node = prep_wanted[-1] - dwi_prep_wf.connect( - [ - (last_node, dwi_prep_outputnode, [("out_file", "out_file")]), - ] - ) - - return dwi_prep_wf diff --git a/dmripreproc/workflows/dwi/base.py b/dmripreproc/workflows/dwi/base.py index 8a1ae2e7..fdce43d8 100755 --- a/dmripreproc/workflows/dwi/base.py +++ b/dmripreproc/workflows/dwi/base.py @@ -32,7 +32,7 @@ def init_dwi_preproc_wf(subject_id, dwi_file, metadata, parameters): fmap["metadata"] = parameters.layout.get_metadata(fmap[fmap["suffix"]]) sdc_wf = init_sdc_prep_wf( - fmaps, metadata, parameters.layout, parameters.bet_mag + subject_id, fmaps, metadata, parameters.layout, parameters.bet_mag ) dwi_wf = pe.Workflow(name="dwi_preproc_wf") diff --git a/dmripreproc/workflows/fieldmap/base.py b/dmripreproc/workflows/fieldmap/base.py index c84fe674..d8a13258 100644 --- a/dmripreproc/workflows/fieldmap/base.py +++ b/dmripreproc/workflows/fieldmap/base.py @@ -7,7 +7,13 @@ def init_sdc_prep_wf( - fmaps, metadata, layout, bet_mag_frac, omp_nthreads=1, fmap_bspline=False + subject_id, + fmaps, + metadata, + layout, + bet_mag_frac, + omp_nthreads=1, + fmap_bspline=False, ): sdc_prep_wf = pe.Workflow(name="sdc_prep_wf") @@ -37,7 +43,17 @@ def init_sdc_prep_wf( if fmap["suffix"] == "epi": from .pepolar import init_pepolar_wf - pepolar_wf = init_pepolar_wf() + epi_fmaps = [ + (fmap_["epi"], fmap_["metadata"]["PhaseEncodingDirection"]) + for fmap_ in fmaps + if fmap_["suffix"] == "epi" + ] + + pepolar_wf = init_pepolar_wf(subject_id, metadata, epi_fmaps) + + sdc_prep_wf.connect( + [(pepolar_wf, outputnode, [("outputnode.out_fmap", "out_fmap")])] + ) if fmap["suffix"] == "fieldmap": from .fmap import init_fmap_wf From 299b7a15162ad1c4bb602f2af9b06f0836d104a6 Mon Sep 17 00:00:00 2001 From: Salim Mansour Date: Fri, 19 Jul 2019 10:29:56 -0400 Subject: [PATCH 068/156] Added support for topup --- dmripreproc/cli.py | 15 +- dmripreproc/interfaces/fsl.py | 505 ++++++++++++++++++++++ dmripreproc/workflows/base.py | 12 +- dmripreproc/workflows/dwi/base.py | 38 +- dmripreproc/workflows/dwi/outputs.py | 1 + dmripreproc/workflows/fieldmap/base.py | 21 +- dmripreproc/workflows/fieldmap/pepolar.py | 59 ++- setup.py | 4 +- 8 files changed, 625 insertions(+), 30 deletions(-) create mode 100644 dmripreproc/interfaces/fsl.py diff --git a/dmripreproc/cli.py b/dmripreproc/cli.py index a0a09d22..8a3675a3 100644 --- a/dmripreproc/cli.py +++ b/dmripreproc/cli.py @@ -9,7 +9,7 @@ import click from . import utils -from .workflows.base import init_dmriprepoc_wf +from .workflows.base import init_dmripreproc_wf # Filter warnings that are visible whenever you import another package that # was compiled against an older numpy than is installed. @@ -28,12 +28,11 @@ "can be specified with a space separated list.", default=None, ) -@click.command() -@click.option( - "--ignore", - help="Ignore selected parts of the workflow.", - type=click.Choice(["denoise", "unring"]), -) +#@click.option( +# "--ignore", +# help="Ignore selected parts of the workflow.", +# type=click.Choice(["denoise", "unring"]), +#) @click.option( "--resize-scale", help="Scale factor to resize DWI image", type=(float) ) @@ -142,7 +141,7 @@ def main( parameters.ignore_nodes = ignore_nodes parameters.analysis_level = analysis_level - wf = init_dmriprep_wf(parameters) + wf = init_dmripreproc_wf(parameters) wf.write_graph(graph2use="colored") wf.config["execution"]["remove_unnecessary_outputs"] = False wf.config["execution"]["keep_inputs"] = True diff --git a/dmripreproc/interfaces/fsl.py b/dmripreproc/interfaces/fsl.py new file mode 100644 index 00000000..96962133 --- /dev/null +++ b/dmripreproc/interfaces/fsl.py @@ -0,0 +1,505 @@ +#!/usr/bin/env python + +from __future__ import ( + print_function, + division, + unicode_literals, + absolute_import, +) + +from builtins import str + +import os +import numpy as np +import nibabel as nb +import warnings + +from nipype.interfaces.base import traits, TraitedSpec, InputMultiPath, File, isdefined +from nipype.interfaces.fsl.base import FSLCommand, FSLCommandInputSpec, Info + +class EddyInputSpec(FSLCommandInputSpec): + in_file = File( + exists=True, + mandatory=True, + argstr='--imain=%s', + desc=('File containing all the images to estimate ' + 'distortions for')) + in_mask = File( + exists=True, + mandatory=True, + argstr='--mask=%s', + desc='Mask to indicate brain') + in_index = File( + exists=True, + mandatory=True, + argstr='--index=%s', + desc=('File containing indices for all volumes in --imain ' + 'into --acqp and --topup')) + in_acqp = File( + exists=True, + mandatory=True, + argstr='--acqp=%s', + desc='File containing acquisition parameters') + in_bvec = File( + exists=True, + mandatory=True, + argstr='--bvecs=%s', + desc=('File containing the b-vectors for all volumes in ' + '--imain')) + in_bval = File( + exists=True, + mandatory=True, + argstr='--bvals=%s', + desc=('File containing the b-values for all volumes in ' + '--imain')) + out_base = traits.Str( + 'eddy_corrected', + argstr='--out=%s', + usedefault=True, + desc=('basename for output (warped) image')) + session = File( + exists=True, + argstr='--session=%s', + desc=('File containing session indices for all volumes in ' + '--imain')) + in_topup_fieldcoef = File( + #exists=True, + argstr="--topup=%s", + requires=['in_topup_movpar'], + desc=('topup file containing the field ' + 'coefficients')) + in_topup_movpar = File( + exists=True, + requires=['in_topup_fieldcoef'], + desc='topup movpar.txt file') + + flm = traits.Enum( + 'linear', + 'quadratic', + 'cubic', + argstr='--flm=%s', + desc='First level EC model') + + slm = traits.Enum( + 'none', + 'linear', + 'quadratic', + argstr='--slm=%s', + desc='Second level EC model') + + fep = traits.Bool( + False, argstr='--fep', desc='Fill empty planes in x- or y-directions') + + interp = traits.Enum( + 'spline', + 'trilinear', + argstr='--interp=%s', + desc='Interpolation model for estimation step') + + nvoxhp = traits.Int( + 1000, usedefault=True, + argstr='--nvoxhp=%s', + desc=('# of voxels used to estimate the ' + 'hyperparameters')) + + fudge_factor = traits.Float( + 10.0, usedefault=True, + argstr='--ff=%s', + desc=('Fudge factor for hyperparameter ' + 'error variance')) + + dont_sep_offs_move = traits.Bool( + False, + argstr='--dont_sep_offs_move', + desc=('Do NOT attempt to separate ' + 'field offset from subject ' + 'movement')) + + dont_peas = traits.Bool( + False, + argstr='--dont_peas', + desc="Do NOT perform a post-eddy alignment of " + "shells") + + fwhm = traits.Float( + desc=('FWHM for conditioning filter when estimating ' + 'the parameters'), + argstr='--fwhm=%s') + + niter = traits.Int(5, usedefault=True, + argstr='--niter=%s', desc='Number of iterations') + + method = traits.Enum( + 'jac', + 'lsr', + argstr='--resamp=%s', + desc=('Final resampling method (jacobian/least ' + 'squares)')) + repol = traits.Bool( + False, argstr='--repol', desc='Detect and replace outlier slices') + num_threads = traits.Int( + 1, + usedefault=True, + nohash=True, + desc="Number of openmp threads to use") + is_shelled = traits.Bool( + False, + argstr='--data_is_shelled', + desc="Override internal check to ensure that " + "date are acquired on a set of b-value " + "shells") + field = traits.Str( + argstr='--field=%s', + desc="NonTOPUP fieldmap scaled in Hz - filename has " + "to be provided without an extension. TOPUP is " + "strongly recommended") + field_mat = File( + exists=True, + argstr='--field_mat=%s', + desc="Matrix that specifies the relative locations of " + "the field specified by --field and first volume " + "in file --imain") + use_cuda = traits.Bool(False, desc="Run eddy using cuda gpu") + cnr_maps = traits.Bool( + False, desc='Output CNR-Maps', argstr='--cnr_maps', min_ver='5.0.10') + residuals = traits.Bool( + False, desc='Output Residuals', argstr='--residuals', min_ver='5.0.10') + + +class EddyOutputSpec(TraitedSpec): + out_corrected = File( + exists=True, desc='4D image file containing all the corrected volumes') + out_parameter = File( + exists=True, + desc=('text file with parameters definining the field and' + 'movement for each scan')) + out_rotated_bvecs = File( + exists=True, desc='File containing rotated b-values for all volumes') + out_movement_rms = File( + exists=True, desc='Summary of the "total movement" in each volume') + out_restricted_movement_rms = File( + exists=True, + desc=('Summary of the "total movement" in each volume ' + 'disregarding translation in the PE direction')) + out_shell_alignment_parameters = File( + exists=True, + desc=('File containing rigid body movement parameters ' + 'between the different shells as estimated by a ' + 'post-hoc mutual information based registration')) + out_outlier_report = File( + exists=True, + desc=('Text-file with a plain language report on what ' + 'outlier slices eddy has found')) + out_cnr_maps = File( + exists=True, desc='path/name of file with the cnr_maps') + out_residuals = File( + exists=True, desc='path/name of file with the residuals') + + +class Eddy(FSLCommand): + """ + Interface for FSL eddy, a tool for estimating and correcting eddy + currents induced distortions. `User guide + `_ and + `more info regarding acqp file + `_. + Examples + -------- + >>> from nipype.interfaces.fsl import Eddy + >>> eddy = Eddy() + >>> eddy.inputs.in_file = 'epi.nii' + >>> eddy.inputs.in_mask = 'epi_mask.nii' + >>> eddy.inputs.in_index = 'epi_index.txt' + >>> eddy.inputs.in_acqp = 'epi_acqp.txt' + >>> eddy.inputs.in_bvec = 'bvecs.scheme' + >>> eddy.inputs.in_bval = 'bvals.scheme' + >>> eddy.inputs.use_cuda = True + >>> eddy.cmdline # doctest: +ELLIPSIS + 'eddy_cuda --ff=10.0 --acqp=epi_acqp.txt --bvals=bvals.scheme \ +--bvecs=bvecs.scheme --imain=epi.nii --index=epi_index.txt \ +--mask=epi_mask.nii --niter=5 --nvoxhp=1000 --out=.../eddy_corrected' + >>> eddy.inputs.use_cuda = False + >>> eddy.cmdline # doctest: +ELLIPSIS + 'eddy_openmp --ff=10.0 --acqp=epi_acqp.txt --bvals=bvals.scheme \ +--bvecs=bvecs.scheme --imain=epi.nii --index=epi_index.txt \ +--mask=epi_mask.nii --niter=5 --nvoxhp=1000 --out=.../eddy_corrected' + >>> res = eddy.run() # doctest: +SKIP + """ + _cmd = 'eddy_openmp' + input_spec = EddyInputSpec + output_spec = EddyOutputSpec + + _num_threads = 1 + + def __init__(self, **inputs): + super(Eddy, self).__init__(**inputs) + self.inputs.on_trait_change(self._num_threads_update, 'num_threads') + if not isdefined(self.inputs.num_threads): + self.inputs.num_threads = self._num_threads + else: + self._num_threads_update() + self.inputs.on_trait_change(self._use_cuda, 'use_cuda') + if isdefined(self.inputs.use_cuda): + self._use_cuda() + + def _num_threads_update(self): + self._num_threads = self.inputs.num_threads + if not isdefined(self.inputs.num_threads): + if 'OMP_NUM_THREADS' in self.inputs.environ: + del self.inputs.environ['OMP_NUM_THREADS'] + else: + self.inputs.environ['OMP_NUM_THREADS'] = str( + self.inputs.num_threads) + + def _use_cuda(self): + self._cmd = 'eddy_cuda' if self.inputs.use_cuda else 'eddy_openmp' + + def _run_interface(self, runtime): + # If 'eddy_openmp' is missing, use 'eddy' + FSLDIR = os.getenv('FSLDIR', '') + cmd = self._cmd + if all((FSLDIR != '', cmd == 'eddy_openmp', + not os.path.exists(os.path.join(FSLDIR, 'bin', cmd)))): + self._cmd = 'eddy' + runtime = super(Eddy, self)._run_interface(runtime) + + # Restore command to avoid side-effects + self._cmd = cmd + return runtime + + def _format_arg(self, name, spec, value): + if name == 'in_topup_fieldcoef': + return spec.argstr % value.split('_fieldcoef')[0] + if name == 'out_base': + return spec.argstr % os.path.abspath(value) + return super(Eddy, self)._format_arg(name, spec, value) + + def _list_outputs(self): + outputs = self.output_spec().get() + outputs['out_corrected'] = os.path.abspath( + '%s.nii.gz' % self.inputs.out_base) + outputs['out_parameter'] = os.path.abspath( + '%s.eddy_parameters' % self.inputs.out_base) + + # File generation might depend on the version of EDDY + out_rotated_bvecs = os.path.abspath( + '%s.eddy_rotated_bvecs' % self.inputs.out_base) + out_movement_rms = os.path.abspath( + '%s.eddy_movement_rms' % self.inputs.out_base) + out_restricted_movement_rms = os.path.abspath( + '%s.eddy_restricted_movement_rms' % self.inputs.out_base) + out_shell_alignment_parameters = os.path.abspath( + '%s.eddy_post_eddy_shell_alignment_parameters' % + self.inputs.out_base) + out_outlier_report = os.path.abspath( + '%s.eddy_outlier_report' % self.inputs.out_base) + if isdefined(self.inputs.cnr_maps) and self.inputs.cnr_maps: + out_cnr_maps = os.path.abspath( + '%s.eddy_cnr_maps.nii.gz' % self.inputs.out_base) + if os.path.exists(out_cnr_maps): + outputs['out_cnr_maps'] = out_cnr_maps + if isdefined(self.inputs.residuals) and self.inputs.residuals: + out_residuals = os.path.abspath( + '%s.eddy_residuals.nii.gz' % self.inputs.out_base) + if os.path.exists(out_residuals): + outputs['out_residuals'] = out_residuals + + if os.path.exists(out_rotated_bvecs): + outputs['out_rotated_bvecs'] = out_rotated_bvecs + if os.path.exists(out_movement_rms): + outputs['out_movement_rms'] = out_movement_rms + if os.path.exists(out_restricted_movement_rms): + outputs['out_restricted_movement_rms'] = \ + out_restricted_movement_rms + if os.path.exists(out_shell_alignment_parameters): + outputs['out_shell_alignment_parameters'] = \ + out_shell_alignment_parameters + if os.path.exists(out_outlier_report): + outputs['out_outlier_report'] = out_outlier_report + + return outputs + +class EddyQuadInputSpec(FSLCommandInputSpec): + base_name = traits.Str( + 'eddy_corrected', + usedefault=True, + argstr='%s', + desc=("Basename (including path) for EDDY output files, i.e., " + "corrected images and QC files"), + position=0, + ) + idx_file = File( + exists=True, + mandatory=True, + argstr="--eddyIdx %s", + desc=("File containing indices for all volumes into acquisition " + "parameters") + ) + param_file = File( + exists=True, + mandatory=True, + argstr="--eddyParams %s", + desc="File containing acquisition parameters" + ) + mask_file = File( + exists=True, + mandatory=True, + argstr="--mask %s", + desc="Binary mask file" + ) + bval_file = File( + exists=True, + mandatory=True, + argstr="--bvals %s", + desc="b-values file" + ) + bvec_file = File( + exists=True, + argstr="--bvecs %s", + desc=("b-vectors file - only used when .eddy_residuals " + "file is present") + ) + output_dir = traits.Str( + name_template='%s.qc', + name_source=['base_name'], + argstr='--output-dir %s', + desc="Output directory - default = '.qc'", + ) + field = File( + exists=True, + argstr='--field %s', + desc="TOPUP estimated field (in Hz)", + ) + slice_spec = File( + exists=True, + argstr='--slspec %s', + desc="Text file specifying slice/group acquisition", + ) + verbose = traits.Bool( + argstr='--verbose', + desc="Display debug messages", + ) + + +class EddyQuadOutputSpec(TraitedSpec): + qc_json = File( + exists=True, + desc=("Single subject database containing quality metrics and data " + "info.") + ) + qc_pdf = File( + exists=True, + desc="Single subject QC report." + ) + avg_b_png = traits.List( + File(exists=True), + desc=("Image showing mid-sagittal, -coronal and -axial slices of " + "each averaged b-shell volume.") + ) + avg_b0_pe_png = traits.List( + File(exists=True), + desc=("Image showing mid-sagittal, -coronal and -axial slices of " + "each averaged pe-direction b0 volume. Generated when using " + "the -f option.") + ) + cnr_png = traits.List( + File(exists=True), + desc=("Image showing mid-sagittal, -coronal and -axial slices of " + "each b-shell CNR volume. Generated when CNR maps are " + "available.") + ) + vdm_png = File( + exists=True, + desc=("Image showing mid-sagittal, -coronal and -axial slices of " + "the voxel displacement map. Generated when using the -f " + "option.") + ) + residuals = File( + exists=True, + desc=("Text file containing the volume-wise mask-averaged squared " + "residuals. Generated when residual maps are available.") + ) + clean_volumes = File( + exists=True, + desc=("Text file containing a list of clean volumes, based on " + "the eddy squared residuals. To generate a version of the " + "pre-processed dataset without outlier volumes, use: " + "`fslselectvols -i -o " + "eddy_corrected_data_clean --vols=vols_no_outliers.txt`") + ) + + +class EddyQuad(FSLCommand): + """ + Interface for FSL eddy_quad, a tool for generating single subject reports + and storing the quality assessment indices for each subject. + `User guide `_ + Examples + -------- + >>> from nipype.interfaces.fsl import EddyQuad + >>> quad = EddyQuad() + >>> quad.inputs.base_name = 'eddy_corrected' + >>> quad.inputs.idx_file = 'epi_index.txt' + >>> quad.inputs.param_file = 'epi_acqp.txt' + >>> quad.inputs.mask_file = 'epi_mask.nii' + >>> quad.inputs.bval_file = 'bvals.scheme' + >>> quad.inputs.bvec_file = 'bvecs.scheme' + >>> quad.inputs.output_dir = 'eddy_corrected.qc' + >>> quad.inputs.field = 'fieldmap_phase_fslprepared.nii' + >>> quad.inputs.verbose = True + >>> quad.cmdline + 'eddy_quad eddy_corrected --bvals bvals.scheme --bvecs bvecs.scheme \ +--field fieldmap_phase_fslprepared.nii --eddyIdx epi_index.txt \ +--mask epi_mask.nii --output-dir eddy_corrected.qc --eddyParams epi_acqp.txt \ +--verbose' + >>> res = quad.run() # doctest: +SKIP + """ + _cmd = 'eddy_quad' + input_spec = EddyQuadInputSpec + output_spec = EddyQuadOutputSpec + + def _list_outputs(self): + from glob import glob + outputs = self.output_spec().get() + + # If the output directory isn't defined, the interface seems to use + # the default but not set its value in `self.inputs.output_dir` + if not isdefined(self.inputs.output_dir): + out_dir = os.path.abspath(os.path.basename(self.inputs.base_name) + '.qc') + else: + out_dir = os.path.abspath(self.inputs.output_dir) + + outputs['qc_json'] = os.path.join(out_dir, 'qc.json') + outputs['qc_pdf'] = os.path.join(out_dir, 'qc.pdf') + + # Grab all b* files here. This will also grab the b0_pe* files + # as well, but only if the field input was provided. So we'll remove + # them later in the next conditional. + outputs['avg_b_png'] = sorted(glob( + os.path.join(out_dir, 'avg_b*.png') + )) + + if isdefined(self.inputs.field): + outputs['avg_b0_pe_png'] = sorted(glob( + os.path.join(out_dir, 'avg_b0_pe*.png') + )) + + # The previous glob for `avg_b_png` also grabbed the + # `avg_b0_pe_png` files so we have to remove them + # from `avg_b_png`. + for fname in outputs['avg_b0_pe_png']: + outputs['avg_b_png'].remove(fname) + + outputs['vdm_png'] = os.path.join(out_dir, 'vdm.png') + + outputs['cnr_png'] = sorted(glob(os.path.join(out_dir, 'cnr*.png'))) + + residuals = os.path.join(out_dir, 'eddy_msr.txt') + if os.path.isfile(residuals): + outputs['residuals'] = residuals + + clean_volumes = os.path.join(out_dir, 'vols_no_outliers.txt') + if os.path.isfile(clean_volumes): + outputs['clean_volumes'] = clean_volumes + + return outputs diff --git a/dmripreproc/workflows/base.py b/dmripreproc/workflows/base.py index d7e8aa66..20f86365 100644 --- a/dmripreproc/workflows/base.py +++ b/dmripreproc/workflows/base.py @@ -7,9 +7,9 @@ from .dwi import init_dwi_preproc_wf, init_output_wf -def init_dmriprep_wf(parameters): - dmriprep_wf = pe.Workflow(name="dmriprep_wf") - dmriprep_wf.base_dir = parameters.work_dir +def init_dmripreproc_wf(parameters): + dmripreproc_wf = pe.Workflow(name="dmripreproc_wf") + dmripreproc_wf.base_dir = parameters.work_dir for subject_id in parameters.subject_list: @@ -26,9 +26,9 @@ def init_dmriprep_wf(parameters): for node in single_subject_wf._get_all_nodes(): node.config = deepcopy(single_subject_wf.config) - dmriprepoc_wf.add_nodes([single_subject_wf]) + dmripreproc_wf.add_nodes([single_subject_wf]) - return dmriprepoc_wf + return dmripreproc_wf def init_single_subject_wf(subject_id, name, parameters): @@ -72,7 +72,7 @@ def init_single_subject_wf(subject_id, name, parameters): inputspec = dwi_preproc_wf.get_node("inputnode") inputspec.inputs.subject_id = subject_id inputspec.inputs.dwi_file = dwi_file - inputspec.inputs.metadata = metadata + inputspec.inputs.dwi_meta = metadata inputspec.inputs.bvec_file = parameters.layout.get_bvec(dwi_file) inputspec.inputs.bval_file = parameters.layout.get_bval(dwi_file) inputspec.inputs.out_dir = os.path.abspath(parameters.output_dir) diff --git a/dmripreproc/workflows/dwi/base.py b/dmripreproc/workflows/dwi/base.py index fdce43d8..9c775d41 100755 --- a/dmripreproc/workflows/dwi/base.py +++ b/dmripreproc/workflows/dwi/base.py @@ -13,9 +13,11 @@ from numba import cuda from ...interfaces import mrtrix3 +from ...interfaces import fsl as dmri_fsl from ..fieldmap.base import init_sdc_prep_wf from .dwiprep import init_dwiprep_wf +FMAP_PRIORITY = {"epi": 0, "fieldmap": 1, "phasediff": 2, "phase": 3, "syn": 4} def init_dwi_preproc_wf(subject_id, dwi_file, metadata, parameters): @@ -42,7 +44,7 @@ def init_dwi_preproc_wf(subject_id, dwi_file, metadata, parameters): fields=[ "subject_id", "dwi_file", - "di_meta", + "dwi_meta", "bvec_file", "bval_file", "out_dir", @@ -190,7 +192,7 @@ def b0_average(in_dwi, in_bval, b0_thresh=10.0, out_file=None): # mrtrix3.MaskFilter ecc = pe.Node( - fsl.Eddy(repol=True, cnr_maps=True, residuals=True, method="jac"), + dmri_fsl.Eddy(repol=True, cnr_maps=True, residuals=True, method="jac"), name="fsl_eddy", ) @@ -238,6 +240,35 @@ def get_b0_mask_fn(b0_file): name="getB0Mask", ) + # Decide what ecc will take: topup or fmap + fmaps.sort(key=lambda fmap: FMAP_PRIORITY[fmap["suffix"]]) + fmap = fmaps[0] + # If epi files detected + if fmap["suffix"] == "epi" or True: + dwi_wf.connect( + [ + ( + sdc_wf, + ecc, + [ + ("outputnode.out_topup", "in_topup_fieldcoef"), + ("outputnode.out_enc_file", "in_acqp"), + ("outputnode.out_movpar", "in_topup_movpar"), + ], + ), + (sdc_wf, eddy_quad, [("outputnode.out_enc_file", "param_file")]) + ] + ) + # Otherwise (fieldmaps) + else: + dwi_wf.connect( + [ + (sdc_wf, ecc, [(("outputnode.out_fmap", get_path), "field")]), + (acqp, ecc, [("out_file", "in_acqp")]), + (acqp, eddy_quad, [("out_file", "param_file")]) + ] + ) + dtifit = pe.Node(fsl.DTIFit(save_tensor=True, sse=True), name="dtifit") dwi_wf.connect( @@ -268,7 +299,6 @@ def get_b0_mask_fn(b0_file): ), (bet_dwi0, ecc, [("mask_file", "in_mask")]), (gen_idx, ecc, [("out_file", "in_index")]), - (acqp, ecc, [("out_file", "in_acqp")]), (ecc, denoise_eddy, [("out_corrected", "in_file")]), (ecc, fslroi, [("out_corrected", "in_file")]), (fslroi, b0mask_node, [("roi_file", "b0_file")]), @@ -284,12 +314,10 @@ def get_b0_mask_fn(b0_file): (ecc, eddy_quad, [("out_rotated_bvecs", "bvec_file")]), (b0mask_node, eddy_quad, [("mask_file", "mask_file")]), (gen_idx, eddy_quad, [("out_file", "idx_file")]), - (acqp, eddy_quad, [("out_file", "param_file")]), (ecc, outputnode, [("out_corrected", "out_file")]), (b0mask_node, outputnode, [("mask_file", "out_mask")]), (ecc, outputnode, [("out_rotated_bvecs", "out_bvec")]), (bet_dwi0, sdc_wf, [("out_file", "inputnode.b0_stripped")]), - (sdc_wf, ecc, [(("outputnode.out_fmap", get_path), "field")]), (sdc_wf, eddy_quad, [("outputnode.out_fmap", "field")]), ( ecc, diff --git a/dmripreproc/workflows/dwi/outputs.py b/dmripreproc/workflows/dwi/outputs.py index a69322f9..3d09c9de 100644 --- a/dmripreproc/workflows/dwi/outputs.py +++ b/dmripreproc/workflows/dwi/outputs.py @@ -33,6 +33,7 @@ def init_output_wf(subject_id, session_id, output_folder): ) def build_path(output_folder, subject_id, session_id): + import os return os.path.join( output_folder, diff --git a/dmripreproc/workflows/fieldmap/base.py b/dmripreproc/workflows/fieldmap/base.py index d8a13258..c656e723 100644 --- a/dmripreproc/workflows/fieldmap/base.py +++ b/dmripreproc/workflows/fieldmap/base.py @@ -26,12 +26,15 @@ def init_sdc_prep_wf( niu.IdentityInterface( fields=[ "out_fmap", + "out_topup", "bold_ref", "bold_mask", "bold_ref_brain", "out_warp", "syn_bold_ref", "method", + "out_movpar", + "out_enc_file", ] ), name="outputnode", @@ -52,7 +55,23 @@ def init_sdc_prep_wf( pepolar_wf = init_pepolar_wf(subject_id, metadata, epi_fmaps) sdc_prep_wf.connect( - [(pepolar_wf, outputnode, [("outputnode.out_fmap", "out_fmap")])] + [ + ( + inputnode, + pepolar_wf, + [("b0_stripped", "inputnode.b0_stripped")], + ), + ( + pepolar_wf, + outputnode, + [ + ("outputnode.out_topup", "out_topup"), + ("outputnode.out_movpar", "out_movpar"), + ("outputnode.out_enc_file", "out_enc_file"), + ("outputnode.out_fmap", "out_fmap") + ], + ) + ] ) if fmap["suffix"] == "fieldmap": diff --git a/dmripreproc/workflows/fieldmap/pepolar.py b/dmripreproc/workflows/fieldmap/pepolar.py index 34ca318c..413299c5 100644 --- a/dmripreproc/workflows/fieldmap/pepolar.py +++ b/dmripreproc/workflows/fieldmap/pepolar.py @@ -8,14 +8,18 @@ def init_pepolar_wf(subject_id, dwi_meta, epi_fmaps): dwi_file_pe = dwi_meta["PhaseEncodingDirection"] + file2dir = dict() + usable_fieldmaps_matching_pe = [] usable_fieldmaps_opposite_pe = [] for fmap, pe_dir in epi_fmaps: if pe_dir == dwi_file_pe: usable_fieldmaps_matching_pe.append(fmap) + file2dir[fmap] = pe_dir elif pe_dir[0] == dwi_file_pe[0]: usable_fieldmaps_opposite_pe.append(fmap) + file2dir[fmap] = pe_dir if not usable_fieldmaps_opposite_pe: raise Exception("None of the discovered fieldmaps for " @@ -24,12 +28,13 @@ def init_pepolar_wf(subject_id, dwi_meta, epi_fmaps): wf = pe.Workflow(name="pepolar_wf") - inputnode = pe.Node(niu.IdentityInterface(fields=["b0_stripped"])) + inputnode = pe.Node(niu.IdentityInterface(fields=["b0_stripped"]), name = "inputnode") - outputnode = pe.Node(niu.IdentityInterface(fields=["out_topup"])) + outputnode = pe.Node(niu.IdentityInterface(fields=["out_topup", "out_movpar", "out_fmap", "out_enc_file"]), name = "outputnode") topup_wf = init_topup_wf() - topup_wf.inputnode.altepi_file = usable_fieldmaps_opposite_pe[0] + topup_wf.inputs.inputnode.altepi_file = usable_fieldmaps_opposite_pe[0] + wf.add_nodes([inputnode]) if not usable_fieldmaps_matching_pe: wf.connect( @@ -38,7 +43,33 @@ def init_pepolar_wf(subject_id, dwi_meta, epi_fmaps): ] ) else: - topup_wf.inputnode.epi_file = usable_fieldmaps_matching_pe[0] + topup_wf.inputs.inputnode.epi_file = usable_fieldmaps_matching_pe[0] + + epi_list = [topup_wf.inputs.inputnode.epi_file, topup_wf.inputs.inputnode.altepi_file] + dir_map = { + 'i': 'x', + 'i-': 'x-', + 'j': 'y', + 'j-': 'y-', + 'k': 'z', + 'k-': 'z-' + } + topup_wf.inputs.inputnode.encoding_directions = [dir_map[file2dir[file]] for file in epi_list] + + wf.connect( + [ + ( + topup_wf, + outputnode, + [ + ("outputnode.out_fmap", "out_fmap"), + ("outputnode.out_movpar", "out_movpar"), + ("outputnode.out_base", "out_topup"), + ("outputnode.out_enc_file", "out_enc_file"), + ] + ), + ] + ) return wf @@ -48,11 +79,12 @@ def init_topup_wf(): wf = pe.Workflow(name="topup_wf") inputnode = pe.Node( - niu.IdentityInterface(fields=["epi_file", "altepi_file", "encoding_directions"]), + niu.IdentityInterface(fields=["epi_file", "altepi_file", "encoding_directions", "topup_name"]), name="inputnode") + inputnode.inputs.topup_name = "topup_base" outputnode = pe.Node( - niu.IdentityInterface(fields=["out_fmap"]), + niu.IdentityInterface(fields=["out_fmap", "out_movpar", "out_base", "out_enc_file"]), name="outputnode") list_merge = pe.Node(niu.Merge(numinputs=2), name="list_merge") @@ -62,6 +94,8 @@ def init_topup_wf(): topup = pe.Node(fsl.TOPUP(), name="topup") topup.inputs.readout_times = [0.05, 0.05] + get_base_movpar = lambda x: x.split("_movpar.txt")[0] + wf.connect( [ ( @@ -70,9 +104,18 @@ def init_topup_wf(): [("epi_file", "in1"), ("altepi_file", "in2")] ), (list_merge, merge, [("out", "in_files")]), - (merge, topup, [("merged_file", "in_file")]), (inputnode, topup, [("encoding_directions", "encoding_direction")]), - (topup, outputnode, [("out_field", "out_fmap")]), + (merge, topup, [("merged_file", "in_file")]), + ( + topup, + outputnode, + [ + ("out_field", "out_fmap"), + ("out_movpar", "out_movpar"), + ("out_enc_file", "out_enc_file"), + (("out_movpar", get_base_movpar), "out_base") + ] + ), ] ) diff --git a/setup.py b/setup.py index 90e755b3..0a14d1b0 100644 --- a/setup.py +++ b/setup.py @@ -45,7 +45,7 @@ description="Preprocessing of neuroimaging data in preparation for AFQ analysis", entry_points={ "console_scripts": [ - "dmriprepoc=dmriprepoc.cli:main", + "dmriprepoc=dmripreproc.cli:main", "dmriprepoc-data=dmriprepoc.cli:data", "dmriprepoc-upload=dmriprepoc.cli:upload", ] @@ -57,7 +57,7 @@ include_package_data=True, keywords="dmriprepoc", name="dmriprepoc", - packages=find_packages(include=["dmriprepoc*"]), + packages=find_packages(include=["dmripreproc*"]), setup_requires=setup_requirements, test_suite="tests", tests_require=test_requirements, From dc30025d94cd945e8926c32193090257910a53d2 Mon Sep 17 00:00:00 2001 From: Salim Mansour Date: Fri, 19 Jul 2019 11:04:17 -0400 Subject: [PATCH 069/156] dmripreproc renaming --- .github/ISSUE_TEMPLATE.md | 2 +- .travis.yml | 2 +- CONTRIBUTING.rst | 28 ++++++++++++++-------------- Makefile | 8 ++++---- README.rst | 24 ++++++++++++------------ dmripreproc/__init__.py | 8 ++++---- dmripreproc/cli.py | 4 ++-- dmripreproc/workflows/dwi/outputs.py | 2 +- docs/Makefile | 2 +- docs/conf.py | 26 +++++++++++++------------- docs/dmriprep.interfaces.rst | 12 ++++++------ docs/dmriprep.rst | 20 ++++++++++---------- docs/dmriprep.workflows.dwi.rst | 16 ++++++++-------- docs/dmriprep.workflows.fieldmap.rst | 16 ++++++++-------- docs/dmriprep.workflows.rst | 12 ++++++------ docs/img/dmriprep_icon.svg | 4 ++-- docs/index.rst | 2 +- docs/installation.rst | 16 ++++++++-------- docs/make.bat | 2 +- docs/modules.rst | 4 ++-- docs/usage.rst | 4 ++-- setup.cfg | 2 +- setup.py | 12 ++++++------ tests/test_dmriprep.py | 4 ++-- tests/test_utils.py | 4 ++-- tox.ini | 2 +- 26 files changed, 119 insertions(+), 119 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index b1e3287a..48e96685 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,4 +1,4 @@ -* dmriprepoc version: +* dmripreproc version: * Python version: * Operating System: diff --git a/.travis.yml b/.travis.yml index 3f5e8338..bd44759d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,5 +24,5 @@ deploy: secure: PLEASE_REPLACE_ME on: tags: true - repo: tigrlab/dmriprepocroc + repo: tigrlab/dmripreprocroc python: 3.7 diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 7b7906a9..0162eab2 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -7,10 +7,10 @@ Contributing Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given. -Installing a development version of dmriprepoc +Installing a development version of dmripreproc -------------------------------------------- -First, you can install a development version of dmriprepoc by cloning this repository +First, you can install a development version of dmripreproc by cloning this repository and then typing:: $ pip install -e .[dev] @@ -32,7 +32,7 @@ You can contribute in many ways: Report Bugs ~~~~~~~~~~~ -Report bugs at https://github.com/nipy/dmriprepoc/issues. +Report bugs at https://github.com/nipy/dmripreproc/issues. If you are reporting a bug, please include: @@ -55,14 +55,14 @@ and "help wanted" is open to whoever wants to implement it. Write Documentation ~~~~~~~~~~~~~~~~~~~ -dmriprepoc could always use more documentation, whether as part of the -official dmriprepoc docs, in docstrings, or even on the web in blog posts, +dmripreproc could always use more documentation, whether as part of the +official dmripreproc docs, in docstrings, or even on the web in blog posts, articles, and such. Submit Feedback ~~~~~~~~~~~~~~~ -The best way to send feedback is to file an issue at https://github.com/nipy/dmriprepoc/issues. +The best way to send feedback is to file an issue at https://github.com/nipy/dmripreproc/issues. If you are proposing a feature: @@ -74,17 +74,17 @@ If you are proposing a feature: Get Started! ------------ -Ready to contribute? Here's how to set up `dmriprepoc` for local development. +Ready to contribute? Here's how to set up `dmripreproc` for local development. -1. Fork the `dmriprepoc` repo on GitHub. +1. Fork the `dmripreproc` repo on GitHub. 2. Clone your fork locally:: - $ git clone git@github.com:your_name_here/dmriprepoc.git + $ git clone git@github.com:your_name_here/dmripreproc.git 3. Install your local copy into a virtualenv. Assuming you have virtualenvwrapper installed, this is how you set up your fork for local development:: - $ mkvirtualenv dmriprepoc - $ cd dmriprepoc/ + $ mkvirtualenv dmripreproc + $ cd dmripreproc/ $ python setup.py develop 4. Create a branch for local development:: @@ -96,7 +96,7 @@ Ready to contribute? Here's how to set up `dmriprepoc` for local development. 5. When you're done making changes, check that your changes pass flake8 and the tests, including testing other Python versions with tox:: - $ flake8 dmriprepoc tests + $ flake8 dmripreproc tests $ python setup.py test or py.test $ tox @@ -120,7 +120,7 @@ Before you submit a pull request, check that it meets these guidelines: your new functionality into a function with a docstring, and add the feature to the list in README.rst. 3. The pull request should work for Python 3.5, 3.6 and 3.7, and for PyPy. Check - https://travis-ci.org/nipy/dmriprepoc/pull_requests + https://travis-ci.org/nipy/dmripreproc/pull_requests and make sure that the tests pass for all supported Python versions. When opening a pull request, please use one of the following prefixes: @@ -137,7 +137,7 @@ Tips To run a subset of tests:: -$ py.test tests.test_dmriprepoc +$ py.test tests.test_dmripreproc Deploying diff --git a/Makefile b/Makefile index 88ba6d85..4c048710 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,7 @@ clean-test: ## remove test and coverage artifacts rm -fr .pytest_cache lint: ## check style with flake8 - flake8 dmriprepoc tests + flake8 dmripreproc tests test: ## run tests quickly with the default Python py.test @@ -60,15 +60,15 @@ test-all: ## run tests on every Python version with tox tox coverage: ## check code coverage quickly with the default Python - coverage run --source dmriprepoc -m pytest + coverage run --source dmripreproc -m pytest coverage report -m coverage html $(BROWSER) htmlcov/index.html docs: ## generate Sphinx HTML documentation, including API docs - rm -f docs/dmriprepoc.rst + rm -f docs/dmripreproc.rst rm -f docs/modules.rst - sphinx-apidoc -o docs/ dmriprepoc + sphinx-apidoc -o docs/ dmripreproc $(MAKE) -C docs clean $(MAKE) -C docs html $(BROWSER) docs/_build/html/index.html diff --git a/README.rst b/README.rst index e882d9e6..0426ffce 100644 --- a/README.rst +++ b/README.rst @@ -3,12 +3,12 @@ .. image:: https://img.shields.io/badge/code%20style-black-000000.svg :target: https://github.com/python/black -# dmriprepoc dmriprepoc +# dmripreproc dmripreproc Preprocessing of neuroimaging data in preparation for AFQ analysis * Free software: BSD license -* Documentation: https://dmriprepoc.readthedocs.io. +* Documentation: https://dmripreproc.readthedocs.io. ## Preparing your data @@ -32,28 +32,28 @@ You should have raw data organized in the BIDS format. Also, you should have run ## Quickstart ```bash -git clone https://github.com/nipy/dmriprepoc -cd dmriprepoc +git clone https://github.com/nipy/dmripreproc +cd dmripreproc python setup.py install -dmriprepoc $BIDS_INPUT_DIR $OUTPUT_DIR --participant-label 01 +dmripreproc $BIDS_INPUT_DIR $OUTPUT_DIR --participant-label 01 ``` ```bash -git clone https://github.com/nipy/dmriprepoc -cd dmriprepoc +git clone https://github.com/nipy/dmripreproc +cd dmripreproc make docker # If you don't want to log into the docker image: -docker run -ti -v $BIDS_INPUT_DIR:/inputs -v $OUTPUT_DIR:/outputs dmriprepoc:prod dmriprepoc /inputs /outputs +docker run -ti -v $BIDS_INPUT_DIR:/inputs -v $OUTPUT_DIR:/outputs dmripreproc:prod dmripreproc /inputs /outputs # If you want to log into the image: -docker run -ti -v $BIDS_INPUT_DIR:/inputs -v $OUTPUT_DIR:/outputs dmriprepoc:prod +docker run -ti -v $BIDS_INPUT_DIR:/inputs -v $OUTPUT_DIR:/outputs dmripreproc:prod # Run this inside the docker image: -dmriprepoc /inputs /outpus --participant-label 01 +dmripreproc /inputs /outpus --participant-label 01 ``` ## Features @@ -62,11 +62,11 @@ dmriprepoc /inputs /outpus --participant-label 01 ## Contributing -We love contributions! dmriprepoc is open source, built on open source, +We love contributions! dmripreproc is open source, built on open source, and we'd love to have you hang out in our community. We have developed some [guidelines](CONTRIBUTING.rst) for contributing to -dmriprepoc. +dmripreproc. **Imposter syndrome disclaimer**: We want your help. No, really. diff --git a/dmripreproc/__init__.py b/dmripreproc/__init__.py index ea3a9a84..c1bef0e6 100644 --- a/dmripreproc/__init__.py +++ b/dmripreproc/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -"""Top-level package for dmriprepoc.""" +"""Top-level package for dmripreproc.""" __author__ = """Anisha Keshavan""" __email__ = "anishakeshavan@gmail.com" @@ -22,13 +22,13 @@ # get the log level from environment variable if "DMIRPREP_LOGLEVEL" in os.environ: - loglevel = os.environ["dmriprepoc_LOGLEVEL"] + loglevel = os.environ["dmripreproc_LOGLEVEL"] module_logger.setLevel(getattr(logging, loglevel.upper())) else: module_logger.setLevel(logging.WARNING) # create a file handler -logpath = os.path.join(os.path.expanduser("~"), ".dmriprepoc", "dmriprepoc.log") +logpath = os.path.join(os.path.expanduser("~"), ".dmripreproc", "dmripreproc.log") # Create the config directory if it doesn't exist logdir = os.path.dirname(logpath) @@ -52,4 +52,4 @@ # add the handlers to the logger module_logger.addHandler(handler) -module_logger.info("Started new dmriprepoc session") +module_logger.info("Started new dmripreproc session") diff --git a/dmripreproc/cli.py b/dmripreproc/cli.py index 8a3675a3..3d3eb41c 100644 --- a/dmripreproc/cli.py +++ b/dmripreproc/cli.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -"""Console script for dmriprepoc.""" +"""Console script for dmripreproc.""" import os import sys import warnings @@ -115,7 +115,7 @@ def main( """ if analysis_level is not "participant": raise NotImplementedError( - "The only valid analysis level for dmriprepoc " + "The only valid analysis level for dmripreproc " "is participant at the moment." ) diff --git a/dmripreproc/workflows/dwi/outputs.py b/dmripreproc/workflows/dwi/outputs.py index 3d09c9de..8e1a6f87 100644 --- a/dmripreproc/workflows/dwi/outputs.py +++ b/dmripreproc/workflows/dwi/outputs.py @@ -37,7 +37,7 @@ def build_path(output_folder, subject_id, session_id): return os.path.join( output_folder, - "dmriprepoc", + "dmripreproc", "sub-" + subject_id, "ses-" + session_id, "dwi", diff --git a/docs/Makefile b/docs/Makefile index 22b578e9..de207f3c 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -4,7 +4,7 @@ # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = python -msphinx -SPHINXPROJ = dmriprepoc +SPHINXPROJ = dmripreproc SOURCEDIR = . BUILDDIR = _build diff --git a/docs/conf.py b/docs/conf.py index bbc5fb7f..263b8ac8 100755 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # -# dmriprepoc documentation build configuration file, created by +# dmripreproc documentation build configuration file, created by # sphinx-quickstart on Fri Jun 9 13:47:02 2017. # # This file is execfile()d with the current directory set to its @@ -22,7 +22,7 @@ import sys sys.path.insert(0, os.path.abspath('..')) -import dmriprepoc +import dmripreproc # -- General configuration --------------------------------------------- @@ -47,7 +47,7 @@ master_doc = 'index' # General information about the project. -project = u'dmriprepoc' +project = u'dmripreproc' copyright = u"2018, Anisha Keshavan" author = u"Anisha Keshavan" @@ -56,9 +56,9 @@ # the built documents. # # The short X.Y version. -version = dmriprepoc.__version__ +version = dmripreproc.__version__ # The full version, including alpha/beta/rc tags. -release = dmriprepoc.__version__ +release = dmripreproc.__version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -101,7 +101,7 @@ # -- Options for HTMLHelp output --------------------------------------- # Output file base name for HTML help builder. -htmlhelp_basename = 'dmriprepocdoc' +htmlhelp_basename = 'dmripreprocdoc' # -- Options for LaTeX output ------------------------------------------ @@ -128,8 +128,8 @@ # (source start file, target name, title, author, documentclass # [howto, manual, or own class]). latex_documents = [ - (master_doc, 'dmriprepoc.tex', - u'dmriprepoc Documentation', + (master_doc, 'dmripreproc.tex', + u'dmripreproc Documentation', u'Anisha Keshavan', 'manual'), ] @@ -139,8 +139,8 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - (master_doc, 'dmriprepoc', - u'dmriprepoc Documentation', + (master_doc, 'dmripreproc', + u'dmripreproc Documentation', [author], 1) ] @@ -151,10 +151,10 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'dmriprepoc', - u'dmriprepoc Documentation', + (master_doc, 'dmripreproc', + u'dmripreproc Documentation', author, - 'dmriprepoc', + 'dmripreproc', 'One line description of project.', 'Miscellaneous'), ] diff --git a/docs/dmriprep.interfaces.rst b/docs/dmriprep.interfaces.rst index 81890580..beb6f852 100644 --- a/docs/dmriprep.interfaces.rst +++ b/docs/dmriprep.interfaces.rst @@ -1,21 +1,21 @@ -dmriprepoc.interfaces package +dmripreproc.interfaces package =========================== Submodules ---------- -dmriprepoc.interfaces.fmap module +dmripreproc.interfaces.fmap module ------------------------------- -.. automodule:: dmriprepoc.interfaces.fmap +.. automodule:: dmripreproc.interfaces.fmap :members: :undoc-members: :show-inheritance: -dmriprepoc.interfaces.mrtrix3 module +dmripreproc.interfaces.mrtrix3 module ---------------------------------- -.. automodule:: dmriprepoc.interfaces.mrtrix3 +.. automodule:: dmripreproc.interfaces.mrtrix3 :members: :undoc-members: :show-inheritance: @@ -24,7 +24,7 @@ dmriprepoc.interfaces.mrtrix3 module Module contents --------------- -.. automodule:: dmriprepoc.interfaces +.. automodule:: dmripreproc.interfaces :members: :undoc-members: :show-inheritance: diff --git a/docs/dmriprep.rst b/docs/dmriprep.rst index dd17d085..5f279a7d 100644 --- a/docs/dmriprep.rst +++ b/docs/dmriprep.rst @@ -1,4 +1,4 @@ -dmriprepoc package +dmripreproc package ================ Subpackages @@ -6,32 +6,32 @@ Subpackages .. toctree:: - dmriprepoc.interfaces - dmriprepoc.workflows + dmripreproc.interfaces + dmripreproc.workflows Submodules ---------- -dmriprepoc.cli module +dmripreproc.cli module ------------------- -.. automodule:: dmriprepoc.cli +.. automodule:: dmripreproc.cli :members: :undoc-members: :show-inheritance: -dmriprepoc.qc module +dmripreproc.qc module ------------------ -.. automodule:: dmriprepoc.qc +.. automodule:: dmripreproc.qc :members: :undoc-members: :show-inheritance: -dmriprepoc.utils module +dmripreproc.utils module --------------------- -.. automodule:: dmriprepoc.utils +.. automodule:: dmripreproc.utils :members: :undoc-members: :show-inheritance: @@ -40,7 +40,7 @@ dmriprepoc.utils module Module contents --------------- -.. automodule:: dmriprepoc +.. automodule:: dmripreproc :members: :undoc-members: :show-inheritance: diff --git a/docs/dmriprep.workflows.dwi.rst b/docs/dmriprep.workflows.dwi.rst index 66a921ff..6758e8c9 100644 --- a/docs/dmriprep.workflows.dwi.rst +++ b/docs/dmriprep.workflows.dwi.rst @@ -1,29 +1,29 @@ -dmriprepoc.workflows.dwi package +dmripreproc.workflows.dwi package ============================== Submodules ---------- -dmriprepoc.workflows.dwi.base module +dmripreproc.workflows.dwi.base module ---------------------------------- -.. automodule:: dmriprepoc.workflows.dwi.base +.. automodule:: dmripreproc.workflows.dwi.base :members: :undoc-members: :show-inheritance: -dmriprepoc.workflows.dwi.outputs module +dmripreproc.workflows.dwi.outputs module ------------------------------------- -.. automodule:: dmriprepoc.workflows.dwi.outputs +.. automodule:: dmripreproc.workflows.dwi.outputs :members: :undoc-members: :show-inheritance: -dmriprepoc.workflows.dwi.tensor module +dmripreproc.workflows.dwi.tensor module ------------------------------------ -.. automodule:: dmriprepoc.workflows.dwi.tensor +.. automodule:: dmripreproc.workflows.dwi.tensor :members: :undoc-members: :show-inheritance: @@ -32,7 +32,7 @@ dmriprepoc.workflows.dwi.tensor module Module contents --------------- -.. automodule:: dmriprepoc.workflows.dwi +.. automodule:: dmripreproc.workflows.dwi :members: :undoc-members: :show-inheritance: diff --git a/docs/dmriprep.workflows.fieldmap.rst b/docs/dmriprep.workflows.fieldmap.rst index 6ad3e485..c1139a3d 100644 --- a/docs/dmriprep.workflows.fieldmap.rst +++ b/docs/dmriprep.workflows.fieldmap.rst @@ -1,29 +1,29 @@ -dmriprepoc.workflows.fieldmap package +dmripreproc.workflows.fieldmap package =================================== Submodules ---------- -dmriprepoc.workflows.fieldmap.base module +dmripreproc.workflows.fieldmap.base module --------------------------------------- -.. automodule:: dmriprepoc.workflows.fieldmap.base +.. automodule:: dmripreproc.workflows.fieldmap.base :members: :undoc-members: :show-inheritance: -dmriprepoc.workflows.fieldmap.fmap module +dmripreproc.workflows.fieldmap.fmap module --------------------------------------- -.. automodule:: dmriprepoc.workflows.fieldmap.fmap +.. automodule:: dmripreproc.workflows.fieldmap.fmap :members: :undoc-members: :show-inheritance: -dmriprepoc.workflows.fieldmap.phasediff module +dmripreproc.workflows.fieldmap.phasediff module -------------------------------------------- -.. automodule:: dmriprepoc.workflows.fieldmap.phasediff +.. automodule:: dmripreproc.workflows.fieldmap.phasediff :members: :undoc-members: :show-inheritance: @@ -32,7 +32,7 @@ dmriprepoc.workflows.fieldmap.phasediff module Module contents --------------- -.. automodule:: dmriprepoc.workflows.fieldmap +.. automodule:: dmripreproc.workflows.fieldmap :members: :undoc-members: :show-inheritance: diff --git a/docs/dmriprep.workflows.rst b/docs/dmriprep.workflows.rst index 11d0f655..744abc81 100644 --- a/docs/dmriprep.workflows.rst +++ b/docs/dmriprep.workflows.rst @@ -1,4 +1,4 @@ -dmriprepoc.workflows package +dmripreproc.workflows package ========================== Subpackages @@ -6,16 +6,16 @@ Subpackages .. toctree:: - dmriprepoc.workflows.dwi - dmriprepoc.workflows.fieldmap + dmripreproc.workflows.dwi + dmripreproc.workflows.fieldmap Submodules ---------- -dmriprepoc.workflows.base module +dmripreproc.workflows.base module ------------------------------ -.. automodule:: dmriprepoc.workflows.base +.. automodule:: dmripreproc.workflows.base :members: :undoc-members: :show-inheritance: @@ -24,7 +24,7 @@ dmriprepoc.workflows.base module Module contents --------------- -.. automodule:: dmriprepoc.workflows +.. automodule:: dmripreproc.workflows :members: :undoc-members: :show-inheritance: diff --git a/docs/img/dmriprep_icon.svg b/docs/img/dmriprep_icon.svg index e6df76d0..68296c30 100644 --- a/docs/img/dmriprep_icon.svg +++ b/docs/img/dmriprep_icon.svg @@ -1,9 +1,9 @@ -dmriprepocdmripreproc \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index 18515044..f3ef5b39 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,4 +1,4 @@ -Welcome to dmriprepoc's documentation! +Welcome to dmripreproc's documentation! ====================================== .. toctree:: diff --git a/docs/installation.rst b/docs/installation.rst index d56cd9c9..b618ae6a 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -8,13 +8,13 @@ Installation Stable release -------------- -To install dmriprepoc, run this command in your terminal: +To install dmripreproc, run this command in your terminal: .. code-block:: console - $ pip install dmriprepoc + $ pip install dmripreproc -This is the preferred method to install dmriprepoc, as it will always install the most recent stable release. +This is the preferred method to install dmripreproc, as it will always install the most recent stable release. If you don't have `pip`_ installed, this `Python installation guide`_ can guide you through the process. @@ -26,19 +26,19 @@ you through the process. From sources ------------ -The sources for dmriprepoc can be downloaded from the `Github repo`_. +The sources for dmripreproc can be downloaded from the `Github repo`_. You can either clone the public repository: .. code-block:: console - $ git clone git://github.com/nipy/dmriprepoc + $ git clone git://github.com/nipy/dmripreproc Or download the `tarball`_: .. code-block:: console - $ curl -OL https://github.com/nipy/dmriprepoc/tarball/master + $ curl -OL https://github.com/nipy/dmripreproc/tarball/master Once you have a copy of the source, you can install it with: @@ -47,5 +47,5 @@ Once you have a copy of the source, you can install it with: $ python setup.py install -.. _Github repo: https://github.com/nipy/dmriprepoc -.. _tarball: https://github.com/nipy/dmriprepoc/tarball/master +.. _Github repo: https://github.com/nipy/dmripreproc +.. _tarball: https://github.com/nipy/dmripreproc/tarball/master diff --git a/docs/make.bat b/docs/make.bat index 265138ef..5d078c4d 100644 --- a/docs/make.bat +++ b/docs/make.bat @@ -9,7 +9,7 @@ if "%SPHINXBUILD%" == "" ( ) set SOURCEDIR=. set BUILDDIR=_build -set SPHINXPROJ=dmriprepoc +set SPHINXPROJ=dmripreproc if "%1" == "" goto help diff --git a/docs/modules.rst b/docs/modules.rst index 1e0222cc..6109e78d 100644 --- a/docs/modules.rst +++ b/docs/modules.rst @@ -1,7 +1,7 @@ -dmriprepoc +dmripreproc ======== .. toctree:: :maxdepth: 4 - dmriprepoc + dmripreproc diff --git a/docs/usage.rst b/docs/usage.rst index 56bc3cf4..80bb6949 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -2,6 +2,6 @@ Usage ===== -To use dmriprepoc in a project:: +To use dmripreproc in a project:: - import dmriprepoc + import dmripreproc diff --git a/setup.cfg b/setup.cfg index da6c0ceb..065bb7ef 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,7 +7,7 @@ tag = True search = version='{current_version}' replace = version='{new_version}' -[bumpversion:file:dmriprepoc/__init__.py] +[bumpversion:file:dmripreproc/__init__.py] search = __version__ = '{current_version}' replace = __version__ = '{new_version}' diff --git a/setup.py b/setup.py index 0a14d1b0..1d9ceecb 100644 --- a/setup.py +++ b/setup.py @@ -45,9 +45,9 @@ description="Preprocessing of neuroimaging data in preparation for AFQ analysis", entry_points={ "console_scripts": [ - "dmriprepoc=dmripreproc.cli:main", - "dmriprepoc-data=dmriprepoc.cli:data", - "dmriprepoc-upload=dmriprepoc.cli:upload", + "dmripreproc=dmripreproc.cli:main", + "dmripreproc-data=dmripreproc.cli:data", + "dmripreproc-upload=dmripreproc.cli:upload", ] }, install_requires=requirements, @@ -55,13 +55,13 @@ license="BSD license", long_description=readme + "\n\n" + history, include_package_data=True, - keywords="dmriprepoc", - name="dmriprepoc", + keywords="dmripreproc", + name="dmripreproc", packages=find_packages(include=["dmripreproc*"]), setup_requires=setup_requirements, test_suite="tests", tests_require=test_requirements, - url="https://github.com/nipy/dmriprepoc", + url="https://github.com/nipy/dmripreproc", version="0.1.0", zip_safe=False, ) diff --git a/tests/test_dmriprep.py b/tests/test_dmriprep.py index 783ccb6f..eebb40d8 100644 --- a/tests/test_dmriprep.py +++ b/tests/test_dmriprep.py @@ -1,13 +1,13 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -"""Tests for `dmriprepoc` package.""" +"""Tests for `dmripreproc` package.""" import pytest from click.testing import CliRunner -from dmriprepoc import cli +from dmripreproc import cli @pytest.fixture diff --git a/tests/test_utils.py b/tests/test_utils.py index 1ab5c059..b3f1a662 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,11 +1,11 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -"""Tests for `dmriprepoc` package.""" +"""Tests for `dmripreproc` package.""" import numpy as np -from dmriprepoc.utils import is_hemispherical +from dmripreproc.utils import is_hemispherical def uniform_points_on_sphere(npoints=1, hemisphere=True, rotate=(0, 0, 0)): diff --git a/tox.ini b/tox.ini index 589cce4e..16279fc7 100644 --- a/tox.ini +++ b/tox.ini @@ -10,7 +10,7 @@ python = [testenv:flake8] basepython = python deps = flake8 -commands = flake8 dmriprepoc +commands = flake8 dmripreproc [testenv] setenv = From efe05c0719129625bb93b43dede1e5ca673b1bad Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Sun, 21 Jul 2019 21:31:58 -0400 Subject: [PATCH 070/156] update repository url --- CONTRIBUTING.rst | 6 +++--- README.rst | 4 ++-- requirements.txt | 5 ++--- setup.py | 3 +-- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 0162eab2..849e7da1 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -32,7 +32,7 @@ You can contribute in many ways: Report Bugs ~~~~~~~~~~~ -Report bugs at https://github.com/nipy/dmripreproc/issues. +Report bugs at https://github.com/tigrlab/dmripreproc/issues. If you are reporting a bug, please include: @@ -62,7 +62,7 @@ articles, and such. Submit Feedback ~~~~~~~~~~~~~~~ -The best way to send feedback is to file an issue at https://github.com/nipy/dmripreproc/issues. +The best way to send feedback is to file an issue at https://github.com/tigrlab/dmripreproc/issues. If you are proposing a feature: @@ -120,7 +120,7 @@ Before you submit a pull request, check that it meets these guidelines: your new functionality into a function with a docstring, and add the feature to the list in README.rst. 3. The pull request should work for Python 3.5, 3.6 and 3.7, and for PyPy. Check - https://travis-ci.org/nipy/dmripreproc/pull_requests + https://travis-ci.org/tigrlab/dmripreproc/pull_requests and make sure that the tests pass for all supported Python versions. When opening a pull request, please use one of the following prefixes: diff --git a/README.rst b/README.rst index 0426ffce..52af7567 100644 --- a/README.rst +++ b/README.rst @@ -32,7 +32,7 @@ You should have raw data organized in the BIDS format. Also, you should have run ## Quickstart ```bash -git clone https://github.com/nipy/dmripreproc +git clone https://github.com/tigrlab/dmripreproc cd dmripreproc python setup.py install @@ -40,7 +40,7 @@ dmripreproc $BIDS_INPUT_DIR $OUTPUT_DIR --participant-label 01 ``` ```bash -git clone https://github.com/nipy/dmripreproc +git clone https://github.com/tigrlab/dmripreproc cd dmripreproc make docker diff --git a/requirements.txt b/requirements.txt index a2323d48..59b784e6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,9 @@ Click>=7.0 dipy==0.16.0 nipype==1.2.0 -pandas==0.24.2 +pandas==0.25.0 parse==1.12.0 tqdm==4.32.2 pybids==0.9.2 matplotlib==3.1.1 -numba==0.44.1 -Sphinx==2.1.2 +numba==0.45.0 diff --git a/setup.py b/setup.py index e234e9d5..5c2f7c46 100644 --- a/setup.py +++ b/setup.py @@ -20,8 +20,7 @@ "tqdm", "pybids>=0.9.1", "matplotlib", - "numba", - "sphinx", + "numba" ] extras_require = {"dev": ["flake8", "pytest", "pytest-cov", "pre-commit"]} From ecdbae80e7d774f1139f55fc92f9ce8a9fd66a40 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Sun, 21 Jul 2019 21:32:33 -0400 Subject: [PATCH 071/156] update setup and requirements --- requirements.txt | 1 + requirements_dev.txt | 1 + setup.py | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 59b784e6..8a391489 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +bids-validator==1.2.2 Click>=7.0 dipy==0.16.0 nipype==1.2.0 diff --git a/requirements_dev.txt b/requirements_dev.txt index 5e2741ce..4ff69330 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -6,6 +6,7 @@ flake8==3.7.8 tox==3.13.2 coverage==4.5.3 Sphinx==2.1.2 +sphinx-click==2.2.0 twine==1.13.0 black==19.3b0 versioneer==0.18 diff --git a/setup.py b/setup.py index 5c2f7c46..afb66e57 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,7 @@ "tqdm", "pybids>=0.9.1", "matplotlib", - "numba" + "numba", ] extras_require = {"dev": ["flake8", "pytest", "pytest-cov", "pre-commit"]} @@ -60,7 +60,7 @@ setup_requires=setup_requirements, test_suite="tests", tests_require=test_requirements, - url="https://github.com/nipy/dmripreproc", + url="https://github.com/tigrlab/dmripreproc", version="0.1.0", zip_safe=False, ) From c416b2a3f27c3eb9bfadd4bb23c12fcb98040f71 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Mon, 22 Jul 2019 06:51:03 -0400 Subject: [PATCH 072/156] update docs --- docs/dmripreproc.interfaces.rst | 38 ++++++++++++++++++++ docs/dmripreproc.rst | 47 +++++++++++++++++++++++++ docs/dmripreproc.utils.rst | 30 ++++++++++++++++ docs/dmripreproc.workflows.dwi.rst | 38 ++++++++++++++++++++ docs/dmripreproc.workflows.fieldmap.rst | 46 ++++++++++++++++++++++++ docs/dmripreproc.workflows.rst | 30 ++++++++++++++++ docs/installation.rst | 8 ++--- docs/modules.rst | 2 +- 8 files changed, 234 insertions(+), 5 deletions(-) create mode 100644 docs/dmripreproc.interfaces.rst create mode 100644 docs/dmripreproc.rst create mode 100644 docs/dmripreproc.utils.rst create mode 100644 docs/dmripreproc.workflows.dwi.rst create mode 100644 docs/dmripreproc.workflows.fieldmap.rst create mode 100644 docs/dmripreproc.workflows.rst diff --git a/docs/dmripreproc.interfaces.rst b/docs/dmripreproc.interfaces.rst new file mode 100644 index 00000000..8c3592bb --- /dev/null +++ b/docs/dmripreproc.interfaces.rst @@ -0,0 +1,38 @@ +dmripreproc.interfaces package +============================== + +Submodules +---------- + +dmripreproc.interfaces.fmap module +---------------------------------- + +.. automodule:: dmripreproc.interfaces.fmap + :members: + :undoc-members: + :show-inheritance: + +dmripreproc.interfaces.fsl module +--------------------------------- + +.. automodule:: dmripreproc.interfaces.fsl + :members: + :undoc-members: + :show-inheritance: + +dmripreproc.interfaces.mrtrix3 module +------------------------------------- + +.. automodule:: dmripreproc.interfaces.mrtrix3 + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: dmripreproc.interfaces + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/dmripreproc.rst b/docs/dmripreproc.rst new file mode 100644 index 00000000..8aeb9d0f --- /dev/null +++ b/docs/dmripreproc.rst @@ -0,0 +1,47 @@ +dmripreproc package +=================== + +Subpackages +----------- + +.. toctree:: + + dmripreproc.interfaces + dmripreproc.utils + dmripreproc.workflows + +Submodules +---------- + +dmripreproc.cli module +---------------------- + +.. automodule:: dmripreproc.cli + :members: + :undoc-members: + :show-inheritance: + +dmripreproc.qc module +--------------------- + +.. automodule:: dmripreproc.qc + :members: + :undoc-members: + :show-inheritance: + +dmripreproc.utils module +------------------------ + +.. automodule:: dmripreproc.utils + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: dmripreproc + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/dmripreproc.utils.rst b/docs/dmripreproc.utils.rst new file mode 100644 index 00000000..b66c4b11 --- /dev/null +++ b/docs/dmripreproc.utils.rst @@ -0,0 +1,30 @@ +dmripreproc.utils package +========================= + +Submodules +---------- + +dmripreproc.utils.bids module +----------------------------- + +.. automodule:: dmripreproc.utils.bids + :members: + :undoc-members: + :show-inheritance: + +dmripreproc.utils.image module +------------------------------ + +.. automodule:: dmripreproc.utils.image + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: dmripreproc.utils + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/dmripreproc.workflows.dwi.rst b/docs/dmripreproc.workflows.dwi.rst new file mode 100644 index 00000000..709ab2a5 --- /dev/null +++ b/docs/dmripreproc.workflows.dwi.rst @@ -0,0 +1,38 @@ +dmripreproc.workflows.dwi package +================================= + +Submodules +---------- + +dmripreproc.workflows.dwi.base module +------------------------------------- + +.. automodule:: dmripreproc.workflows.dwi.base + :members: + :undoc-members: + :show-inheritance: + +dmripreproc.workflows.dwi.outputs module +---------------------------------------- + +.. automodule:: dmripreproc.workflows.dwi.outputs + :members: + :undoc-members: + :show-inheritance: + +dmripreproc.workflows.dwi.tensor module +--------------------------------------- + +.. automodule:: dmripreproc.workflows.dwi.tensor + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: dmripreproc.workflows.dwi + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/dmripreproc.workflows.fieldmap.rst b/docs/dmripreproc.workflows.fieldmap.rst new file mode 100644 index 00000000..bd497c3c --- /dev/null +++ b/docs/dmripreproc.workflows.fieldmap.rst @@ -0,0 +1,46 @@ +dmripreproc.workflows.fieldmap package +====================================== + +Submodules +---------- + +dmripreproc.workflows.fieldmap.base module +------------------------------------------ + +.. automodule:: dmripreproc.workflows.fieldmap.base + :members: + :undoc-members: + :show-inheritance: + +dmripreproc.workflows.fieldmap.fmap module +------------------------------------------ + +.. automodule:: dmripreproc.workflows.fieldmap.fmap + :members: + :undoc-members: + :show-inheritance: + +dmripreproc.workflows.fieldmap.pepolar module +--------------------------------------------- + +.. automodule:: dmripreproc.workflows.fieldmap.pepolar + :members: + :undoc-members: + :show-inheritance: + +dmripreproc.workflows.fieldmap.phasediff module +----------------------------------------------- + +.. automodule:: dmripreproc.workflows.fieldmap.phasediff + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: dmripreproc.workflows.fieldmap + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/dmripreproc.workflows.rst b/docs/dmripreproc.workflows.rst new file mode 100644 index 00000000..0ae1c325 --- /dev/null +++ b/docs/dmripreproc.workflows.rst @@ -0,0 +1,30 @@ +dmripreproc.workflows package +============================= + +Subpackages +----------- + +.. toctree:: + + dmripreproc.workflows.dwi + dmripreproc.workflows.fieldmap + +Submodules +---------- + +dmripreproc.workflows.base module +--------------------------------- + +.. automodule:: dmripreproc.workflows.base + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: dmripreproc.workflows + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/installation.rst b/docs/installation.rst index b618ae6a..b921afe0 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -32,13 +32,13 @@ You can either clone the public repository: .. code-block:: console - $ git clone git://github.com/nipy/dmripreproc + $ git clone git://github.com/tigrlab/dmripreproc Or download the `tarball`_: .. code-block:: console - $ curl -OL https://github.com/nipy/dmripreproc/tarball/master + $ curl -OL https://github.com/tigrlab/dmripreproc/tarball/master Once you have a copy of the source, you can install it with: @@ -47,5 +47,5 @@ Once you have a copy of the source, you can install it with: $ python setup.py install -.. _Github repo: https://github.com/nipy/dmripreproc -.. _tarball: https://github.com/nipy/dmripreproc/tarball/master +.. _Github repo: https://github.com/tigrlab/dmripreproc +.. _tarball: https://github.com/tigrlab/dmripreproc/tarball/master diff --git a/docs/modules.rst b/docs/modules.rst index 6109e78d..498372a8 100644 --- a/docs/modules.rst +++ b/docs/modules.rst @@ -1,5 +1,5 @@ dmripreproc -======== +=========== .. toctree:: :maxdepth: 4 From 6eccb37a08106f28746e1eb35022f04ecbc6c676 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Mon, 22 Jul 2019 06:51:31 -0400 Subject: [PATCH 073/156] move bids related functions to utils folder and add bids validator --- dmripreproc/utils/__init__.py | 0 dmripreproc/utils/bids.py | 179 ++++++++++++++++++++++++++++++++++ 2 files changed, 179 insertions(+) create mode 100644 dmripreproc/utils/__init__.py create mode 100644 dmripreproc/utils/bids.py diff --git a/dmripreproc/utils/__init__.py b/dmripreproc/utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/dmripreproc/utils/bids.py b/dmripreproc/utils/bids.py new file mode 100644 index 00000000..0320b630 --- /dev/null +++ b/dmripreproc/utils/bids.py @@ -0,0 +1,179 @@ +import warnings +import json +import sys + +from bids.layout import BIDSLayout + + +class BIDSError(ValueError): + def __init__(self, message, bids_root): + indent = 10 + header = '{sep} BIDS root folder: "{bids_root}" {sep}'.format( + bids_root=bids_root, sep="".join(["-"] * indent) + ) + self.msg = "\n{header}\n{indent}{message}\n{footer}".format( + header=header, + indent="".join([" "] * (indent + 1)), + message=message, + footer="".join(["-"] * len(header)), + ) + super(BIDSError, self).__init__(self.msg) + self.bids_root = bids_root + + +class BIDSWarning(RuntimeWarning): + pass + + +def collect_participants( + bids_dir, participant_label=None, strict=False, bids_validate=True +): + """ + List the participants under the BIDS root and checks that participants + designated with the participant_label argument exist in that folder. + Returns the list of participants to be finally processed. + Requesting all subjects in a BIDS directory root: + >>> collect_participants(str(datadir / 'ds114'), bids_validate=False) + ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10'] + Requesting two subjects, given their IDs: + >>> collect_participants(str(datadir / 'ds114'), participant_label=['02', '04'], + ... bids_validate=False) + ['02', '04'] + Requesting two subjects, given their IDs (works with 'sub-' prefixes): + >>> collect_participants(str(datadir / 'ds114'), participant_label=['sub-02', 'sub-04'], + ... bids_validate=False) + ['02', '04'] + Requesting two subjects, but one does not exist: + >>> collect_participants(str(datadir / 'ds114'), participant_label=['02', '14'], + ... bids_validate=False) + ['02'] + >>> collect_participants( + ... str(datadir / 'ds114'), participant_label=['02', '14'], + ... strict=True, bids_validate=False) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + fmriprep.utils.bids.BIDSError: + ... + """ + + if isinstance(bids_dir, BIDSLayout): + layout = bids_dir + else: + layout = BIDSLayout(str(bids_dir), validate=bids_validate) + + all_participants = set(layout.get_subjects()) + + # Error: bids_dir does not contain subjects + if not all_participants: + raise BIDSError( + "Could not find participants. Please make sure the BIDS data " + "structure is present and correct. Datasets can be validated online " + "using the BIDS Validator (http://bids-standard.github.io/bids-validator/).\n" + "If you are using Docker for Mac or Docker for Windows, you " + 'may need to adjust your "File sharing" preferences.', + bids_dir, + ) + + # No --participant-label was set, return all + if not participant_label: + return sorted(all_participants) + + if isinstance(participant_label, str): + participant_label = [participant_label] + + # Drop sub- prefixes + participant_label = [ + sub[4:] if sub.startswith("sub-") else sub for sub in participant_label + ] + # Remove duplicates + participant_label = sorted(set(participant_label)) + + # Remove labels not found + found_label = sorted(set(participant_label) & all_participants) + if not found_label: + raise BIDSError( + "Could not find participants [{}]".format( + ", ".join(participant_label) + ), + bids_dir, + ) + + # Warn if some IDs were not found + notfound_label = sorted(set(participant_label) - all_participants) + if notfound_label: + exc = BIDSError( + "Some participants were not found: {}".format( + ", ".join(notfound_label) + ), + bids_dir, + ) + if strict: + raise exc + warnings.warn(exc.msg, BIDSWarning) + + return all_participants, found_label + + +def validate_input_dir(bids_dir, all_subjects, subject_list): + # Ignore issues and warnings that should not influence FMRIPREP + import tempfile + import subprocess + + validator_config_dict = { + "ignore": [ + "EVENTS_COLUMN_ONSET", + "EVENTS_COLUMN_DURATION", + "TSV_EQUAL_ROWS", + "TSV_EMPTY_CELL", + "TSV_IMPROPER_NA", + "INCONSISTENT_SUBJECTS", + "INCONSISTENT_PARAMETERS", + "PARTICIPANT_ID_COLUMN", + "PARTICIPANT_ID_MISMATCH", + "TASK_NAME_MUST_DEFINE", + "PHENOTYPE_SUBJECTS_MISSING", + "STIMULUS_FILE_MISSING", + "BOLD_NOT_4D", + "EVENTS_TSV_MISSING", + "ACQTIME_FMT", + "Participants age 89 or higher", + "DATASET_DESCRIPTION_JSON_MISSING", + "TASK_NAME_CONTAIN_ILLEGAL_CHARACTER", + "FILENAME_COLUMN", + "WRONG_NEW_LINE", + "MISSING_TSV_COLUMN_CHANNELS", + "MISSING_TSV_COLUMN_IEEG_CHANNELS", + "MISSING_TSV_COLUMN_IEEG_ELECTRODES", + "UNUSED_STIMULUS", + "CHANNELS_COLUMN_SFREQ", + "CHANNELS_COLUMN_LOWCUT", + "CHANNELS_COLUMN_HIGHCUT", + "CHANNELS_COLUMN_NOTCH", + "CUSTOM_COLUMN_WITHOUT_DESCRIPTION", + "ACQTIME_FMT", + "SUSPICIOUSLY_LONG_EVENT_DESIGN", + "SUSPICIOUSLY_SHORT_EVENT_DESIGN", + "MISSING_TSV_COLUMN_EEG_ELECTRODES", + "MISSING_SESSION", + ], + "error": ["NO_T1W"], + "ignoredFiles": ["/dataset_description.json", "/participants.tsv"], + } + # Limit validation only to data from requested participants + ignored_subjects = all_subjects.difference(subject_list) + if ignored_subjects: + for subject in ignored_subjects: + validator_config_dict["ignoredFiles"].append( + "/sub-%s/**" % subject + ) + with tempfile.NamedTemporaryFile("w+") as temp: + temp.write(json.dumps(validator_config_dict)) + temp.flush() + try: + subprocess.check_call( + ["bids-validator", bids_dir, "-c", temp.name] + ) + except FileNotFoundError: + print( + "bids-validator does not appear to be installed", + file=sys.stderr, + ) From 1b647ad52bca7714d1a5f4b1aaeb8303c341f4d8 Mon Sep 17 00:00:00 2001 From: Michael Joseph Date: Mon, 22 Jul 2019 06:52:47 -0400 Subject: [PATCH 074/156] move Parameters class to cli and add bids validatation and concating shells options --- dmripreproc/cli.py | 80 ++++++++++++++++++++-------- dmripreproc/utils.py | 123 ++----------------------------------------- 2 files changed, 60 insertions(+), 143 deletions(-) diff --git a/dmripreproc/cli.py b/dmripreproc/cli.py index 3d3eb41c..50b8a032 100644 --- a/dmripreproc/cli.py +++ b/dmripreproc/cli.py @@ -8,7 +8,7 @@ import click -from . import utils +from .utils.bids import collect_participants from .workflows.base import init_dmripreproc_wf # Filter warnings that are visible whenever you import another package that @@ -17,24 +17,56 @@ warnings.filterwarnings("ignore", message="numpy.ufunc size changed") +class Parameters: + def __init__(self): + self.participant_label = "" + self.layout = None + self.subject_list = "" + self.bids_dir = "" + self.work_dir = "" + self.output_dir = "" + self.b0_thresh = 5 + self.eddy_niter = 5 + self.bet_dwi = 0.3 + self.bet_mag = 0.3 + self.total_readout = None + self.ignore_nodes = "" + self.analysis_level = "participant" + + @click.command() +@click.argument("bids_dir") +@click.argument("output_dir") +@click.argument( + "analysis_level", + type=click.Choice(["participant", "group"]), + default="participant", +) +@click.option( + "--skip_bids_validation", help="Skip BIDS validation", default=False +) @click.option( - "--participant-label", + "--participant_label", help="The label(s) of the participant(s) that should be " "analyzed. The label corresponds to " - "sub- from the BIDS spec (so it does " - "not include 'sub-'). If this parameter is not provided " + "sub- from the BIDS spec (the 'sub-' " + "prefix can be removed). If this parameter is not provided " "all subjects will be analyzed. Multiple participants " - "can be specified with a space separated list.", + "can be specified with a space delimited list.", default=None, ) -#@click.option( +@click.option( + "--concat_shells", + help="A space delimited list of acq-