Exemplo n.º 1
0
def execute():
    import os
    import argparse
    from colorama import Fore
    import warnings
    import logging

    # Suppress potential warnings
    warnings.filterwarnings("ignore")

    # Remove warnings from duecredit package in order to silent
    # "Assuming non interactive session since isatty found missing" message
    logging.getLogger("duecredit.utils").setLevel(logging.ERROR)

    # Add warning message if PYTHONPATH is not empty
    # cf https://groups.google.com/forum/#!topic/clinica-user/bVgifEdkg20
    python_path = os.environ.get('PYTHONPATH', '')
    if python_path:
        print(
            '%s[Warning] The PYTHONPATH environment variable is not empty.'
            ' Make sure there is no interference with Clinica (content of PYTHONPATH: %s).%s'
            % (Fore.YELLOW, python_path, Fore.RESET))

    # Nice traceback when clinica crashes
    sys.excepthook = custom_traceback

    MANDATORY_TITLE = (Fore.YELLOW + 'Mandatory arguments' + Fore.RESET)
    OPTIONAL_TITLE = (Fore.YELLOW + 'Optional arguments' + Fore.RESET)
    """
    Define and parse the command line argument
    """
    parser = ArgumentParser(add_help=False)
    parser.add_argument('-h',
                        '--help',
                        action='help',
                        default=argparse.SUPPRESS,
                        help=argparse.SUPPRESS)
    parser._positionals.title = (
        Fore.YELLOW + 'clinica expects one of the following keywords' +
        Fore.RESET)
    parser._optionals.title = OPTIONAL_TITLE

    sub_parser = parser.add_subparsers(metavar='')
    parser.add_argument("-v",
                        "--verbose",
                        dest='verbose',
                        action='store_true',
                        default=False,
                        help='Verbose: print all messages to the console')
    parser.add_argument("-l",
                        "--logname",
                        dest='logname',
                        default="clinica.log",
                        metavar=('file.log'),
                        help='Define the log file name (default: clinica.log)')
    """
    run category: run one of the available pipelines
    """
    from clinica.engine import CmdParser

    from clinica.pipelines.t1_freesurfer.t1_freesurfer_cli import T1FreeSurferCLI
    from clinica.pipelines.t1_freesurfer_longitudinal.t1_freesurfer_longitudinal_cli import T1FreeSurferLongitudinalCLI
    from clinica.pipelines.t1_freesurfer_longitudinal.t1_freesurfer_template_cli import T1FreeSurferTemplateCLI
    from clinica.pipelines.t1_freesurfer_longitudinal.t1_freesurfer_longitudinal_correction_cli import T1FreeSurferLongitudinalCorrectionCLI
    from clinica.pipelines.t1_volume_tissue_segmentation.t1_volume_tissue_segmentation_cli import T1VolumeTissueSegmentationCLI
    from clinica.pipelines.t1_volume_create_dartel.t1_volume_create_dartel_cli import T1VolumeCreateDartelCLI
    from clinica.pipelines.t1_volume_register_dartel.t1_volume_register_dartel_cli import T1VolumeRegisterDartelCLI
    from clinica.pipelines.t1_volume_dartel2mni.t1_volume_dartel2mni_cli import T1VolumeDartel2MNICLI
    from clinica.pipelines.t1_volume.t1_volume_cli import T1VolumeCLI
    from clinica.pipelines.t1_volume_existing_template.t1_volume_existing_template_cli import T1VolumeExistingTemplateCLI
    from clinica.pipelines.t1_volume_parcellation.t1_volume_parcellation_cli import T1VolumeParcellationCLI
    from clinica.pipelines.t1_linear.t1_linear_cli import T1LinearCLI
    from clinica.pipelines.dwi_preprocessing_using_phasediff_fieldmap.dwi_preprocessing_using_phasediff_fieldmap_cli import DwiPreprocessingUsingPhaseDiffFieldmapCli
    from clinica.pipelines.dwi_preprocessing_using_t1.dwi_preprocessing_using_t1_cli import DwiPreprocessingUsingT1Cli
    from clinica.pipelines.dwi_dti.dwi_dti_cli import DwiDtiCli
    from clinica.pipelines.dwi_connectome.dwi_connectome_cli import DwiConnectomeCli
    from clinica.pipelines.fmri_preprocessing.fmri_preprocessing_cli import fMRIPreprocessingCLI
    from clinica.pipelines.pet_volume.pet_volume_cli import PETVolumeCLI
    from clinica.pipelines.pet_surface.pet_surface_cli import PetSurfaceCLI
    from clinica.pipelines.pet_surface.pet_surface_longitudinal_cli import PetSurfaceLongitudinalCLI
    from clinica.pipelines.machine_learning_spatial_svm.spatial_svm_cli import SpatialSVMCLI
    from clinica.pipelines.statistics_surface.statistics_surface_cli import StatisticsSurfaceCLI
    from clinica.pipelines.statistics_volume.statistics_volume_cli import StatisticsVolumeCLI
    from clinica.pipelines.statistics_volume_correction.statistics_volume_correction_cli import StatisticsVolumeCorrectionCLI
    pipelines = ClinicaClassLoader(baseclass=CmdParser,
                                   extra_dir="pipelines").load()
    # The order in pipelines var will the same when typing `clinica run`
    # Pipelines are sorted by main / advanced pipelines then by modality
    pipelines += [
        # Main pipelines:
        T1FreeSurferCLI(),
        T1VolumeCLI(),
        # T1FreeSurferLongitudinalCLI(),
        T1LinearCLI(),
        DwiPreprocessingUsingPhaseDiffFieldmapCli(),
        DwiPreprocessingUsingT1Cli(),
        DwiDtiCli(),
        DwiConnectomeCli(),
        fMRIPreprocessingCLI(),
        PETVolumeCLI(),
        PetSurfaceCLI(),
        # PetSurfaceLongitudinalCLI(),
        SpatialSVMCLI(),
        StatisticsSurfaceCLI(),
        StatisticsVolumeCLI(),
        StatisticsVolumeCorrectionCLI(),
        # Advanced pipelines:
        T1VolumeExistingTemplateCLI(),
        T1VolumeTissueSegmentationCLI(),
        T1VolumeCreateDartelCLI(),
        T1VolumeRegisterDartelCLI(),
        T1VolumeDartel2MNICLI(),
        T1VolumeParcellationCLI(),
        # T1FreeSurferTemplateCLI(),
        # T1FreeSurferLongitudinalCorrectionCLI(),
    ]

    run_parser = sub_parser.add_parser(
        'run',
        add_help=False,
        formatter_class=argparse.RawTextHelpFormatter,
        help='To run pipelines on BIDS/CAPS datasets.')
    run_parser.description = '%sRun pipelines on BIDS/CAPS datasets.%s' % \
                             (Fore.GREEN, Fore.RESET)
    run_parser._positionals.title = '%sclinica run expects one of the following pipelines%s' % \
                                    (Fore.GREEN, Fore.RESET)

    init_cmdparser_objects(parser,
                           run_parser.add_subparsers(metavar='', dest='run'),
                           pipelines)
    """
    convert category: convert one of the supported datasets into BIDS hierarchy
    """
    from clinica.iotools.converters.aibl_to_bids.aibl_to_bids_cli import AiblToBidsCLI
    from clinica.iotools.converters.adni_to_bids.adni_to_bids_cli import AdniToBidsCLI
    from clinica.iotools.converters.oasis_to_bids.oasis_to_bids_cli import OasisToBidsCLI
    from clinica.iotools.converters.nifd_to_bids.nifd_to_bids_cli import NifdToBidsCLI  # noga

    converters = ClinicaClassLoader(baseclass=CmdParser,
                                    extra_dir="iotools/converters").load()
    converters += [
        AdniToBidsCLI(),
        AiblToBidsCLI(),
        OasisToBidsCLI(),
        NifdToBidsCLI(),
    ]

    convert_parser = sub_parser.add_parser(
        'convert',
        add_help=False,
        help='To convert unorganized datasets into a BIDS hierarchy.',
    )
    convert_parser.description = '%sTools to convert unorganized datasets into a BIDS hierarchy.%s' % \
                                 (Fore.GREEN, Fore.RESET)
    convert_parser._positionals.title = '%sclinica convert expects one of the following datasets%s' % \
                                        (Fore.YELLOW, Fore.RESET)
    convert_parser._optionals.title = OPTIONAL_TITLE
    init_cmdparser_objects(
        parser, convert_parser.add_subparsers(metavar='', dest='convert'),
        converters)
    """
    iotools category
    """
    from clinica.iotools.utils.data_handling_cli import CmdParserSubjectsSessions
    from clinica.iotools.utils.data_handling_cli import CmdParserMergeTsv
    from clinica.iotools.utils.data_handling_cli import CmdParserMissingModalities
    from clinica.iotools.utils.data_handling_cli import CmdParserCenterNifti
    io_tools = [
        CmdParserSubjectsSessions(),
        CmdParserMergeTsv(),
        CmdParserMissingModalities(),
        CmdParserCenterNifti()
    ]

    HELP_IO_TOOLS = 'Tools to handle BIDS/CAPS datasets.'
    io_parser = sub_parser.add_parser(
        'iotools',
        add_help=False,
        help=HELP_IO_TOOLS,
    )
    io_parser.description = '%s%s%s' % (Fore.GREEN, HELP_IO_TOOLS, Fore.RESET)
    io_parser._positionals.title = '%sclinica iotools expects one of the following BIDS/CAPS utilities%s' % \
                                   (Fore.YELLOW, Fore.RESET)
    io_parser._optionals.title = OPTIONAL_TITLE

    init_cmdparser_objects(
        parser, io_parser.add_subparsers(metavar='', dest='iotools'), io_tools)
    """
    visualize category: run one of the available pipelines
    """
    from clinica.engine import CmdParser

    from clinica.pipelines.t1_freesurfer.t1_freesurfer_visualizer import T1FreeSurferVisualizer

    visualizers = ClinicaClassLoader(baseclass=CmdParser,
                                     extra_dir="pipelines").load()
    visualizers += [
        T1FreeSurferVisualizer(),
    ]

    visualize_parser = sub_parser.add_parser(
        'visualize',
        add_help=False,
        formatter_class=argparse.RawTextHelpFormatter,
        help='To visualize outputs of Clinica pipelines.')
    visualize_parser.description = '%sVisualize outputs of Clinica pipelines.%s' % \
                                   (Fore.GREEN, Fore.RESET)
    visualize_parser._positionals.title = '%sclinica visualize expects one of the following pipelines%s' % \
                                          (Fore.YELLOW, Fore.RESET)

    init_cmdparser_objects(
        parser, visualize_parser.add_subparsers(metavar='', dest='visualize'),
        visualizers)
    """
    generate category: template
    """
    generate_parser = sub_parser.add_parser(
        'generate',
        add_help=False,
        help=('To generate pre-filled files when creating '
              'new pipelines (for developers).'),
    )
    generate_parser.description = '%sGenerate pre-filled files when creating new pipelines (for  developers).%s' % \
                                  (Fore.GREEN, Fore.RESET)
    generate_parser._positionals.title = '%sclinica generate expects one of the following tools%s' % \
                                         (Fore.YELLOW, Fore.RESET)
    generate_parser._optionals.title = OPTIONAL_TITLE

    from clinica.engine.template import CmdGenerateTemplates
    init_cmdparser_objects(
        parser, generate_parser.add_subparsers(metavar='', dest='generate'),
        [CmdGenerateTemplates()])
    """
    Silent all sub-parser errors methods except the one which is called
    otherwise the output console will display useless messages
    """
    def silent_help():
        pass

    def single_error_message(p):
        def error(x):
            from colorama import Fore
            print('%sError %s%s\n' % (Fore.RED, x, Fore.RESET))
            p.print_help()
            parser.print_help = silent_help
            exit(-1)

        return error

    for p in [run_parser, io_parser, convert_parser, generate_parser]:
        p.error = single_error_message(p)

    # Do not want stderr message
    def silent_msg(x):
        pass

    parser.error = silent_msg
    """
    Parse the command and check that everything went fine
    """
    args = None
    unknown_args = None
    try:
        argcomplete.autocomplete(parser)
        args, unknown_args = parser.parse_known_args()
        if unknown_args:
            if ('--verbose' in unknown_args) or ('-v' in unknown_args):
                cprint("Verbose detected")
                args.verbose = True
            unknown_args = [i for i in unknown_args if i != '-v']
            unknown_args = [i for i in unknown_args if i != '--verbose']
            if unknown_args:
                print(
                    '%s[Warning] Unknown flag(s) detected: %s. This will be ignored by Clinica%s'
                    % (Fore.YELLOW, unknown_args, Fore.RESET))
    except SystemExit:
        exit(0)
    except Exception:
        print(
            "%s\n[Error] You wrote wrong arguments on the command line. Clinica will now exit.\n%s"
            % (Fore.RED, Fore.RESET))
        parser.print_help()
        exit(-1)

    if 'run' in args and hasattr(args, 'func') is False:
        # Case when we type `clinica run` on the terminal
        run_parser.print_help()
        exit(0)
    elif 'convert' in args and hasattr(args, 'func') is False:
        # Case when we type `clinica convert` on the terminal
        convert_parser.print_help()
        exit(0)
    elif 'iotools' in args and hasattr(args, 'func') is False:
        # Case when we type `clinica iotools` on the terminal
        io_parser.print_help()
        exit(0)
    elif 'visualize' in args and hasattr(args, 'func') is False:
        # Case when we type `clinica visualize` on the terminal
        visualize_parser.print_help()
        exit(0)
    elif 'generate' in args and hasattr(args, 'func') is False:
        # Case when we type `clinica generate` on the terminal
        generate_parser.print_help()
        exit(0)
    elif args is None or hasattr(args, 'func') is False:
        # Case when we type `clinica` on the terminal
        parser.print_help()
        exit(0)

    import clinica.utils.stream as var
    var.clinica_verbose = args.verbose

    if args.verbose is False:
        """
        Enable only cprint(msg) --> clinica print(msg)
        - All the print() will be ignored!
        - All the logging will be redirect to the log file.
        """
        from clinica.utils.stream import FilterOut
        sys.stdout = FilterOut(sys.stdout)
        import logging as python_logging
        from logging import Filter, ERROR
        import os
        from nipype import config, logging

        # Configure Nipype logger for our needs
        config.update_config({
            'logging': {
                'workflow_level': 'INFO',
                'log_directory': os.getcwd(),
                'log_to_file': True
            },
            'execution': {
                'stop_on_first_crash': False,
                'hash_method': 'content'
            }
        })
        logging.update_logging(config)

        # Define the LogFilter for ERROR detection
        class LogFilter(Filter):
            """
            The LogFilter class ables to monitor if an ERROR log signal is sent
            from Clinica/Nipype. If detected, the user will be warned.
            """
            def filter(self, record):
                if record.levelno >= ERROR:
                    import datetime
                    now = datetime.datetime.now().strftime('%H:%M:%S')
                    cprint(
                        '%s[%s]%s An error was found: please check the log file (%s) or crash file '
                        '(nipypecli crash <pklz_file>) for details. Clinica is still running.'
                        % (Fore.RED, now, Fore.RESET, args.logname))
                return True

        if args.verbose:
            logger = logging.getLogger('nipype.workflow')
            logger.addFilter(LogFilter())

        # Remove all handlers associated with the root logger object
        for handler in python_logging.root.handlers[:]:
            python_logging.root.removeHandler(handler)

        logging.disable_file_logging()

        # Enable file logging using a filename
        def enable_file_logging(self, filename):
            """
            Hack to define a filename for the log file! It overloads the
            'enable_file_logging' method in 'nipype/utils/logger.py' file.
            """
            import logging
            from logging.handlers import RotatingFileHandler as RFHandler
            config = self._config
            LOG_FILENAME = os.path.join(config.get('logging', 'log_directory'),
                                        filename)
            hdlr = RFHandler(LOG_FILENAME,
                             maxBytes=int(config.get('logging', 'log_size')),
                             backupCount=int(
                                 config.get('logging', 'log_rotate')))
            formatter = logging.Formatter(fmt=self.fmt, datefmt=self.datefmt)
            hdlr.setFormatter(formatter)
            self._logger.addHandler(hdlr)
            self._fmlogger.addHandler(hdlr)
            self._iflogger.addHandler(hdlr)
            self._hdlr = hdlr

        enable_file_logging(logging, args.logname)

        class Stream:
            def write(self, text):
                print(text)
                sys.stdout.flush()

        python_logging.basicConfig(format=logging.fmt,
                                   datefmt=logging.datefmt,
                                   stream=Stream())

    # Finally, run the command
    args.func(args)
Exemplo n.º 2
0
def execute():
    import argparse
    from colorama import Fore
    import warnings

    # Suppress potential warnings
    warnings.filterwarnings("ignore")

    # Nice traceback when clinica crashes
    sys.excepthook = custom_traceback

    MANDATORY_TITLE = (Fore.YELLOW + 'Mandatory arguments' + Fore.RESET)
    OPTIONAL_TITLE = (Fore.YELLOW + 'Optional arguments' + Fore.RESET)
    """
    Define and parse the command line argument
    """
    parser = ArgumentParser(add_help=False)
    parser.add_argument('-h',
                        '--help',
                        action='help',
                        default=argparse.SUPPRESS,
                        help=argparse.SUPPRESS)
    parser._positionals.title = (
        Fore.YELLOW + 'clinica expects one of the following keywords' +
        Fore.RESET)
    parser._optionals.title = OPTIONAL_TITLE

    sub_parser = parser.add_subparsers(metavar='')
    parser.add_argument("-v",
                        "--verbose",
                        dest='verbose',
                        action='store_true',
                        default=False,
                        help='Verbose: print all messages to the console')
    parser.add_argument("-l",
                        "--logname",
                        dest='logname',
                        default="clinica.log",
                        metavar=('file.log'),
                        help='Define the log file name (default: clinica.log)')
    """
    run category: run one of the available pipelines
    """
    from clinica.engine import CmdParser

    from clinica.pipelines.t1_freesurfer_cross_sectional.t1_freesurfer_cross_sectional_cli import T1FreeSurferCrossSectionalCLI  # noqa
    from clinica.pipelines.t1_volume_tissue_segmentation.t1_volume_tissue_segmentation_cli import T1VolumeTissueSegmentationCLI  # noqa
    from clinica.pipelines.t1_volume_create_dartel.t1_volume_create_dartel_cli import T1VolumeCreateDartelCLI  # noqa
    from clinica.pipelines.t1_volume_existing_dartel.t1_volume_existing_dartel_cli import T1VolumeExistingDartelCLI  # noqa
    from clinica.pipelines.t1_volume_dartel2mni.t1_volume_dartel2mni_cli import T1VolumeDartel2MNICLI  # noqa
    from clinica.pipelines.t1_volume_new_template.t1_volume_new_template_cli import T1VolumeNewTemplateCLI  # noqa
    from clinica.pipelines.t1_volume_existing_template.t1_volume_existing_template_cli import T1VolumeExistingTemplateCLI  # noqa
    from clinica.pipelines.t1_volume_parcellation.t1_volume_parcellation_cli import T1VolumeParcellationCLI
    from clinica.pipelines.dwi_preprocessing_using_phasediff_fieldmap.dwi_preprocessing_using_phasediff_fieldmap_cli import DwiPreprocessingUsingPhaseDiffFieldmapCli  # noqa
    from clinica.pipelines.dwi_preprocessing_using_t1.dwi_preprocessing_using_t1_cli import DwiPreprocessingUsingT1Cli  # noqa
    from clinica.pipelines.dwi_dti.dwi_dti_cli import DwiDtiCli  # noqa
    # from clinica.pipelines.dwi_connectome.dwi_connectome_cli import DwiConnectomeCli  # noqa
    from clinica.pipelines.fmri_preprocessing.fmri_preprocessing_cli import fMRIPreprocessingCLI  # noqa
    from clinica.pipelines.pet_volume.pet_volume_cli import PETVolumeCLI  # noqa
    from clinica.pipelines.pet_surface.pet_surface_cli import PetSurfaceCLI  # noqa
    from clinica.pipelines.machine_learning_spatial_svm.spatial_svm_cli import SpatialSVMCLI  # noqa
    from clinica.pipelines.statistics_surface.statistics_surface_cli import StatisticsSurfaceCLI  # noqa
    pipelines = ClinicaClassLoader(baseclass=CmdParser,
                                   extra_dir="pipelines").load()
    pipelines += [
        T1FreeSurferCrossSectionalCLI(),
        T1VolumeNewTemplateCLI(),
        DwiPreprocessingUsingPhaseDiffFieldmapCli(),
        DwiPreprocessingUsingT1Cli(),
        DwiDtiCli(),
        # DwiConnectomeCli(),
        fMRIPreprocessingCLI(),
        PETVolumeCLI(),
        PetSurfaceCLI(),
        SpatialSVMCLI(),
        StatisticsSurfaceCLI(),
        T1VolumeExistingTemplateCLI(),
        T1VolumeTissueSegmentationCLI(),
        T1VolumeCreateDartelCLI(),
        T1VolumeExistingDartelCLI(),
        T1VolumeDartel2MNICLI(),
        T1VolumeParcellationCLI()
    ]

    run_parser = sub_parser.add_parser(
        'run',
        add_help=False,
        formatter_class=argparse.RawTextHelpFormatter,
        help='To run pipelines on BIDS/CAPS datasets.')
    run_parser.description = (Fore.GREEN +
                              'Run pipelines on BIDS/CAPS datasets.' +
                              Fore.RESET)
    run_parser._positionals.title = (
        Fore.YELLOW + 'clinica run expects one of the following pipelines' +
        Fore.RESET)

    init_cmdparser_objects(parser,
                           run_parser.add_subparsers(metavar='', dest='run'),
                           pipelines)
    """
    convert category: convert one of the supported datasets into BIDS hierarchy
    """
    from clinica.iotools.converters.aibl_to_bids.aibl_to_bids_cli import AiblToBidsCLI  # noqa
    from clinica.iotools.converters.adni_to_bids.adni_to_bids_cli import AdniToBidsCLI  # noqa
    from clinica.iotools.converters.oasis_to_bids.oasis_to_bids_cli import OasisToBidsCLI  # noqa

    converters = ClinicaClassLoader(baseclass=CmdParser,
                                    extra_dir="iotools/converters").load()
    converters += [
        AdniToBidsCLI(),
        AiblToBidsCLI(),
        OasisToBidsCLI(),
    ]

    convert_parser = sub_parser.add_parser(
        'convert',
        add_help=False,
        help='To convert unorganized datasets into a BIDS hierarchy.',
    )
    convert_parser.description = (
        Fore.GREEN +
        'Tools to convert unorganized datasets into a BIDS hierarchy.' +
        Fore.RESET)
    convert_parser._positionals.title = (
        Fore.YELLOW + 'clinica convert expects one of the following datasets' +
        Fore.RESET)
    convert_parser._optionals.title = OPTIONAL_TITLE
    init_cmdparser_objects(
        parser, convert_parser.add_subparsers(metavar='', dest='convert'),
        converters)
    """
    iotools category
    """
    from clinica.iotools.utils.data_handling_cli import CmdParserSubjectsSessions
    from clinica.iotools.utils.data_handling_cli import CmdParserMergeTsv
    from clinica.iotools.utils.data_handling_cli import CmdParserMissingModalities

    io_tools = [
        CmdParserSubjectsSessions(),
        CmdParserMergeTsv(),
        CmdParserMissingModalities(),
    ]

    HELP_IO_TOOLS = 'Tools to handle BIDS/CAPS datasets.'
    io_parser = sub_parser.add_parser(
        'iotools',
        add_help=False,
        help=HELP_IO_TOOLS,
    )
    io_parser.description = (Fore.GREEN + HELP_IO_TOOLS + Fore.RESET)
    io_parser._positionals.title = (
        Fore.YELLOW +
        'clinica iotools expects one of the following BIDS/CAPS utilities' +
        Fore.RESET)
    io_parser._optionals.title = OPTIONAL_TITLE

    init_cmdparser_objects(
        parser, io_parser.add_subparsers(metavar='', dest='iotools'), io_tools)
    """
    visualize category: run one of the available pipelines
    """
    from clinica.engine import CmdParser

    from clinica.pipelines.t1_freesurfer_cross_sectional.t1_freesurfer_cross_sectional_visualizer import T1FreeSurferVisualizer

    visualizers = ClinicaClassLoader(baseclass=CmdParser,
                                     extra_dir="pipelines").load()
    visualizers += [
        T1FreeSurferVisualizer(),
    ]

    visualize_parser = sub_parser.add_parser(
        'visualize',
        add_help=False,
        formatter_class=argparse.RawTextHelpFormatter,
        help='To visualize outputs of Clinica pipelines.')
    visualize_parser.description = (Fore.GREEN +
                                    'Visualize outputs of Clinica pipelines.' +
                                    Fore.RESET)
    visualize_parser._positionals.title = (
        Fore.YELLOW +
        'clinica visualize expects one of the following pipelines' +
        Fore.RESET)

    init_cmdparser_objects(
        parser, visualize_parser.add_subparsers(metavar='', dest='visualize'),
        visualizers)
    """
    generate category: template
    """
    generate_parser = sub_parser.add_parser(
        'generate',
        add_help=False,
        help=('To generate pre-filled files when creating '
              'new pipelines (for developers).'),
    )
    generate_parser.description = (
        Fore.GREEN + ('Generate pre-filled files when creating new pipelines '
                      '(for  developers).') + Fore.RESET)
    generate_parser._positionals.title = (
        Fore.YELLOW + 'clinica generate expects one of the following tools' +
        Fore.RESET)
    generate_parser._optionals.title = OPTIONAL_TITLE

    from clinica.engine.template import CmdGenerateTemplates
    init_cmdparser_objects(
        parser, generate_parser.add_subparsers(metavar='', dest='generate'),
        [CmdGenerateTemplates()])
    """
    Silent all sub-parser errors methods except the one which is called
    otherwise the output console will display useless messages
    """
    def silent_help():
        pass

    def single_error_message(p):
        def error(x):
            from colorama import Fore
            print('%sError %s%s\n' % (Fore.RED, x, Fore.RESET))
            p.print_help()
            parser.print_help = silent_help
            exit(-1)

        return error

    for p in [run_parser, io_parser, convert_parser, generate_parser]:
        p.error = single_error_message(p)

    # Do not want stderr message
    def silent_msg(x):
        pass

    parser.error = silent_msg
    """
    Parse the command and check that everything went fine
    """
    args = None
    unknown_args = None
    try:
        argcomplete.autocomplete(parser)
        args, unknown_args = parser.parse_known_args()
    except SystemExit:
        exit(0)
    except Exception:
        parser.print_help()
        exit(-1)

    # if unknown_args:
    #    if '--verbose' or '-v' in unknown_args:
    #        cprint('Verbose flag detected')
    #    raise ValueError('Unknown flag detected: %s' % unknown_args)

    if 'run' in args and hasattr(args, 'func') is False:
        # Case when we type `clinica run` on the terminal
        run_parser.print_help()
        exit(0)
    elif 'convert' in args and hasattr(args, 'func') is False:
        # Case when we type `clinica convert` on the terminal
        convert_parser.print_help()
        exit(0)
    elif 'iotools' in args and hasattr(args, 'func') is False:
        # Case when we type `clinica iotools` on the terminal
        io_parser.print_help()
        exit(0)
    elif 'visualize' in args and hasattr(args, 'func') is False:
        # Case when we type `clinica visualize` on the terminal
        visualize_parser.print_help()
        exit(0)
    elif 'generate' in args and hasattr(args, 'func') is False:
        # Case when we type `clinica generate` on the terminal
        generate_parser.print_help()
        exit(0)
    elif args is None or hasattr(args, 'func') is False:
        # Case when we type `clinica` on the terminal
        parser.print_help()
        exit(0)

    import clinica.utils.stream as var
    var.clinica_verbose = args.verbose

    if args.verbose is False:
        """
        Enable only cprint(msg) --> clinica print(msg)
        - All the print() will be ignored!
        - All the logging will be redirect to the log file.
        """
        from clinica.utils.stream import FilterOut
        sys.stdout = FilterOut(sys.stdout)
        import logging as python_logging
        from logging import Filter, ERROR
        import os

        # Resolve bug
        # "Assuming non interactive session since isatty found missing"
        # at the begining of any pipeline caused by logger in duecredit package
        # (utils.py)
        # Deactivate stdout, then reactivate it
        sys.stdout = open(os.devnull, 'w')
        from nipype import config
        sys.stdout = sys.__stdout__

        from nipype import logging

        # Configure Nipype logger for our needs
        config.update_config({
            'logging': {
                'workflow_level': 'INFO',
                'log_directory': os.getcwd(),
                'log_to_file': True
            },
            'execution': {
                'stop_on_first_crash': False,
                'hash_method': 'content'
            }
        })
        logging.update_logging(config)

        # Define the LogFilter for ERROR detection
        class LogFilter(Filter):
            """
            The LogFilter class ables to monitor if an ERROR log signal is sent
            from Clinica/Nipype. If detected, the user will be warned.
            """
            def filter(self, record):
                if record.levelno >= ERROR:
                    cprint(
                        "An ERROR was generated: please check the log file for more information"
                    )
                return True

        logger = logging.getLogger('nipype.workflow')
        logger.addFilter(LogFilter())

        # Remove all handlers associated with the root logger object
        for handler in python_logging.root.handlers[:]:
            python_logging.root.removeHandler(handler)

        logging.disable_file_logging()

        # Enable file logging using a filename
        def enable_file_logging(self, filename):
            """
            Hack to define a filename for the log file! It overloads the
            'enable_file_logging' method in 'nipype/utils/logger.py' file.
            """
            import logging
            from logging.handlers import RotatingFileHandler as RFHandler
            config = self._config
            LOG_FILENAME = os.path.join(config.get('logging', 'log_directory'),
                                        filename)
            hdlr = RFHandler(LOG_FILENAME,
                             maxBytes=int(config.get('logging', 'log_size')),
                             backupCount=int(
                                 config.get('logging', 'log_rotate')))
            formatter = logging.Formatter(fmt=self.fmt, datefmt=self.datefmt)
            hdlr.setFormatter(formatter)
            self._logger.addHandler(hdlr)
            self._fmlogger.addHandler(hdlr)
            self._iflogger.addHandler(hdlr)
            self._hdlr = hdlr

        enable_file_logging(logging, args.logname)

        class Stream:
            def write(self, text):
                print(text)
                sys.stdout.flush()

        python_logging.basicConfig(format=logging.fmt,
                                   datefmt=logging.datefmt,
                                   stream=Stream())

    # Finally, run the pipelines
    args.func(args)
Exemplo n.º 3
0
def execute():
    import argparse
    import logging
    import os
    import warnings

    from colorama import Fore

    # Suppress potential warnings
    warnings.filterwarnings("ignore")

    # Remove warnings from duecredit package in order to silent
    # "Assuming non interactive session since isatty found missing" message
    logging.getLogger("duecredit.utils").setLevel(logging.ERROR)

    # Add warning message if PYTHONPATH is not empty
    # cf https://groups.google.com/forum/#!topic/clinica-user/bVgifEdkg20
    python_path = os.environ.get("PYTHONPATH", "")
    if python_path:
        print(
            f"{Fore.YELLOW}[Warning] The PYTHONPATH environment variable is not empty."
            f" Make sure there is no interference with Clinica "
            f"(content of PYTHONPATH: {python_path}).{Fore.RESET}")

    # Nice traceback when clinica crashes
    sys.excepthook = custom_traceback

    OPTIONAL_TITLE = f"{Fore.YELLOW}Optional arguments{Fore.RESET}"
    """
    Define and parse the command line argument
    """
    parser = ArgumentParser(add_help=False)
    parser.add_argument("-h",
                        "--help",
                        action="help",
                        default=argparse.SUPPRESS,
                        help=argparse.SUPPRESS)
    parser._positionals.title = (
        f"{Fore.YELLOW}clinica expects one of the following keywords{Fore.RESET}"
    )
    parser._optionals.title = OPTIONAL_TITLE

    sub_parser = parser.add_subparsers(metavar="")
    parser.add_argument(
        "-V",
        "--version",
        dest="version",
        action="store_true",
        default=False,
        help="Clinica's installed version",
    )
    parser.add_argument(
        "-v",
        "--verbose",
        dest="verbose",
        action="store_true",
        default=False,
        help="Verbose: print all messages to the standard output",
    )
    parser.add_argument(
        "-l",
        "--logname",
        dest="logname",
        default="clinica.log",
        metavar=("file.log"),
        help="Define the log file name (default: clinica.log)",
    )
    """
    run category: run one of the available pipelines
    """
    # fmt: off
    from clinica.engine import CmdParser
    from clinica.pipelines.deeplearning_prepare_data.deeplearning_prepare_data_cli import (
        DeepLearningPrepareDataCLI, )
    from clinica.pipelines.dwi_connectome.dwi_connectome_cli import DwiConnectomeCli
    from clinica.pipelines.dwi_dti.dwi_dti_cli import DwiDtiCli
    from clinica.pipelines.dwi_preprocessing_using_phasediff_fieldmap.dwi_preprocessing_using_phasediff_fieldmap_cli import (
        DwiPreprocessingUsingPhaseDiffFieldmapCli, )
    from clinica.pipelines.dwi_preprocessing_using_t1.dwi_preprocessing_using_t1_cli import (
        DwiPreprocessingUsingT1Cli, )
    from clinica.pipelines.machine_learning_spatial_svm.spatial_svm_cli import (
        SpatialSVMCLI, )
    from clinica.pipelines.pet_linear.pet_linear_cli import PETLinearCLI
    from clinica.pipelines.pet_surface.pet_surface_cli import PetSurfaceCLI
    from clinica.pipelines.pet_surface.pet_surface_longitudinal_cli import (
        PetSurfaceLongitudinalCLI, )
    from clinica.pipelines.pet_volume.pet_volume_cli import PETVolumeCLI
    from clinica.pipelines.statistics_surface.statistics_surface_cli import (
        StatisticsSurfaceCLI, )
    from clinica.pipelines.statistics_volume.statistics_volume_cli import (
        StatisticsVolumeCLI, )
    from clinica.pipelines.statistics_volume_correction.statistics_volume_correction_cli import (
        StatisticsVolumeCorrectionCLI, )
    from clinica.pipelines.t1_freesurfer.t1_freesurfer_cli import T1FreeSurferCLI
    from clinica.pipelines.t1_freesurfer_longitudinal.t1_freesurfer_longitudinal_cli import (
        T1FreeSurferLongitudinalCLI, )
    from clinica.pipelines.t1_freesurfer_longitudinal.t1_freesurfer_longitudinal_correction_cli import (
        T1FreeSurferLongitudinalCorrectionCLI, )
    from clinica.pipelines.t1_freesurfer_longitudinal.t1_freesurfer_template_cli import (
        T1FreeSurferTemplateCLI, )
    from clinica.pipelines.t1_linear.t1_linear_cli import T1LinearCLI
    from clinica.pipelines.t1_volume.t1_volume_cli import T1VolumeCLI
    from clinica.pipelines.t1_volume_create_dartel.t1_volume_create_dartel_cli import (
        T1VolumeCreateDartelCLI, )
    from clinica.pipelines.t1_volume_dartel2mni.t1_volume_dartel2mni_cli import (
        T1VolumeDartel2MNICLI, )
    from clinica.pipelines.t1_volume_existing_template.t1_volume_existing_template_cli import (
        T1VolumeExistingTemplateCLI, )
    from clinica.pipelines.t1_volume_parcellation.t1_volume_parcellation_cli import (
        T1VolumeParcellationCLI, )
    from clinica.pipelines.t1_volume_register_dartel.t1_volume_register_dartel_cli import (
        T1VolumeRegisterDartelCLI, )
    from clinica.pipelines.t1_volume_tissue_segmentation.t1_volume_tissue_segmentation_cli import (
        T1VolumeTissueSegmentationCLI, )

    # fmt: on

    pipelines = ClinicaClassLoader(baseclass=CmdParser,
                                   extra_dir="pipelines").load()
    # The order in pipelines var will the same when typing `clinica run`
    # Pipelines are sorted by main / advanced pipelines then by modality
    pipelines += [
        # Main pipelines:
        T1FreeSurferCLI(),
        T1VolumeCLI(),
        T1FreeSurferLongitudinalCLI(),
        T1LinearCLI(),
        DwiPreprocessingUsingPhaseDiffFieldmapCli(),
        DwiPreprocessingUsingT1Cli(),
        DwiDtiCli(),
        DwiConnectomeCli(),
        PETLinearCLI(),
        PETVolumeCLI(),
        PetSurfaceCLI(),
        PetSurfaceLongitudinalCLI(),
        DeepLearningPrepareDataCLI(),
        SpatialSVMCLI(),
        StatisticsSurfaceCLI(),
        StatisticsVolumeCLI(),
        StatisticsVolumeCorrectionCLI(),
        # Advanced pipelines:
        T1VolumeExistingTemplateCLI(),
        T1VolumeTissueSegmentationCLI(),
        T1VolumeCreateDartelCLI(),
        T1VolumeRegisterDartelCLI(),
        T1VolumeDartel2MNICLI(),
        T1VolumeParcellationCLI(),
        T1FreeSurferTemplateCLI(),
        T1FreeSurferLongitudinalCorrectionCLI(),
    ]

    run_parser = sub_parser.add_parser(
        "run",
        add_help=False,
        formatter_class=argparse.RawTextHelpFormatter,
        help="To run pipelines on BIDS/CAPS datasets.",
    )
    run_parser.description = (
        f"{Fore.GREEN}Run pipelines on BIDS/CAPS datasets.{Fore.RESET}")
    run_parser._positionals.title = (
        f"{Fore.GREEN}clinica run expects one of the following pipelines{Fore.RESET}"
    )

    init_cmdparser_objects(parser,
                           run_parser.add_subparsers(metavar="", dest="run"),
                           pipelines)
    """
    convert category: convert one of the supported datasets into BIDS hierarchy
    """
    # fmt: off
    from clinica.iotools.converters.adni_to_bids.adni_to_bids_cli import AdniToBidsCLI
    from clinica.iotools.converters.aibl_to_bids.aibl_to_bids_cli import AiblToBidsCLI
    from clinica.iotools.converters.nifd_to_bids.nifd_to_bids_cli import NifdToBidsCLI
    from clinica.iotools.converters.oasis_to_bids.oasis_to_bids_cli import (
        OasisToBidsCLI, )

    # fmt: on

    converters = ClinicaClassLoader(baseclass=CmdParser,
                                    extra_dir="iotools/converters").load()
    converters += [
        AdniToBidsCLI(),
        AiblToBidsCLI(),
        OasisToBidsCLI(),
        NifdToBidsCLI(),
    ]

    convert_parser = sub_parser.add_parser(
        "convert",
        add_help=False,
        help="To convert unorganized datasets into a BIDS hierarchy.",
    )
    convert_parser.description = f"{Fore.GREEN}Tools to convert unorganized datasets into a BIDS hierarchy.{Fore.RESET}"
    convert_parser._positionals.title = f"{Fore.YELLOW}clinica convert expects one of the following datasets{Fore.RESET}"
    convert_parser._optionals.title = OPTIONAL_TITLE
    init_cmdparser_objects(
        parser, convert_parser.add_subparsers(metavar="", dest="convert"),
        converters)
    """
    iotools category
    """
    from clinica.iotools.utils.data_handling_cli import (
        CmdParserCenterNifti,
        CmdParserMergeTsv,
        CmdParserMissingModalities,
        CmdParserMissingProcessing,
        CmdParserSubjectsSessions,
    )

    io_tools = [
        CmdParserSubjectsSessions(),
        CmdParserMergeTsv(),
        CmdParserMissingProcessing(),
        CmdParserMissingModalities(),
        CmdParserCenterNifti(),
    ]

    HELP_IO_TOOLS = "Tools to handle BIDS/CAPS datasets."
    io_parser = sub_parser.add_parser(
        "iotools",
        add_help=False,
        help=HELP_IO_TOOLS,
    )
    io_parser.description = f"{Fore.GREEN}{HELP_IO_TOOLS}{Fore.RESET}"
    io_parser._positionals.title = f"{Fore.YELLOW}clinica iotools expects one of the following BIDS/CAPS utilities{Fore.RESET}"
    io_parser._optionals.title = OPTIONAL_TITLE

    init_cmdparser_objects(
        parser, io_parser.add_subparsers(metavar="", dest="iotools"), io_tools)
    """
    visualize category: run one of the available pipelines
    """
    from clinica.engine import CmdParser
    from clinica.pipelines.t1_freesurfer.t1_freesurfer_visualizer import (
        T1FreeSurferVisualizer, )

    visualizers = ClinicaClassLoader(baseclass=CmdParser,
                                     extra_dir="pipelines").load()
    visualizers += [
        T1FreeSurferVisualizer(),
    ]

    visualize_parser = sub_parser.add_parser(
        "visualize",
        add_help=False,
        formatter_class=argparse.RawTextHelpFormatter,
        help="To visualize outputs of Clinica pipelines.",
    )
    visualize_parser.description = (
        f"{Fore.GREEN}Visualize outputs of Clinica pipelines.{Fore.RESET}")
    visualize_parser._positionals.title = f"{Fore.YELLOW}clinica visualize expects one of the following pipelines{Fore.RESET}"

    init_cmdparser_objects(
        parser,
        visualize_parser.add_subparsers(metavar="", dest="visualize"),
        visualizers,
    )
    """
    generate category: template
    """
    generate_parser = sub_parser.add_parser(
        "generate",
        add_help=False,
        help=
        ("To generate pre-filled files when creating new pipelines (for developers)."
         ),
    )
    generate_parser.description = f"{Fore.GREEN}Generate pre-filled files when creating new pipelines (for  developers).{Fore.RESET}"
    generate_parser._positionals.title = (
        f"{Fore.YELLOW}clinica generate expects one of the following tools{Fore.RESET}"
    )
    generate_parser._optionals.title = OPTIONAL_TITLE

    from clinica.engine.template import CmdGenerateTemplates

    init_cmdparser_objects(
        parser,
        generate_parser.add_subparsers(metavar="", dest="generate"),
        [CmdGenerateTemplates()],
    )
    """
    Silent all sub-parser errors methods except the one which is called
    otherwise the output console will display useless messages
    """
    def silent_help():
        pass

    def single_error_message(p):
        def error(x):
            from colorama import Fore

            print(f"{Fore.RED}Error {x}{Fore.RESET}\n")
            p.print_help()
            parser.print_help = silent_help
            exit(-1)

        return error

    for p in [run_parser, io_parser, convert_parser, generate_parser]:
        p.error = single_error_message(p)

    # Do not want stderr message
    def silent_msg(x):
        pass

    parser.error = silent_msg
    """
    Parse the command and check that everything went fine
    """
    args = None
    unknown_args = None
    try:
        argcomplete.autocomplete(parser)
        args, unknown_args = parser.parse_known_args()
        if args.version:
            import clinica

            print(f"Clinica version is: {clinica.__version__}")
            exit(0)
        if unknown_args:
            if ("--verbose" in unknown_args) or ("-v" in unknown_args):
                cprint("Verbose detected")
                args.verbose = True
            unknown_args = [i for i in unknown_args if i != "-v"]
            unknown_args = [i for i in unknown_args if i != "--verbose"]
            if unknown_args:
                print(
                    f"{Fore.YELLOW}[Warning] Unknown flag(s) detected: {unknown_args}. "
                    f"This will be ignored by Clinica{Fore.RESET}")
    except SystemExit:
        exit(0)
    except Exception:
        print(
            f"{Fore.RED}\n[Error] You wrote wrong arguments on the command line. "
            f"Clinica will now exit.\n{Fore.RESET}")
        parser.print_help()
        exit(-1)

    if "run" in args and hasattr(args, "func") is False:
        # Case when we type `clinica run` on the terminal
        run_parser.print_help()
        exit(0)
    elif "convert" in args and hasattr(args, "func") is False:
        # Case when we type `clinica convert` on the terminal
        convert_parser.print_help()
        exit(0)
    elif "iotools" in args and hasattr(args, "func") is False:
        # Case when we type `clinica iotools` on the terminal
        io_parser.print_help()
        exit(0)
    elif "visualize" in args and hasattr(args, "func") is False:
        # Case when we type `clinica visualize` on the terminal
        visualize_parser.print_help()
        exit(0)
    elif "generate" in args and hasattr(args, "func") is False:
        # Case when we type `clinica generate` on the terminal
        generate_parser.print_help()
        exit(0)
    elif args is None or hasattr(args, "func") is False:
        # Case when we type `clinica` on the terminal
        parser.print_help()
        exit(0)

    import clinica.utils.stream as var

    var.clinica_verbose = args.verbose

    if args.verbose is False:
        """
        Enable only cprint(msg) --> clinica print(msg)
        - All the print() will be ignored!
        - All the logging will be redirect to the log file.
        """
        from clinica.utils.stream import FilterOut

        sys.stdout = FilterOut(sys.stdout)
        import logging as python_logging
        import os
        from logging import ERROR, Filter

        from nipype import config, logging

        # Configure Nipype logger for our needs
        config.update_config({
            "logging": {
                "workflow_level": "INFO",
                "log_directory": os.getcwd(),
                "log_to_file": True,
            },
            "execution": {
                "stop_on_first_crash": False,
                "hash_method": "content"
            },
        })
        logging.update_logging(config)

        # Define the LogFilter for ERROR detection
        class LogFilter(Filter):
            """Monitor if an ERROR log signal is sent from Clinica/Nipype.

            If detected, the user will be warned.
            """
            def filter(self, record):
                if record.levelno >= ERROR:
                    import datetime

                    now = datetime.datetime.now().strftime("%H:%M:%S")
                    cprint(
                        f"{Fore.RED}[{now}]{Fore.RESET} An error was found: please "
                        f"check the log file ({args.logname}) or crash file "
                        f"(nipypecli crash <pklz_file>) for details. Clinica is still running."
                    )
                return True

        if args.verbose:
            logger = logging.getLogger("nipype.workflow")
            logger.addFilter(LogFilter())

        # Remove all handlers associated with the root logger object
        for handler in python_logging.root.handlers[:]:
            python_logging.root.removeHandler(handler)

        logging.disable_file_logging()

        # Enable file logging using a filename
        def enable_file_logging(self, filename):
            """Hack to define a filename for the log file.

            It overloads the 'enable_file_logging' method in 'nipype/utils/logger.py' file.
            """
            import logging
            from logging.handlers import RotatingFileHandler as RFHandler

            config = self._config
            LOG_FILENAME = os.path.join(config.get("logging", "log_directory"),
                                        filename)
            hdlr = RFHandler(
                LOG_FILENAME,
                maxBytes=int(config.get("logging", "log_size")),
                backupCount=int(config.get("logging", "log_rotate")),
            )
            formatter = logging.Formatter(fmt=self.fmt, datefmt=self.datefmt)
            hdlr.setFormatter(formatter)
            self._logger.addHandler(hdlr)
            self._fmlogger.addHandler(hdlr)
            self._iflogger.addHandler(hdlr)
            self._hdlr = hdlr

        enable_file_logging(logging, args.logname)

        class Stream:
            def write(self, text):
                print(text)
                sys.stdout.flush()

        python_logging.basicConfig(format=logging.fmt,
                                   datefmt=logging.datefmt,
                                   stream=Stream())

    # Finally, run the command
    args.func(args)