From 6b26e296b6b4c74f04158e44dded62a46784f477 Mon Sep 17 00:00:00 2001 From: Remi-Andre Olsen Date: Fri, 15 Mar 2024 16:57:50 +0100 Subject: [PATCH 1/8] WIP typer implementation --- anglerfish/__main__.py | 4 +- anglerfish/anglerfish.py | 104 ---------------------------------- anglerfish/cli.py | 119 +++++++++++++++++++++++++++++++++++++++ setup.py | 2 +- 4 files changed, 122 insertions(+), 107 deletions(-) create mode 100644 anglerfish/cli.py diff --git a/anglerfish/__main__.py b/anglerfish/__main__.py index 9310eaa..57d1c17 100644 --- a/anglerfish/__main__.py +++ b/anglerfish/__main__.py @@ -1,7 +1,7 @@ import multiprocessing -from .anglerfish import anglerfish +from .anglerfish import app if __name__ == "__main__": multiprocessing.freeze_support() - anglerfish() + app() diff --git a/anglerfish/anglerfish.py b/anglerfish/anglerfish.py index f7a56cf..02c957a 100755 --- a/anglerfish/anglerfish.py +++ b/anglerfish/anglerfish.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -import argparse import glob import gzip import logging @@ -7,7 +6,6 @@ import os import uuid from collections import Counter -from datetime import datetime as dt from itertools import groupby import numpy as np @@ -245,105 +243,3 @@ def run_demux(args): report.write_report(args.out_fastq) report.write_json(args.out_fastq) report.write_dataframe(args.out_fastq, ss) - - if args.skip_fastqc: - log.warning( - " As of version 0.4.1, built in support for FastQC + MultiQC is removed. The '-f' flag is redundant." - ) - - -def anglerfish(): - parser = argparse.ArgumentParser( - description="Tools to demux I7 and I5 barcodes when sequenced by single-molecules" - ) - parser.add_argument( - "--samplesheet", - "-s", - required=True, - help="CSV formatted list of samples and barcodes", - ) - parser.add_argument( - "--out_fastq", - "-o", - default=".", - help="Analysis output folder (default: Current dir)", - ) - parser.add_argument( - "--threads", - "-t", - default=4, - type=int, - help="Number of threads to use (default: 4)", - ) - parser.add_argument( - "--skip_demux", - "-c", - action="store_true", - help="Only do BC counting and not demuxing", - ) - parser.add_argument( - "--skip_fastqc", "-f", action="store_true", help=argparse.SUPPRESS - ) - parser.add_argument( - "--max-distance", - "-m", - type=int, - help="Manually set maximum edit distance for BC matching, automatically set this is set to either 1 or 2", - ) - parser.add_argument( - "--max-unknowns", - "-u", - type=int, - help="Maximum number of unknown indices to show in the output (default: length of samplesheet + 10)", - ) - parser.add_argument( - "--run_name", - "-r", - default="anglerfish", - help="Name of the run (default: anglerfish)", - ) - parser.add_argument( - "--lenient", - "-l", - action="store_true", - help="Will try reverse complementing the I5 and/or I7 indices and choose the best match.", - ) - parser.add_argument( - "--lenient_factor", - "-x", - default=4.0, - type=float, - help="If lenient is set, this is the minimum factor of additional matches required to reverse complement the index (default: 4.0)", - ) - parser.add_argument( - "--force_rc", - "-p", - choices=["i7", "i5", "i7+i5"], - help="Force reverse complementing the I5 and/or I7 indices. This will disregard lenient mode.", - ) - parser.add_argument( - "--ont_barcodes", - "-n", - action="store_true", - help="Will assume the samplesheet refers to a single ONT run prepped with a barcoding kit. And will treat each barcode separately", - ) - parser.add_argument( - "--debug", "-d", action="store_true", help="Extra commandline output" - ) - parser.add_argument( - "--version", - "-v", - action="version", - help="Print version and quit", - version=f'anglerfish {pkg_resources.get_distribution("bio-anglerfish").version}', - ) - args = parser.parse_args() - utcnow = dt.utcnow() - runname = utcnow.strftime(f"{args.run_name}_%Y_%m_%d_%H%M%S") - - assert os.path.exists(args.out_fastq) - assert os.path.exists(args.samplesheet) - args.out_fastq = os.path.join(os.path.abspath(args.out_fastq), runname) - args.samplesheet = os.path.abspath(args.samplesheet) - args.run_name = runname - run_demux(args) diff --git a/anglerfish/cli.py b/anglerfish/cli.py new file mode 100644 index 0000000..c653b41 --- /dev/null +++ b/anglerfish/cli.py @@ -0,0 +1,119 @@ +import argparse +import os +from datetime import datetime as dt +from enum import Enum + +import typer +from typing_extensions import Annotated + +from .anglerfish import run_demux + +app = typer.Typer(pretty_exceptions_show_locals=False) + + +class IndexOrientations(str, Enum): + i7 = "i7" + i5 = "i5" + i7i5 = "i7+i5" + default = "default" + + +@app.command() +def run( + samplesheet: Annotated[ + str, + typer.Option( + "--samplesheet", "-s", help="CSV formatted list of samples and barcodes" + ), + ], + out_fastq: Annotated[ + str, typer.Option("--out_fastq", "-o", help="Analysis output folder") + ] = ".", + threads: Annotated[ + int, typer.Option("--threads", "-t", help="Number of threads to use") + ] = 4, + skip_demux: Annotated[ + bool, + typer.Option("--skip_demux", "-c", help="Only do BC counting and not demuxing"), + ] = False, + max_distance: Annotated[ + int, + typer.Option( + "--max-distance", + "-m", + help="Manually set maximum edit distance for BC matching, automatically set this is set to either 1 or 2", + ), + ] = 2, + max_unknowns: Annotated[ + int, + typer.Option( + "--max-unknowns", + "-u", + help="Maximum number of unknown indices to show in the output. default is length of samplesheet + 10", + ), + ] = 0, + run_name: Annotated[ + str, typer.Option("--run_name", "-r", help="Run name") + ] = "anglerfish", + lenient: Annotated[ + bool, + typer.Option( + "--lenient", + "-l", + help="Will try reverse complementing the I5 and/or I7 indices and choose the best match.", + ), + ] = False, + lenient_factor: Annotated[ + int, + typer.Option( + "--lenient_factor", + "-x", + help="If lenient is set, this is the minimum factor of additional matches required to reverse complement the index", + ), + ] = 2, + force_rc: Annotated[ + IndexOrientations, + typer.Option( + "--force_rc", + "-p", + help="Force reverse complementing the I5 and/or I7 indices. This will disregard lenient mode.", + ), + ] = IndexOrientations.default, + ont_barcodes: Annotated[ + bool, + typer.Option( + "--ont_barcodes", + "-n", + help="Will assume the samplesheet refers to a single ONT run prepped with a barcoding kit. And will treat each barcode separately", + ), + ] = False, + debug: Annotated[bool, typer.Option("--debug", "-d", help="Debug mode")] = False, + version: Annotated[ + bool, typer.Option("--version", "-v", help="Print version and quit") + ] = False, +): + """Run anglerfish demux. Now with emojis 💩✨""" + args = argparse.Namespace( + samplesheet=samplesheet, + out_fastq=out_fastq, + threads=threads, + skip_demux=skip_demux, + max_distance=max_distance, + max_unknowns=max_unknowns, + run_name=run_name, + lenient=lenient, + lenient_factor=lenient_factor, + force_rc=force_rc.value, + ont_barcodes=ont_barcodes, + debug=debug, + version=version, + ) + utcnow = dt.utcnow() + runname = utcnow.strftime(f"{args.run_name}_%Y_%m_%d_%H%M%S") + assert os.path.exists(args.out_fastq) + assert os.path.exists(args.samplesheet) + args.out_fastq = os.path.join(os.path.abspath(args.out_fastq), runname) + args.samplesheet = os.path.abspath(args.samplesheet) + args.run_name = runname + + run_demux(args) diff --git a/setup.py b/setup.py index 47d8660..723000b 100644 --- a/setup.py +++ b/setup.py @@ -38,7 +38,7 @@ extras_require={"dev": ["ruff", "mypy", "editorconfig-checker"]}, entry_points={ "console_scripts": [ - "anglerfish=anglerfish.anglerfish:anglerfish", + "anglerfish=anglerfish.cli:app", "anglerfish-explore=anglerfish.explore.cli:main", ], }, From 0b9c37b0f0a92a2e3b76105ef8c33bdab6d1b068 Mon Sep 17 00:00:00 2001 From: Remi-Andre Olsen Date: Fri, 12 Apr 2024 17:02:29 +0200 Subject: [PATCH 2/8] Added explore as a command --- anglerfish/cli.py | 180 +++++++++++++++++++++++++++++++++++--- anglerfish/explore/cli.py | 100 --------------------- 2 files changed, 170 insertions(+), 110 deletions(-) delete mode 100644 anglerfish/explore/cli.py diff --git a/anglerfish/cli.py b/anglerfish/cli.py index c653b41..6d8b16d 100644 --- a/anglerfish/cli.py +++ b/anglerfish/cli.py @@ -1,12 +1,15 @@ import argparse +import datetime as dt import os -from datetime import datetime as dt from enum import Enum +import pkg_resources import typer from typing_extensions import Annotated +from typing import Optional from .anglerfish import run_demux +from .explore.explore import run_explore app = typer.Typer(pretty_exceptions_show_locals=False) @@ -15,7 +18,155 @@ class IndexOrientations(str, Enum): i7 = "i7" i5 = "i5" i7i5 = "i7+i5" - default = "default" + + +def version_callback(value: bool): + if value: + print(f'anglerfish {pkg_resources.get_distribution("bio-anglerfish").version}') + raise typer.Exit() + + +def deprecated_callback(value: bool): + if value: + raise typer.BadParameter( + "Please use the 'anglerfish run -s' command to run anglerfish with a samplesheet. Running only 'anglerfish -s' is not supported as of version 0.7.0" + ) + + +@app.callback() +def main( + version: Annotated[ + Optional[bool], + typer.Option( + "--version", + "-v", + help="Print version and quit", + is_eager=True, + callback=version_callback, + ), + ] = False, + samplesheet: Annotated[ + Optional[str], + typer.Option( + "--samplesheet", + "-s", + hidden=True, + is_eager=True, + callback=deprecated_callback, + ), + ] = "", +): + """ + Anglerfish is a tool designed to demultiplex Illumina libraries sequenced on Oxford Nanopore flowcells. + The primary purpose for this would be to do QC, i.e. to check pool balancing, assess contamination, library insert sizes and so on. + """ + if samplesheet: + raise typer.BadParameter( + "Please use the 'run' command to run anglerfish with a samplesheet. Running only 'anglerfish' is not supported as of version 0.7.0" + ) + + +@app.command() +def explore( + fastq: Annotated[str, typer.Option("--fastq", "-f", help="Fastq file to align")], + outdir: Annotated[str, typer.Option("--outdir", "-o", help="Output directory")], + threads: Annotated[ + int, + typer.Option( + "--threads", + "-t", + help="Number of threads specified to minimap2", + ), + ] = 4, + use_existing: Annotated[ + bool, + typer.Option( + "--use-existing", + "-e", + help="Use existing alignments if found in the specified output directory.", + ), + ] = False, + good_hit_threshold: Annotated[ + float, + typer.Option( + "--good_hit_threshold", + "-g", + help="Fraction of adaptor bases immediately before and immediately after index insert required to match perfectly for a hit to be considered a good hit", + ), + ] = 0.9, + insert_thres_low: Annotated[ + int, + typer.Option( + "--insert_thres_low", + "-i", + help="Lower threshold for index(+UMI) insert length, with value included.", + ), + ] = 4, + insert_thres_high: Annotated[ + int, + typer.Option( + "--insert_thres_high", + "-j", + help="Upper threshold for index(+UMI) insert length, with value included.", + ), + ] = 30, + minimap_b: Annotated[ + int, + typer.Option( + "--minimap_b", + "-B", + help="Minimap2 -B parameter, mismatch penalty.", + ), + ] = 4, + min_hits_per_adaptor: Annotated[ + int, + typer.Option( + "--min_hits_per_adaptor", + "-m", + help="Minimum number of good hits for an adaptor to be included in the analysis.", + ), + ] = 50, + umi_threshold: Annotated[ + float, + typer.Option( + "--umi_threshold", + "-u", + help="Minimum number of bases in insert to perform entropy calculation.", + ), + ] = 11, + kmer_length: Annotated[ + int, + typer.Option( + "--kmer_length", + "-k", + help="Kmer length for entropy calculation.", + ), + ] = 2, + version: Annotated[ + Optional[bool], + typer.Option( + "--version", + "-v", + help="Print version and quit", + is_eager=True, + callback=version_callback, + ), + ] = False, +): + """This is an advanced samplesheet-free version of anglerfish.""" + run_explore( + fastq, + outdir, + threads, + use_existing, + good_hit_threshold, + insert_thres_low, + insert_thres_high, + minimap_b, + min_hits_per_adaptor, + umi_threshold, + kmer_length, + ) @app.command() @@ -70,15 +221,15 @@ def run( "-x", help="If lenient is set, this is the minimum factor of additional matches required to reverse complement the index", ), - ] = 2, + ] = 4.0, force_rc: Annotated[ IndexOrientations, typer.Option( "--force_rc", "-p", - help="Force reverse complementing the I5 and/or I7 indices. This will disregard lenient mode.", + help="Force reverse complementing the I5 and/or I7 indices. If set to anything other than 'original' this will disregard lenient mode.", ), - ] = IndexOrientations.default, + ] = None, ont_barcodes: Annotated[ bool, typer.Option( @@ -89,10 +240,17 @@ def run( ] = False, debug: Annotated[bool, typer.Option("--debug", "-d", help="Debug mode")] = False, version: Annotated[ - bool, typer.Option("--version", "-v", help="Print version and quit") + Optional[bool], + typer.Option( + "--version", + "-v", + help="Print version and quit", + is_eager=True, + callback=version_callback, + ), ] = False, ): - """Run anglerfish demux. Now with emojis 💩✨""" + """Run anglerfish. This is the main command for anglerfish""" args = argparse.Namespace( samplesheet=samplesheet, out_fastq=out_fastq, @@ -108,10 +266,12 @@ def run( debug=debug, version=version, ) - utcnow = dt.utcnow() + utcnow = dt.datetime.now(dt.timezone.utc) runname = utcnow.strftime(f"{args.run_name}_%Y_%m_%d_%H%M%S") - assert os.path.exists(args.out_fastq) - assert os.path.exists(args.samplesheet) + assert os.path.exists(args.out_fastq), f"Output folder '{args.out_fastq}' not found" + assert os.path.exists( + args.samplesheet + ), f"Samplesheet file '{args.samplesheet}' not found, please provide a valid path when using the --samplesheet option." args.out_fastq = os.path.join(os.path.abspath(args.out_fastq), runname) args.samplesheet = os.path.abspath(args.samplesheet) args.run_name = runname diff --git a/anglerfish/explore/cli.py b/anglerfish/explore/cli.py deleted file mode 100644 index 1cceab5..0000000 --- a/anglerfish/explore/cli.py +++ /dev/null @@ -1,100 +0,0 @@ -import click - -from anglerfish.explore.explore import run_explore - - -@click.command() -@click.option("-f", "--fastq", required=True, help="Fastq file to align") -@click.option("-o", "--outdir", required=True, help="Output directory") -@click.option( - "-t", - "--threads", - default=4, - type=int, - help="Number of threads specified to minimap2", -) -@click.option( - "-e", - "--use-existing", - is_flag=True, - help="Use existing alignments if found in the specified output directory.", -) -@click.option( - "-g", - "--good_hit_threshold", - default=0.9, - type=float, - help="Fraction of adaptor bases immediately before and immediately after index insert required to match perfectly for a hit to be considered a good hit (default=0.9).", -) -@click.option( - "-i", - "--insert_thres_low", - default=4, - type=int, - help="Lower threshold for index(+UMI) insert length, with value included (deafult=4).", -) -@click.option( - "-j", - "--insert_thres_high", - default=30, - type=int, - help="Upper threshold for index(+UMI) insert length, with value included (default=30).", -) -@click.option( - "-B", - "--minimap_b", - default=4, - type=int, - help="Minimap2 -B parameter, mismatch penalty (default=4).", -) -@click.option( - "-m", - "--min_hits_per_adaptor", - default=50, - type=int, - help="Minimum number of good hits for an adaptor to be included in the analysis (default=50).", -) -@click.option( - "-u", - "--umi_threshold", - default=11, - type=float, - help="Minimum number of bases in insert to perform entropy calculation (default=11).", -) -@click.option( - "-k", - "--kmer_length", - default=2, - type=int, - help="Length of k-mers to use for entropy calculation (default=2).", -) -def main( - fastq, - outdir, - threads, - use_existing, - good_hit_threshold, - insert_thres_low, - insert_thres_high, - minimap_b, - min_hits_per_adaptor, - umi_threshold, - kmer_length, -): - run_explore( - fastq, - outdir, - threads, - use_existing, - good_hit_threshold, - insert_thres_low, - insert_thres_high, - minimap_b, - min_hits_per_adaptor, - umi_threshold, - kmer_length, - ) - - -if __name__ == "__main__": - main() From bf5fea5892e0e7b2bb6e7a92d2c2d477d48ef206 Mon Sep 17 00:00:00 2001 From: Remi-Andre Olsen Date: Mon, 15 Apr 2024 10:55:44 +0200 Subject: [PATCH 3/8] Fix lint and typer dependency --- anglerfish/anglerfish.py | 2 +- anglerfish/cli.py | 7 ++++--- requirements.txt | 3 ++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/anglerfish/anglerfish.py b/anglerfish/anglerfish.py index 02c957a..c69010e 100755 --- a/anglerfish/anglerfish.py +++ b/anglerfish/anglerfish.py @@ -116,7 +116,7 @@ def run_demux(args): "i5": {"i7_reversed": False, "i5_reversed": True}, "i7+i5": {"i7_reversed": True, "i5_reversed": True}, } - if args.force_rc is not None: + if args.force_rc != "original": log.info( f" Force reverse complementing {args.force_rc} index for adaptor {adaptor_name}. Lenient mode is disabled" ) diff --git a/anglerfish/cli.py b/anglerfish/cli.py index 6d8b16d..ee1b68c 100644 --- a/anglerfish/cli.py +++ b/anglerfish/cli.py @@ -2,11 +2,11 @@ import datetime as dt import os from enum import Enum +from typing import Optional import pkg_resources import typer from typing_extensions import Annotated -from typing import Optional from .anglerfish import run_demux from .explore.explore import run_explore @@ -18,6 +18,7 @@ class IndexOrientations(str, Enum): i7 = "i7" i5 = "i5" i7i5 = "i7+i5" + original = "original" def version_callback(value: bool): @@ -215,7 +216,7 @@ def run( ), ] = False, lenient_factor: Annotated[ - int, + float, typer.Option( "--lenient_factor", "-x", @@ -229,7 +230,7 @@ def run( "-p", help="Force reverse complementing the I5 and/or I7 indices. If set to anything other than 'original' this will disregard lenient mode.", ), - ] = None, + ] = IndexOrientations.original, ont_barcodes: Annotated[ bool, typer.Option( diff --git a/requirements.txt b/requirements.txt index db39464..723e451 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,5 @@ click==8.1.7 levenshtein==0.23.0 numpy==1.26.3 pandas==2.1.4 -pyyaml==6.0.1 \ No newline at end of file +pyyaml==6.0.1 +typer==0.9.0 \ No newline at end of file From 0492f1928622c2b1222a2027a35e6c39e9c25f95 Mon Sep 17 00:00:00 2001 From: Remi-Andre Olsen Date: Mon, 15 Apr 2024 11:01:55 +0200 Subject: [PATCH 4/8] Add test for explore --- .github/workflows/anglerfish.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/anglerfish.yml b/.github/workflows/anglerfish.yml index 0bef153..41643c4 100644 --- a/.github/workflows/anglerfish.yml +++ b/.github/workflows/anglerfish.yml @@ -37,4 +37,10 @@ jobs: - shell: bash -l {0} name: Run anglerfish with test data run: | - anglerfish -s test/samples.csv + anglerfish run -s test/samples.csv + + # Run anglerfish explore + - shell: bash -l {0} + name: Run anglerfish explore + run: | + anglerfish explore -f test/BC18_P14351_1001.fastq.gz -o test/explore_output From b8efca4780c74dece62e079fb9fc0fd226fda0fe Mon Sep 17 00:00:00 2001 From: Remi-Andre Olsen Date: Mon, 15 Apr 2024 13:59:59 +0200 Subject: [PATCH 5/8] Fixed mypy error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Matthias Hörtenhuber --- anglerfish/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/anglerfish/__main__.py b/anglerfish/__main__.py index 57d1c17..9816aeb 100644 --- a/anglerfish/__main__.py +++ b/anglerfish/__main__.py @@ -1,6 +1,6 @@ import multiprocessing -from .anglerfish import app +from .cli import app if __name__ == "__main__": multiprocessing.freeze_support() From 6023a889412a1885b96e67ccf62ebd8302bafe35 Mon Sep 17 00:00:00 2001 From: Remi-Andre Olsen Date: Tue, 16 Apr 2024 13:30:06 +0200 Subject: [PATCH 6/8] Add anglerfish ASCII art Co-authored-by: Franziska Bonath --- anglerfish/anglerfish.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/anglerfish/anglerfish.py b/anglerfish/anglerfish.py index c69010e..492dad6 100755 --- a/anglerfish/anglerfish.py +++ b/anglerfish/anglerfish.py @@ -4,6 +4,7 @@ import logging import multiprocessing import os +import sys import uuid from collections import Counter from itertools import groupby @@ -37,7 +38,18 @@ def run_demux(args): ss = SampleSheet(args.samplesheet, args.ont_barcodes) version = pkg_resources.get_distribution("bio-anglerfish").version report = Report(args.run_name, run_uuid, version) - + sys.stderr.write(""" + ___ + ( ) \ -..__ + _.|~”~~~”…_ + ^´ `>. +(+ (+ ) “<..<^( + `´ ``´ ___ ( + \__..~ __( _…_( + \ / + “--…_ _..~%´ + ```´´ +""") log.info(f" version {version}") log.info(f" arguments {vars(args)}") log.info(f" run uuid {run_uuid}") From d5d538a4c489de33038ff76a5d394601e2a620bb Mon Sep 17 00:00:00 2001 From: Remi-Andre Olsen Date: Tue, 16 Apr 2024 13:34:00 +0200 Subject: [PATCH 7/8] Small clean-up --- README.md | 6 +++--- setup.py | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 63bd962..e4ffa20 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,7 @@ P12345_101,truseq,CAGGACGT,/path/to/*.fastq.gz Then run: ``` -anglerfish -s /path/to/samples.csv +anglerfish run -s /path/to/samples.csv ``` ### Options @@ -173,10 +173,10 @@ In folder `anglerfish_????_??_??_?????/` ## Anglerfish Explore (Experimental) -`anglerfish-explore` is a command that aims to explore a sequencing pool without a given samplesheet and give hints on what adapter types are present, which index lenghts are used and whether there are any UMIs within the index sequence. The Anglerfish explore command is still under heavy development but can be triggered by running, e.g. for help text: +`anglerfish explore` is a command that aims to explore a sequencing pool without a given samplesheet and give hints on what adapter types are present, which index lenghts are used and whether there are any UMIs within the index sequence. The Anglerfish explore command is still under heavy development but can be triggered by running, e.g. for help text: ```shell -anglerfish-explore --help +anglerfish explore --help ``` ## Credits diff --git a/setup.py b/setup.py index 723000b..8c43b7a 100644 --- a/setup.py +++ b/setup.py @@ -39,7 +39,6 @@ entry_points={ "console_scripts": [ "anglerfish=anglerfish.cli:app", - "anglerfish-explore=anglerfish.explore.cli:main", ], }, zip_safe=False, From 311c5ed2585e95a5ad7a0037bf8881562a874b67 Mon Sep 17 00:00:00 2001 From: Remi-Andre Olsen Date: Tue, 16 Apr 2024 13:57:17 +0200 Subject: [PATCH 8/8] Remove click requirement --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 723e451..3262cac 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,4 @@ biopython==1.83 -click==8.1.7 levenshtein==0.23.0 numpy==1.26.3 pandas==2.1.4