Source code for mapping.__main__

from typing import Annotated

import typer

from wasp2.cli import create_version_callback, verbosity_callback

from .run_mapping import run_make_remap_reads, run_wasp_filt


def _get_mapping_deps() -> dict[str, str]:
    """Get mapping-specific dependency versions."""
    import polars
    import pysam

    return {"polars": polars.__version__, "pysam": pysam.__version__}


_version_callback = create_version_callback(_get_mapping_deps)

app = typer.Typer(
    pretty_exceptions_short=False,
    rich_markup_mode="rich",
    help="[bold]WASP2 Mapping[/bold] - Generate and filter remapped reads for allele-specific analysis.",
    epilog="[dim]Example: wasp2-map make-reads sample.bam variants.vcf.gz -o remap_dir/[/dim]",
)


[docs] @app.callback(invoke_without_command=True) def main( ctx: typer.Context, version: Annotated[ bool, typer.Option( "--version", "-V", callback=_version_callback, is_eager=True, help="Show version and dependency information.", ), ] = False, verbose: Annotated[ bool, typer.Option("--verbose", "-v", help="Enable verbose output with detailed progress."), ] = False, quiet: Annotated[ bool, typer.Option("--quiet", "-q", help="Suppress all output except errors."), ] = False, ) -> None: """WASP2 read mapping commands for allele swapping and filtering.""" verbosity_callback(verbose, quiet)
[docs] @app.command() def make_reads( bam: Annotated[str, typer.Argument(help="BAM file")], variants: Annotated[str, typer.Argument(help="Variant file (VCF, VCF.GZ, BCF, or PGEN)")], samples: Annotated[ list[str] | None, typer.Option( "--samples", "--sample", "--samps", "-s", help=( "One or more samples to use in variant file. " "Accepts comma delimited string, or file with one sample per line" ), ), ] = None, out_dir: Annotated[ str | None, typer.Option( "--out_dir", "--outdir", "--out", "-o", help="Output directory for data to be remapped" ), ] = None, temp_loc: Annotated[ str | None, typer.Option( "--temp_loc", "--temp", "-t", help=( "Directory for keeping intermediary files." "Defaults to removing intermediary files using temp directory" ), ), ] = None, out_json: Annotated[ str | None, typer.Option( "--out_json", "--json", "--outjson", "-j", help=( "Output json containing wasp file info to this file instead of default. " "Defaults to [BAM_PREFIX]_wasp_data_files.json" ), ), ] = None, is_paired: Annotated[ bool | None, typer.Option( "--paired/--single", help="Reads are paired or single. Will autoparse by default (SINGLE END NOT SUPPORTED YET)", ), ] = None, is_phased: Annotated[ bool | None, typer.Option( "--phased/--unphased", help=( "If variant file is phased/unphased. Will autoparse by default " "(PHASED STRONGLY RECOMMENDED-SINGLE END NOT SUPPORTED YET)" ), ), ] = None, include_indels: Annotated[ bool, typer.Option( "--indels/--snps-only", help=( "Include indels in addition to SNPs. " "Default is SNPs only for backward compatibility. Indel support uses variable-length approach." ), ), ] = False, max_indel_len: Annotated[ int, typer.Option( "--max-indel-len", help="Maximum indel length to process (bp). Indels longer than this are skipped.", min=1, ), ] = 10, insert_qual: Annotated[ int, typer.Option( "--insert-qual", help="Quality score for inserted bases (Phred scale). Used when creating alternate reads.", min=0, max=60, ), ] = 30, max_seqs: Annotated[ int, typer.Option( "--max-seqs", help="Maximum number of alternate sequences per read. Reads with more variants are skipped.", min=1, ), ] = 64, threads: Annotated[ int, typer.Option("--threads", help="Threads for BAM I/O operations", min=1) ] = 1, ) -> None: """Generate reads with swapped alleles for remapping.""" sample_str = samples[0] if samples else None run_make_remap_reads( bam_file=bam, variant_file=variants, samples=sample_str, out_dir=out_dir, temp_loc=temp_loc, out_json=out_json, is_paired=is_paired, is_phased=is_phased, include_indels=include_indels, max_indel_len=max_indel_len, insert_qual=insert_qual, max_seqs=max_seqs, threads=threads, )
[docs] @app.command() def filter_remapped( remapped_bam: Annotated[str, typer.Argument(help="remapped BAM File")], to_remap_bam: Annotated[ str | None, typer.Argument(help="to_remap_bam used to generate swapped alleles") ] = None, keep_bam: Annotated[ str | None, typer.Argument(help="BAM containing reads that were not remapped") ] = None, wasp_data_json: Annotated[ str | None, typer.Option( "--wasp_data_json", "--json", "-j", help="json containing wasp file info to load to_remap_bam and keep_bam", ), ] = None, out_bam: Annotated[ str | None, typer.Option( "--out_bam", "--outbam", "--out", "-o", help="File to output filt bam. Will be created in default name and loc if not provided", ), ] = None, remap_keep_bam: Annotated[ str | None, typer.Option( "--remap_keep_bam", help="Also output remapped bam file containing kept reads" ), ] = None, remap_keep_file: Annotated[ str | None, typer.Option("--remap_keep_file", help="Also output txt file with kept read names"), ] = None, threads: Annotated[ int, typer.Option("--threads", help="Threads for BAM I/O (Rust filter supports >1)", min=1) ] = 1, use_rust: Annotated[ bool, typer.Option( "--use-rust/--no-rust", help="Use Rust acceleration if available (respects WASP2_DISABLE_RUST)", ), ] = True, same_locus_slop: Annotated[ int, typer.Option( "--same-locus-slop", help=( "Tolerance (bp) for 'same locus' test. " "Allows remapped reads to differ by this many bp. " "Use 2-3 for indels to handle micro-homology shifts. Use 0 for strict SNP-only matching." ), min=0, ), ] = 0, ) -> None: """Filter remapped reads using WASP algorithm.""" run_wasp_filt( remapped_bam, to_remap_bam=to_remap_bam, keep_bam=keep_bam, wasp_out_bam=out_bam, remap_keep_bam=remap_keep_bam, remap_keep_file=remap_keep_file, wasp_data_json=wasp_data_json, threads=threads, use_rust=use_rust, same_locus_slop=same_locus_slop, )