Exemplo n.º 1
0
def main():
    parser = argparse.ArgumentParser(prog='patho_typing.py',
                                     description='In silico pathogenic typing directly from raw Illumina reads',
                                     formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument('--version', help='Version information', action='version',
                        version='{prog} v{version}'.format(prog=parser.prog, version=__version__))

    parser_required = parser.add_argument_group('Required options')
    parser_required.add_argument('-f', '--fastq', nargs='+', action=utils.required_length((1, 2), '--fastq'),
                                 type=argparse.FileType('r'), metavar=('/path/to/input/file.fq.gz'),
                                 help='Path to single OR paired-end fastq files. If two files are passed, they will be'
                                      ' assumed as being the paired fastq files', required=True)
    parser_required.add_argument('-s', '--species', nargs=2, type=str, metavar=('Yersinia', 'enterocolitica'),
                                 help='Species name', required=True)

    parser_optional_general = parser.add_argument_group('General facultative options')
    parser_optional_general.add_argument('-o', '--outdir', type=str, metavar='/path/to/output/directory/',
                                         help='Path to the directory where the information will be stored',
                                         required=False, default='.')
    parser_optional_general.add_argument('-j', '--threads', type=int, metavar='N', help='Number of threads to use',
                                         required=False, default=1)
    parser_optional_general.add_argument('--trueCoverage', action='store_true',
                                         help='Assess true coverage before continue typing')
    parser_optional_general.add_argument('--noCheckPoint', action='store_true',
                                         help='Ignore the true coverage checking point')
    parser_optional_general.add_argument('--minGeneCoverage', type=int, metavar='N',
                                         help='Minimum typing percentage of target reference gene sequence covered to'
                                              ' consider a gene to be present (value between [0, 100])', required=False)
    parser_optional_general.add_argument('--minGeneIdentity', type=int, metavar='N',
                                         help='Minimum typing percentage of identity of reference gene sequence covered'
                                              ' to consider a gene to be present (value between [0, 100]). One INDEL'
                                              ' will be considered as one difference', required=False)
    parser_optional_general.add_argument('--minGeneDepth', type=int, metavar='N',
                                         help='Minimum typing gene average coverage depth of present positions to'
                                              ' consider a gene to be present (default is 1/3 of average sample'
                                              ' coverage or 15x)', required=False)
    parser_optional_general.add_argument('--doNotRemoveConsensus', action='store_true',
                                         help='Do not remove ReMatCh consensus sequences')
    parser_optional_general.add_argument('--debug', action='store_true',
                                         help='DeBug Mode: do not remove temporary files')

    args = parser.parse_args()

    if args.minGeneCoverage is not None and (args.minGeneCoverage < 0 or args.minGeneCoverage > 100):
        parser.error('--minGeneCoverage should be a value between [0, 100]')
    if args.minGeneIdentity is not None and (args.minGeneIdentity < 0 or args.minGeneIdentity > 100):
        parser.error('--minGeneIdentity should be a value between [0, 100]')

    start_time = time.time()

    args.outdir = os.path.abspath(args.outdir)
    if not os.path.isdir(args.outdir):
        os.makedirs(args.outdir)

    # Start logger
    logfile, time_str = utils.start_logger(args.outdir)

    script_path = utils.general_information(logfile, __version__, args.outdir, time_str)
    print('\n')

    rematch = include_rematch_dependencies_path()

    args.fastq = [fastq.name for fastq in args.fastq]

    reference_file, trueCoverage_file, trueCoverage_sequences, trueCoverage_headers, trueCoverage_config, typing_file, \
    typing_sequences, typing_headers, typing_rules, typing_config = \
        set_reference(args.species, args.outdir, script_path, args.trueCoverage)
    original_reference_file = str(reference_file)

    confirm_genes_fasta_rules(typing_headers, typing_rules)

    run_successfully, bam_file = mapping_reads(args.fastq, reference_file, args.threads, args.outdir, False, 1)
    if run_successfully:
        rematch_dir = os.path.join(args.outdir, 'rematch', '')
        if not os.path.isdir(rematch_dir):
            os.makedirs(rematch_dir)

        if args.trueCoverage:
            if trueCoverage_file is not None:
                trueCoverage_dir = os.path.join(rematch_dir, 'trueCoverage', '')
                if not os.path.isdir(trueCoverage_dir):
                    os.makedirs(trueCoverage_dir)

                print('\n')
                run_successfully, trueCoverage_bam = split_bam(bam_file, trueCoverage_headers, trueCoverage_dir,
                                                               args.threads)
                if run_successfully:
                    run_successfully = indexAlignment(trueCoverage_bam)
                    if run_successfully:
                        reference_file = os.path.join(trueCoverage_dir, 'reference.fasta')
                        write_sequeces(reference_file, trueCoverage_sequences)
                        index_fasta_samtools(reference_file, None, None, True)
                        config = parse_config(trueCoverage_config)
                        runtime, run_successfully, sample_data_general, data_by_gene = \
                            run_rematch.run_rematch(rematch, trueCoverage_dir, reference_file, trueCoverage_bam,
                                                    args.threads, config['length_extra_seq'],
                                                    config['minimum_depth_presence'], config['minimum_depth_call'],
                                                    config['minimum_depth_frequency_dominant_allele'],
                                                    config['minimum_gene_coverage'], config['minimum_gene_identity'],
                                                    args.debug, args.doNotRemoveConsensus)

                        if run_successfully and sample_data_general['mean_sample_coverage'] is not None and \
                                sample_data_general['number_absent_genes'] is not None and \
                                sample_data_general['number_genes_multiple_alleles'] is not None:
                            if args.minGeneDepth is None:
                                args.minGeneDepth = sample_data_general['mean_sample_coverage'] / 3 if \
                                                    sample_data_general['mean_sample_coverage'] / 3 > 15 else \
                                                    15

                            exit_info = []
                            if sample_data_general['mean_sample_coverage'] < config['minimum_read_coverage']:
                                exit_info.append('Sample coverage ({mean}) lower than the minimum'
                                                 ' required ({minimum})'
                                                 ''.format(mean=sample_data_general['mean_sample_coverage'],
                                                           minimum=config['minimum_read_coverage']))
                            if sample_data_general['number_absent_genes'] > config['maximum_number_absent_genes']:
                                exit_info.append('Number of absent genes ({number}) higher than the'
                                                 ' maximum allowed ({maximum})'
                                                 ''.format(number=sample_data_general['number_absent_genes'],
                                                           maximum=config['maximum_number_absent_genes']))
                            if sample_data_general['number_genes_multiple_alleles'] > \
                                    config['maximum_number_genes_multiple_alleles']:
                                exit_info.append('Number of genes with multiple alleles'
                                                 ' ({number}) higher than the maximum'
                                                 ' allowed ({maximum})'
                                                 ''.format(number=sample_data_general['number_genes_multiple_alleles'],
                                                           maximum=config['maximum_number_genes_multiple_alleles']))

                            if len(exit_info) > 0:
                                print('\n' + '\n'.join(exit_info) + '\n')
                                e = 'TrueCoverage requirements not fulfilled'
                                print('\n' + e + '\n')
                                if not args.noCheckPoint:
                                    clean_pathotyping_folder(args.outdir, original_reference_file, args.debug)
                                    _ = utils.runTime(start_time)
                                    sys.exit(e)
                        else:
                            e = 'TrueCoverage module did not run successfully'
                            print('\n' + e + '\n')
                            if not args.noCheckPoint:
                                clean_pathotyping_folder(args.outdir, original_reference_file, args.debug)
                                _ = utils.runTime(start_time)
                                sys.exit(e)

                        print('\n')
                        typing_dir = os.path.join(rematch_dir, 'typing', '')
                        if not os.path.isdir(typing_dir):
                            os.makedirs(typing_dir)
                        run_successfully, bam_file = split_bam(bam_file, typing_headers, typing_dir, args.threads)
                        if run_successfully:
                            run_successfully = indexAlignment(bam_file)
                            if run_successfully:
                                reference_file = os.path.join(typing_dir, 'reference.fasta')
                                write_sequeces(reference_file, typing_sequences)
                                index_fasta_samtools(reference_file, None, None, True)
                                rematch_dir = str(typing_dir)
                if not run_successfully:
                    if args.noCheckPoint:
                        clean_pathotyping_folder(args.outdir, original_reference_file, args.debug)
                        _ = utils.runTime(start_time)
                        sys.exit('Something in the required TrueCoverage analysis went wrong')
            else:
                print('\n'
                      'WARNING: it was not found trueCoverage target files. trueCoverage will not run.'
                      '\n')

        if run_successfully:
            config = parse_config(typing_config)
            if args.minGeneCoverage is not None:
                config['minimum_gene_coverage'] = args.minGeneCoverage
            if args.minGeneIdentity is not None:
                config['minimum_gene_identity'] = args.minGeneIdentity

            runtime, run_successfully, sample_data_general, data_by_gene = \
                run_rematch.run_rematch(rematch, rematch_dir, reference_file, bam_file, args.threads,
                                        config['length_extra_seq'], config['minimum_depth_presence'],
                                        config['minimum_depth_call'], config['minimum_depth_frequency_dominant_allele'],
                                        config['minimum_gene_coverage'], config['minimum_gene_identity'],
                                        args.debug, args.doNotRemoveConsensus)
            if run_successfully and data_by_gene is not None:
                if args.minGeneDepth is None:
                    args.minGeneDepth = sample_data_general['mean_sample_coverage'] / 3 if \
                                        sample_data_general['mean_sample_coverage'] / 3 > 15 else \
                                        15

                _, _, _ = typing.typing(data_by_gene, typing_rules, config['minimum_gene_coverage'],
                                        config['minimum_gene_identity'], args.minGeneDepth, args.outdir)
            else:
                clean_pathotyping_folder(args.outdir, original_reference_file, args.debug)
                _ = utils.runTime(start_time)
                sys.exit('ReMatCh run for pathotyping did not run successfully')
        else:
            clean_pathotyping_folder(args.outdir, original_reference_file, args.debug)
            _ = utils.runTime(start_time)
            sys.exit('Something did not run successfully')

    clean_pathotyping_folder(args.outdir, original_reference_file, args.debug)

    print('\n')
    _ = utils.runTime(start_time)
Exemplo n.º 2
0
def python_arguments(program_name, version):
    """
    Sets pythons arguments

    Parameters
    ----------
    program_name : str
        String with the name of the program
    version : str
        String with version

    Returns
    -------
    parser : argparse.ArgumentParser
        General argparse
    parser_reads : argparse.ArgumentParser.add_subparsers.add_parser
        Reads subparser
        For running the program using fastq files
    parser_index : argparse.ArgumentParser.add_subparsers.add_parser
        Index subparser
        For creating Bowtie2 index
    parser_assembly : argparse.ArgumentParser.add_subparsers.add_parser
        Assembly subparser
        For running the program using a fasta file
    parser_blast : argparse.ArgumentParser.add_subparsers.add_parser
        Blast subparser
        For creating Blast DB
    """
    parser = argparse.ArgumentParser(
        prog=program_name,
        description=
        'Determines which reference sequence is more likely to be present in a'
        ' given sample',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument('--version',
                        help='Version information',
                        action='version',
                        version='{prog} v{version}'.format(prog=parser.prog,
                                                           version=version))

    subparsers = parser.add_subparsers(title='Subcommands',
                                       description='Valid subcommands',
                                       help='Additional help')

    parser_reads = subparsers.add_parser(
        'reads',
        description='Run {prog} using fastq files. If running multiple'
        ' samples using the same reference sequences file,'
        ' consider use first "{prog} index"'
        ' subcommand.'.format(prog=parser.prog),
        help='reads --help',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser_index = subparsers.add_parser(
        'index',
        description='Creates Bowtie2 index. This is useful when running the'
        ' same reference sequences file for different reads'
        ' dataset.',
        help='index --help',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser_assembly = subparsers.add_parser(
        'assembly',
        description='Run {prog} using a fasta file. If running'
        ' multiple samples using the same DB sequence file,'
        ' consider use first "{prog} blast"'
        ' subcommand.'.format(prog=parser.prog),
        help='assembly --help',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser_blast = subparsers.add_parser(
        'blast',
        description='Creates Blast DB. This is useful when running the same'
        ' DB sequence file for different assemblies.',
        help='blast --help',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)

    parser_reads_required = parser_reads.add_argument_group('Required options')
    parser_reads_required.add_argument(
        '-f',
        '--fastq',
        nargs='+',
        action=utils.required_length((1, 2), '--fastq'),
        type=argparse.FileType('r'),
        metavar='/path/to/input/file.fq.gz',
        help=
        'Path to single OR paired-end fastq files. If two files are passed, they'
        ' will be assumed as being the paired fastq files',
        required=True)

    parser_reads_reference = parser_reads.add_mutually_exclusive_group(
        required=True)
    parser_reads_reference.add_argument(
        '-r',
        '--reference',
        nargs='+',
        type=str,
        metavar='/path/to/reference/sequences.file',
        help=
        'Path to reference sequences file. If Bowtie2 index was already produced,'
        ' only provide the file name that ends with ".1.bt2", but without this'
        ' termination (for example, for a Bowtie2 index'
        ' "/file/sequences.fasta.1.bt2", only provide "/file/sequences.fasta").'
        ' If no Bowtie2 index files are found, those will be created in --outdir.'
        ' If more than one file is passed, a type for each file will be'
        ' determined. Give the files name in the same order that the type must be'
        ' determined.')
    parser_reads_reference.add_argument(
        '--org',
        nargs=2,
        type=str.lower,
        metavar=('escherichia', 'coli'),
        help='Name of the organism with reference sequences provided together'
        ' with {} for typing ("seqtyping/reference_sequences/"'
        ' folder)'.format(parser.prog),
        action=utils.arguments_choices_words(get_species_allowed(), '--org'))

    parser_reads_optional_general = parser_reads.add_argument_group(
        'General facultative options')
    parser_reads_optional_general.add_argument(
        '-o',
        '--outdir',
        type=str,
        metavar='/path/to/output/directory/',
        help='Path to the directory where the information will be stored'
        ' (default: ./',
        required=False,
        default='.')
    parser_reads_optional_general.add_argument(
        '-j',
        '--threads',
        type=int,
        metavar='N',
        help='Number of threads to use (default: 1)',
        required=False,
        default=1)
    parser_reads_optional_general.add_argument('--mapRefTogether',
                                               action='store_true',
                                               help=argparse.SUPPRESS)
    # parser_reads_optional_general.add_argument('--mapRefTogether', action='store_true',
    #                                            help='Map the reads against all references together')
    parser_reads_optional_general.add_argument(
        '--typeSeparator',
        type=str,
        metavar='_',
        help='Last single character separating the general sequence header from'
        ' the last part containing the type (default: _)',
        required=False,
        default='_')
    parser_reads_optional_general.add_argument(
        '--extraSeq',
        type=int,
        metavar='N',
        help='Sequence length added to both ends of target sequences (usefull to'
        ' improve reads mapping to the target one) that will be trimmed in'
        ' ReMatCh outputs (default when not using --org: 0)',
        required=False,
        default=0)
    parser_reads_optional_general.add_argument(
        '--minCovPresence',
        type=int,
        metavar='N',
        help='Reference position minimum coverage depth to consider the position'
        ' to be present in the sample (default when not using --org: 5)',
        required=False,
        default=5)
    parser_reads_optional_general.add_argument(
        '--minCovCall',
        type=int,
        metavar='N',
        help='Reference position minimum coverage depth to perform a base call'
        ' (default when not using --org: 10)',
        required=False,
        default=10)
    parser_reads_optional_general.add_argument('--minFrequencyDominantAllele',
                                               type=float,
                                               metavar='0.6',
                                               help=argparse.SUPPRESS,
                                               required=False,
                                               default=0.6)
    # parser_optional_general.add_argument('--minFrequencyDominantAllele', type=float, metavar='0.6',
    #                                      help='Minimum relative frequency of the dominant allele coverage depth'
    #                                           ' (value between [0, 1]). Positions with lower values will be'
    #                                           ' considered as having multiple alleles (and will be coded as N)',
    #                                      required=False, default=0.6)
    parser_reads_optional_general.add_argument(
        '--minGeneCoverage',
        type=int,
        metavar='N',
        help='Minimum percentage of target reference sequence covered to'
        ' consider a sequence to be present (value between [0, 100])'
        ' (default when not using --org: 60)',
        required=False,
        default=60)
    parser_reads_optional_general.add_argument(
        '--minDepthCoverage',
        type=int,
        metavar='N',
        help='Minimum depth of coverage of target reference sequence to'
        ' consider a sequence to be present (default: 2)',
        required=False,
        default=2)
    # parser_reads_optional_general.add_argument('--minGeneIdentity', type=int, metavar='N', help=argparse.SUPPRESS,
    #                                            required=False, default=80)
    parser_reads_optional_general.add_argument(
        '--minGeneIdentity',
        type=int,
        metavar='N',
        help='Minimum percentage of identity of reference sequence covered to'
        ' consider a gene to be present (value between [0, 100]). One INDEL'
        ' will be considered as one difference (default: 80)',
        required=False,
        default=80)
    parser_reads_optional_general.add_argument(
        '--bowtieAlgo',
        type=str,
        metavar='"--very-sensitive-local"',
        help='Bowtie2 alignment mode. It can be an end-to-end alignment'
        ' (unclipped alignment) or local alignment (soft clipped'
        ' alignment). Also, can choose between fast or sensitive'
        ' alignments. Please check Bowtie2 manual for extra information:'
        ' http://bowtie-bio.sourceforge.net/bowtie2/index.shtml .'
        ' This option should be provided between quotes and starting with'
        ' an empty space (like --bowtieAlgo " --very-fast") or using equal'
        ' sign (like --bowtieAlgo="--very-fast")',
        required=False,
        default='--very-sensitive-local')
    parser_reads_optional_general.add_argument(
        '--doNotRemoveConsensus',
        action='store_true',
        help='Do not remove ReMatCh consensus sequences')
    parser_reads_optional_general.add_argument(
        '--debug',
        action='store_true',
        help='Debug mode: do not remove temporary files')
    parser_reads_optional_general.add_argument('--resume',
                                               action='store_true',
                                               help='Resume %(prog)s')
    parser_reads_optional_general.add_argument(
        '--notClean',
        action='store_true',
        help='Do not remove intermediate files')

    parser_index_reference = parser_index.add_mutually_exclusive_group(
        required=True)
    parser_index_reference.add_argument(
        '-r',
        '--reference',
        nargs='+',
        type=str,
        metavar='/path/to/reference/sequences.fasta',
        help=
        'Path to reference sequences file. If more than one file is passed, a'
        ' Bowtie2 index for each file will be created.')
    parser_index_reference.add_argument(
        '--org',
        nargs=2,
        type=str.lower,
        metavar=('escherichia', 'coli'),
        help='Name of the organism with reference sequences provided'
        ' ("seqtyping/reference_sequences/" folder) together'
        ' with seq_typing.py for typing',
        action=utils.arguments_choices_words(get_species_allowed(), '--org'))

    parser_index_optional_general = parser_index.add_argument_group(
        'General facultative options')
    parser_index_optional_general.add_argument(
        '-o',
        '--outdir',
        type=str,
        metavar='/path/to/output/directory/',
        help='Path to the directory where the information will be stored'
        ' (default: ./)',
        required=False,
        default='.')
    parser_index_optional_general.add_argument(
        '-j',
        '--threads',
        type=int,
        metavar='N',
        required=False,
        help='Number of threads to use (default: 1)',
        default=1)

    parser_assembly_required = parser_assembly.add_argument_group(
        'Required options')
    parser_assembly_required.add_argument(
        '-f',
        '--fasta',
        nargs=1,
        type=argparse.FileType('r'),
        metavar='/path/to/query/assembly_file.fasta',
        help='Path to fasta file containing the query sequences from which the'
        ' types should be assessed',
        required=True)

    parser_assembly_reference = parser_assembly.add_mutually_exclusive_group(
        required=True)
    parser_assembly_reference.add_argument(
        '-b',
        '--blast',
        nargs='+',
        type=argparse.FileType('r'),
        metavar='/path/to/Blast/db.sequences.file',
        help='Path to DB sequence file. If Blast DB was already produced, only'
        ' provide the file that do not end with ".n*" something (do not use for'
        ' example /blast_db.sequences.fasta.nhr). If no Blast DB is found for'
        ' the DB sequence file, one will be created in --outdir. If more than'
        ' one Blast DB file is passed, a type for each file will be determined.'
        ' Give the files in the same order that the type must be determined.')
    parser_assembly_reference.add_argument(
        '--org',
        nargs=2,
        type=str.lower,
        metavar=('escherichia', 'coli'),
        help='Name of the organism with DB sequence file provided'
        ' ("seqtyping/reference_sequences/" folder) together'
        ' with seq_typing.py for typing',
        action=utils.arguments_choices_words(get_species_allowed(), '--org'))

    parser_assembly_optional_reference = parser_assembly.add_argument_group(
        'Required option for --blast')
    parser_assembly_optional_reference.add_argument(
        '-t',
        '--type',
        choices=['nucl', 'prot'],
        type=str,
        metavar='nucl',
        help='Blast DB type (available options: %(choices)s)')

    parser_assembly_optional_general = parser_assembly.add_argument_group(
        'General facultative options')
    parser_assembly_optional_general.add_argument(
        '-o',
        '--outdir',
        type=str,
        metavar='/path/to/output/directory/',
        help='Path to the directory where the information will be stored'
        ' (default: ./)',
        required=False,
        default='.')
    parser_assembly_optional_general.add_argument(
        '-j',
        '--threads',
        type=int,
        metavar='N',
        required=False,
        help='Number of threads to use (default: 1)',
        default=1)
    parser_assembly_optional_general.add_argument(
        '--typeSeparator',
        type=str,
        metavar='_',
        help='Last single character separating the general sequence header'
        ' from the last part containing the type (default: _)',
        required=False,
        default='_')
    parser_assembly_optional_general.add_argument(
        '--minGeneCoverage',
        type=int,
        metavar='N',
        help='Minimum percentage of target reference sequence covered to'
        ' consider a sequence to be present (value between [0, 100])'
        ' (default when not using --org: 60)',
        required=False,
        default=60)
    parser_assembly_optional_general.add_argument(
        '--minGeneIdentity',
        type=int,
        metavar='N',
        help='Minimum percentage of identity of reference sequence covered'
        ' to consider a gene to be present (value between [0, 100])'
        ' (default: 80)',
        required=False,
        default=80)
    parser_assembly_optional_general.add_argument('--minDepthCoverage',
                                                  type=int,
                                                  metavar='N',
                                                  help=argparse.SUPPRESS,
                                                  required=False,
                                                  default=1)
    parser_assembly_optional_general.add_argument(
        '--debug',
        action='store_true',
        help='Debug mode: do not remove temporary files')
    parser_assembly_optional_general.add_argument('--resume',
                                                  action='store_true',
                                                  help=argparse.SUPPRESS)

    parser_blast_required = parser_blast.add_argument_group('Required options')
    parser_blast_required.add_argument(
        '-t',
        '--type',
        choices=['nucl', 'prot'],
        type=str,
        metavar='nucl',
        help='Blast DB type (available options: %(choices)s)',
        required=True)

    parser_blast_reference = parser_blast.add_mutually_exclusive_group(
        required=True)
    parser_blast_reference.add_argument(
        '-f',
        '--fasta',
        nargs='+',
        type=argparse.FileType('r'),
        metavar='/path/to/db.sequences.fasta',
        help=
        'Path to DB sequence file. If more than one file is passed, a Blast DB for'
        ' each file will be created.')
    parser_blast_reference.add_argument(
        '--org',
        nargs=2,
        type=str.lower,
        metavar=('escherichia', 'coli'),
        help='Name of the organism with DB sequence file provided'
        ' ("seqtyping/reference_sequences/" folder) together'
        ' with seq_typing.py for typing',
        action=utils.arguments_choices_words(get_species_allowed(), '--org'))

    parser_blast_optional_general = parser_blast.add_argument_group(
        'General facultative options')
    parser_blast_optional_general.add_argument(
        '-o',
        '--outdir',
        type=str,
        metavar='/path/to/output/directory/',
        help='Path to the directory where the information will be stored'
        ' (default: ./)',
        required=False,
        default='.')

    parser_reads.set_defaults(func=reads_subcommand)
    parser_index.set_defaults(func=index_subcommand)
    parser_assembly.set_defaults(func=assembly_subcommand)
    parser_blast.set_defaults(func=blast_subcommand)

    return parser, parser_reads, parser_index, parser_assembly, parser_blast