Repository: AroneyS/binchicken (language: python)

Issue:
update kingfisher to v0.3.0
adds gzip -t test after downloading


Discussion / hints:

Fix patch:
diff --git a/ibis/__init__.py b/ibis/__init__.py
index a2fecb45..c5981731 100644
--- a/ibis/__init__.py
+++ b/ibis/__init__.py
@@ -1,1 +1,1 @@
-__version__ = "0.9.2"
+__version__ = "0.9.3"
diff --git a/ibis/workflow/coassemble.smk b/ibis/workflow/coassemble.smk
index 3691d966..19375d42 100644
--- a/ibis/workflow/coassemble.smk
+++ b/ibis/workflow/coassemble.smk
@@ -330,10 +330,13 @@ rule target_elusive:
         min_coassembly_coverage = config["min_coassembly_coverage"],
         max_coassembly_samples = config["max_coassembly_samples"],
         taxa_of_interest = config["taxa_of_interest"],
+        samples = config["reads_1"],
     threads: 32
     resources:
         mem_mb=250*1000,
         runtime = "24h",
+    log:
+        logs_dir + "/target/target_elusive.log"
     script:
         "scripts/target_elusive.py"
 
@@ -353,6 +356,8 @@ checkpoint cluster_graph:
     resources:
         mem_mb=500*1000,
         runtime = "168h",
+    log:
+        logs_dir + "/target/cluster_graph.log"
     script:
         "scripts/cluster_graph.py"
 
@@ -402,10 +407,10 @@ rule sra_qc:
     output:
         out1 = output_dir + "/sra_qc/{sra}_1.fastq.gz",
         out2 = output_dir + "/sra_qc/{sra}_2.fastq.gz",
+        stats = output_dir + "/sra_qc/{sra}_qc.stats",
     params:
         in1 = output_dir + "/sra/{sra}_1.fastq.gz",
         in2 = output_dir + "/sra/{sra}_2.fastq.gz",
-        stats = output_dir + "/sra_qc/{sra}_qc.stats",
     threads: 16
     resources:
         mem_mb=125*1000,
@@ -422,7 +427,7 @@ rule sra_qc:
         "ktrim=r k=23 mink=11 hdist=1 "
         "qtrim=r trimq=10 "
         "minlen=30 "
-        "stats={params.stats} statscolumns=3 "
+        "stats={output.stats} statscolumns=3 "
         "t={threads} "
         "&> {log} "
 
diff --git a/ibis/workflow/env/kingfisher.yml b/ibis/workflow/env/kingfisher.yml
index e7356dc9..bb534d71 100644
--- a/ibis/workflow/env/kingfisher.yml
+++ b/ibis/workflow/env/kingfisher.yml
@@ -3,5 +3,5 @@ channels:
   - bioconda
   - rpetit3
 dependencies:
-  - kingfisher=0.2.*
+  - kingfisher=0.3.*
   - aspera-connect
diff --git a/ibis/workflow/scripts/cluster_graph.py b/ibis/workflow/scripts/cluster_graph.py
index f463f5c3..65feaa35 100644
--- a/ibis/workflow/scripts/cluster_graph.py
+++ b/ibis/workflow/scripts/cluster_graph.py
@@ -116,11 +116,6 @@ def pipeline(
             .with_columns(
                 pl.col("samples")
                     .str.split(",")
-                    .list.eval(
-                        pl.when(pl.all_horizontal(pl.element().is_in(read_size.get_column("sample"))))
-                        .then(pl.element())
-                        .otherwise(pl.element().str.replace(r"(_|\.)1$", ""))
-                        )
                     .cast(pl.List(pl.Categorical)),
                 pl.col("target_ids")
                     .str.split(",")
@@ -285,6 +280,7 @@ def pipeline(
     import polars as pl
 
     logging.basicConfig(
+        filename=snakemake.log[0],
         level=logging.INFO,
         format='%(asctime)s %(levelname)s: %(message)s',
         datefmt='%Y/%m/%d %I:%M:%S %p'
diff --git a/ibis/workflow/scripts/collect_reference_bins.py b/ibis/workflow/scripts/collect_reference_bins.py
index 3088a8bd..d5ef55dc 100755
--- a/ibis/workflow/scripts/collect_reference_bins.py
+++ b/ibis/workflow/scripts/collect_reference_bins.py
@@ -98,6 +98,11 @@ def pipeline(appraise_binned, appraise_unbinned, sample, MIN_APPRAISED=0.1, TRIM
         cmd = f"touch {output_path}"
         extern.run(cmd)
     else:
-        for bin in reference_bins:
-            cmd = f"cat {genomes[bin]} >> {output_path}"
-            extern.run(cmd)
\ No newline at end of file
+        with open(str(output_path), "w") as outfile:
+            for bin in reference_bins:
+                bin_name = os.path.splitext(os.path.basename(genomes[bin]))[0]
+                with open(genomes[bin], "r") as infile:
+                    for line in infile:
+                        if line.startswith(">"):
+                            line = f">{bin_name}~{line[1:]}"
+                        outfile.write(line)
diff --git a/ibis/workflow/scripts/target_elusive.py b/ibis/workflow/scripts/target_elusive.py
index 405d8555..05ea3629 100755
--- a/ibis/workflow/scripts/target_elusive.py
+++ b/ibis/workflow/scripts/target_elusive.py
@@ -16,6 +16,7 @@
 
 def pipeline(
     unbinned,
+    samples,
     MIN_COASSEMBLY_COVERAGE=10,
     TAXA_OF_INTEREST="",
     MAX_COASSEMBLY_SAMPLES=2):
@@ -40,6 +41,12 @@ def pipeline(
     logging.info("Grouping hits by marker gene sequences to form targets")
     unbinned = (
         unbinned
+        .with_columns(
+            pl.when(pl.col("sample").is_in(samples))
+            .then(pl.col("sample"))
+            .otherwise(pl.col("sample").str.replace(r"(_|\.)1$", ""))
+            )
+        .filter(pl.col("sample").is_in(samples))
         .drop("found_in")
         .with_row_count("target")
         .select(
@@ -117,6 +124,7 @@ def process_groups(df):
     import polars as pl
 
     logging.basicConfig(
+        filename=snakemake.log[0],
         level=logging.INFO,
         format='%(asctime)s %(levelname)s: %(message)s',
         datefmt='%Y/%m/%d %I:%M:%S %p'
@@ -128,11 +136,13 @@ def process_groups(df):
     unbinned_path = snakemake.input.unbinned
     targets_path = snakemake.output.output_targets
     edges_path = snakemake.output.output_edges
+    samples = set(snakemake.params.samples)
 
     unbinned = pl.read_csv(unbinned_path, separator="\t")
 
     targets, edges = pipeline(
         unbinned,
+        samples,
         MIN_COASSEMBLY_COVERAGE=MIN_COASSEMBLY_COVERAGE,
         TAXA_OF_INTEREST=TAXA_OF_INTEREST,
         MAX_COASSEMBLY_SAMPLES=MAX_COASSEMBLY_SAMPLES,


Test patch:
diff --git a/test/data/GB_GCA_013286235.3.fna b/test/data/GB_GCA_013286235.3.fna
new file mode 100644
index 00000000..27f2c6ca
--- /dev/null
+++ b/test/data/GB_GCA_013286235.3.fna
@@ -0,0 +1,4 @@
+>JABDGE010000038.1_13
+ATGAGAAAGTCGAAACCAAAAAAGAGAATTATCCTTCCTGATCCAAAGTTCAACGACACTTTGGTAACAA
+>JABDGE010000038.1_14
+ATGCCTACTATTCAGCAATTAGTTAGAAAAGGTAGAGTAGCTCTGGTTGACAAGAGTAAGTCGCCAGCGT
diff --git a/test/data/mock_coassemble/appraise/binned_three.otu_table.tsv b/test/data/mock_coassemble/appraise/binned_three.otu_table.tsv
new file mode 100644
index 00000000..14e235b4
--- /dev/null
+++ b/test/data/mock_coassemble/appraise/binned_three.otu_table.tsv
@@ -0,0 +1,7 @@
+gene	sample	sequence	num_hits	coverage	taxonomy	found_in
+S3.7.ribosomal_protein_S7	sample_1.1	TTCCAGGTGCCTACTGAAGTTCGTCCCGAGCGTAAAATTGCATTGGGTATGAAATGGCTG	2	3.28	Root; d__Bacteria; p__Bacteroidota; c__Bacteroidia; o__Sphingobacteriales; f__Sphingobacteriaceae; g__Mucilaginibacter; s__Mucilaginibacter sp013286235	GB_GCA_013286235.1_protein
+S3.7.ribosomal_protein_S7	sample_2.1	TTCCAGGTGCCTACTGAAGTTCGTCCCGAGCGTAAAATTGCATTGGGTATGAAATGGCTG	1	1.64	Root; d__Bacteria; p__Bacteroidota; c__Bacteroidia; o__Sphingobacteriales; f__Sphingobacteriaceae; g__Mucilaginibacter; s__Mucilaginibacter sp013286235	GB_GCA_013286235.1_protein
+S3.7.ribosomal_protein_S7	sample_3.1	TTCCAGGTGCCTACTGAAGTTCGTCCCGAGCGTAAAATTGCATTGGGTATGAAATGGCTG	1	1.64	Root; d__Bacteria; p__Bacteroidota; c__Bacteroidia; o__Sphingobacteriales; f__Sphingobacteriaceae; g__Mucilaginibacter; s__Mucilaginibacter sp013286235	GB_GCA_013286235.1_protein
+S3.7.ribosomal_protein_S7	sample_1.1	TTCCAGGTGCCTACTGAAGTTCGTCCCGAGCGTAAAATTGCATTGGGTATGAAATGGCTG	2	3.28	Root; d__Bacteria; p__Bacteroidota; c__Bacteroidia; o__Sphingobacteriales; f__Sphingobacteriaceae; g__Mucilaginibacter; s__Mucilaginibacter sp013286235	GB_GCA_013286235.3_protein
+S3.7.ribosomal_protein_S7	sample_2.1	TTCCAGGTGCCTACTGAAGTTCGTCCCGAGCGTAAAATTGCATTGGGTATGAAATGGCTG	1	1.64	Root; d__Bacteria; p__Bacteroidota; c__Bacteroidia; o__Sphingobacteriales; f__Sphingobacteriaceae; g__Mucilaginibacter; s__Mucilaginibacter sp013286235	GB_GCA_013286235.3_protein
+S3.7.ribosomal_protein_S7	sample_3.1	TTCCAGGTGCCTACTGAAGTTCGTCCCGAGCGTAAAATTGCATTGGGTATGAAATGGCTG	1	1.64	Root; d__Bacteria; p__Bacteroidota; c__Bacteroidia; o__Sphingobacteriales; f__Sphingobacteriaceae; g__Mucilaginibacter; s__Mucilaginibacter sp013286235	GB_GCA_013286235.3_protein
diff --git a/test/data/mock_coassemble/appraise/unbinned_biased.otu_table.tsv b/test/data/mock_coassemble/appraise/unbinned_biased.otu_table.tsv
new file mode 100644
index 00000000..9b2a60cb
--- /dev/null
+++ b/test/data/mock_coassemble/appraise/unbinned_biased.otu_table.tsv
@@ -0,0 +1,7 @@
+gene	sample	sequence	num_hits	coverage	taxonomy	found_in
+S3.7.ribosomal_protein_S7	sample_1.1	AAAAAAAAAAAACAAGAAGTTAGAGGAGAAAGAAGAATCTCGTTAGCTAAAAAAAAAAAA	3	4.92	Root; d__Bacteria; p__Proteobacteria; c__Gammaproteobacteria; o__Burkholderiales; f__Burkholderiaceae; g__Polynucleobacter; s__Polynucleobacter sp018688335	
+S3.7.ribosomal_protein_S7	sample_1.1	TACCAGGTCCCGGTCGAGGTCCGTCCGATCCGCCAGACGACGCTCGCCCTGCGCTGGCTC	5	8.21	Root; d__Bacteria; p__Actinobacteriota; c__Actinomycetia; o__Mycobacteriales; f__Mycobacteriaceae; g__Nocardia; s__Nocardia grenadensis	
+S3.7.ribosomal_protein_S7	sample_1.1	TATCAGGTGCCTATTGAGGTAAGACCTGAAAGAAGACAGACTTTAGCGCTTCGCTGGATA	1	1.64	Root; d__Bacteria; p__Actinobacteriota; c__Actinomycetia; o__Mycobacteriales; f__Mycobacteriaceae; g__Nocardia; s__Nocardia grenadensis	
+S3.7.ribosomal_protein_S7	sample_2.1	AAAAAAAAAAAACAAGAAGTTAGAGGAGAAAGAAGAATCTCGTTAGCTAAAAAAAAAAAA	4	6.57	Root; d__Bacteria; p__Proteobacteria; c__Gammaproteobacteria; o__Burkholderiales; f__Burkholderiaceae; g__Polynucleobacter; s__Polynucleobacter sp018688335	
+S3.7.ribosomal_protein_S7	sample_2.1	TACCAGGTCCCGGTCGAGGTCCGTCCGATCCGCCAGACGACGCTCGCCCTGCGCTGGCTC	3	4.92	Root; d__Bacteria; p__Actinobacteriota; c__Actinomycetia; o__Mycobacteriales; f__Mycobacteriaceae; g__Nocardia; s__Nocardia grenadensis	
+S3.7.ribosomal_protein_S7	sample_3.1	TATCAGGTGCCTATTGAGGTAAGACCTGAAAGAAGACAGACTTTAGCGCTTCGCTGGATA	6	9.85	Root; d__Bacteria; p__Actinobacteriota; c__Actinomycetia; o__Mycobacteriales; f__Mycobacteriaceae; g__Nocardia; s__Nocardia grenadensis	
diff --git a/test/test_cluster_graph.py b/test/test_cluster_graph.py
index 14a3d2d5..d9e4bea5 100644
--- a/test/test_cluster_graph.py
+++ b/test/test_cluster_graph.py
@@ -54,25 +54,8 @@ def assertSeriesEqual(self, a, b):
 
     def test_cluster_graph(self):
         elusive_edges = pl.DataFrame([
-            ["match", 2, "sample_2.1,sample_1.1", "0,1,2"],
-            ["match", 2, "sample_1.1,sample_3.1", "1,2"],
-        ], schema=ELUSIVE_EDGES_COLUMNS)
-        read_size = pl.DataFrame([
-            ["sample_1", 1000],
-            ["sample_2", 2000],
-            ["sample_3", 1000],
-        ], schema=READ_SIZE_COLUMNS)
-
-        expected = pl.DataFrame([
-            ["sample_1,sample_2", 2, 3, 3000, "sample_1,sample_2,sample_3", "coassembly_0"],
-        ], schema=ELUSIVE_CLUSTERS_COLUMNS)
-        observed = pipeline(elusive_edges, read_size)
-        self.assertDataFrameEqual(expected, observed)
-
-    def test_cluster_graph_underscore(self):
-        elusive_edges = pl.DataFrame([
-            ["match", 2, "sample_2_1,sample_1_1", "0,1,2"],
-            ["match", 2, "sample_1_1,sample_3_1", "1,2"],
+            ["match", 2, "sample_2,sample_1", "0,1,2"],
+            ["match", 2, "sample_1,sample_3", "1,2"],
         ], schema=ELUSIVE_EDGES_COLUMNS)
         read_size = pl.DataFrame([
             ["sample_1", 1000],
diff --git a/test/test_iterate.py b/test/test_iterate.py
index de075aee..8f11adc8 100644
--- a/test/test_iterate.py
+++ b/test/test_iterate.py
@@ -21,6 +21,15 @@
     os.path.join(path_to_data, "sample_2.2.fq"),
     os.path.join(path_to_data, "sample_3.2.fq"),
 ])
+SAMPLE_READS_FORWARD_NO_TWO = " ".join([
+    os.path.join(path_to_data, "sample_1.1.fq"),
+    os.path.join(path_to_data, "sample_3.1.fq"),
+])
+SAMPLE_READS_REVERSE_NO_TWO = " ".join([
+    os.path.join(path_to_data, "sample_1.2.fq"),
+    os.path.join(path_to_data, "sample_3.2.fq"),
+])
+
 GENOMES = " ".join([os.path.join(path_to_data, "GB_GCA_013286235.1.fna")])
 TWO_GENOMES = " ".join([
     os.path.join(path_to_data, "GB_GCA_013286235.1.fna"),
@@ -30,6 +39,7 @@
 
 MOCK_COASSEMBLE = os.path.join(path_to_data, "mock_coassemble")
 MOCK_UNBINNED = os.path.join(MOCK_COASSEMBLE, "appraise", "unbinned.otu_table.tsv")
+MOCK_UNBINNED_BIASED = os.path.join(MOCK_COASSEMBLE, "appraise", "unbinned_biased.otu_table.tsv")
 MOCK_BINNED = os.path.join(MOCK_COASSEMBLE, "appraise", "binned.otu_table.tsv")
 MOCK_COASSEMBLIES = ' '.join([os.path.join(MOCK_COASSEMBLE, "coassemble", "coassembly_0")])
 MOCK_GENOMES = " ".join([
@@ -414,6 +424,41 @@ def test_iterate_genome_singlem_and_new(self):
                 self.assertTrue("TATCAAGTTCCACAAGAAGTTAGAGGAGAAAGAAGAATCTCGTTAGCTATTAGATGGATT" in file)
                 self.assertTrue("bin_1_protein" in file)
 
+    def test_iterate_missing_samples(self):
+        with in_tempdir():
+            cmd = (
+                f"ibis iterate "
+                f"--forward {SAMPLE_READS_FORWARD_NO_TWO} "
+                f"--reverse {SAMPLE_READS_REVERSE_NO_TWO} "
+                f"--aviary-outputs {MOCK_COASSEMBLIES} "
+                f"--coassemble-unbinned {MOCK_UNBINNED_BIASED} "
+                f"--coassemble-binned {MOCK_BINNED} "
+                f"--genomes {GENOMES} "
+                f"--sample-read-size {SAMPLE_READ_SIZE} "
+                f"--singlem-metapackage {METAPACKAGE} "
+                f"--output test "
+                f"--conda-prefix {path_to_conda} "
+            )
+            extern.run(cmd)
+
+            bin_provenance_path = os.path.join("test", "recovered_bins", "bin_provenance.tsv")
+            self.assertTrue(os.path.exists(bin_provenance_path))
+
+            config_path = os.path.join("test", "config.yaml")
+            self.assertTrue(os.path.exists(config_path))
+
+            elusive_clusters_path = os.path.join("test", "coassemble", "target", "elusive_clusters.tsv")
+            self.assertTrue(os.path.exists(elusive_clusters_path))
+            expected = "\n".join(
+                [
+                    "\t".join(["samples", "length", "total_targets", "total_size", "recover_samples", "coassembly"]),
+                    "\t".join(["sample_1,sample_3", "2", "1", "2869", "sample_1,sample_3", "coassembly_0"]),
+                    ""
+                ]
+            )
+            with open(elusive_clusters_path) as f:
+                self.assertEqual(expected, f.read())
+
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/test/test_target_elusive.py b/test/test_target_elusive.py
index 1cb11a67..1c6aa403 100644
--- a/test/test_target_elusive.py
+++ b/test/test_target_elusive.py
@@ -42,6 +42,7 @@ def test_target_elusive(self):
             ["S3.1", "sample_1", "AAA", 5, 10, "Root", ""],
             ["S3.1", "sample_2", "AAA", 5, 10, "Root", ""],
         ], schema=APPRAISE_COLUMNS)
+        samples = set(["sample_1", "sample_2"])
 
         expected_targets = pl.DataFrame([
             ["S3.1", "sample_1", "AAA", 5, 10, "Root", "0"],
@@ -51,20 +52,59 @@ def test_target_elusive(self):
             ["match", 2, "sample_1,sample_2", "0"],
         ], schema=EDGES_COLUMNS)
 
-        observed_targets, observed_edges = pipeline(unbinned)
+        observed_targets, observed_edges = pipeline(unbinned, samples)
+        self.assertDataFrameEqual(expected_targets, observed_targets)
+        self.assertDataFrameEqual(expected_edges, observed_edges)
+
+    def test_target_elusive_suffix(self):
+        unbinned = pl.DataFrame([
+            ["S3.1", "sample_1.1", "AAA", 5, 10, "Root", ""],
+            ["S3.1", "sample_2.1", "AAA", 5, 10, "Root", ""],
+        ], schema=APPRAISE_COLUMNS)
+        samples = set(["sample_1", "sample_2"])
+
+        expected_targets = pl.DataFrame([
+            ["S3.1", "sample_1", "AAA", 5, 10, "Root", "0"],
+            ["S3.1", "sample_2", "AAA", 5, 10, "Root", "0"],
+        ], schema=TARGETS_COLUMNS)
+        expected_edges = pl.DataFrame([
+            ["match", 2, "sample_1,sample_2", "0"],
+        ], schema=EDGES_COLUMNS)
+
+        observed_targets, observed_edges = pipeline(unbinned, samples)
+        self.assertDataFrameEqual(expected_targets, observed_targets)
+        self.assertDataFrameEqual(expected_edges, observed_edges)
+
+    def test_target_elusive_suffix(self):
+        unbinned = pl.DataFrame([
+            ["S3.1", "sample_1_1", "AAA", 5, 10, "Root", ""],
+            ["S3.1", "sample_2_1", "AAA", 5, 10, "Root", ""],
+        ], schema=APPRAISE_COLUMNS)
+        samples = set(["sample_1", "sample_2"])
+
+        expected_targets = pl.DataFrame([
+            ["S3.1", "sample_1", "AAA", 5, 10, "Root", "0"],
+            ["S3.1", "sample_2", "AAA", 5, 10, "Root", "0"],
+        ], schema=TARGETS_COLUMNS)
+        expected_edges = pl.DataFrame([
+            ["match", 2, "sample_1,sample_2", "0"],
+        ], schema=EDGES_COLUMNS)
+
+        observed_targets, observed_edges = pipeline(unbinned, samples)
         self.assertDataFrameEqual(expected_targets, observed_targets)
         self.assertDataFrameEqual(expected_edges, observed_edges)
 
     def test_target_elusive_empty_input(self):
         unbinned = pl.DataFrame([
         ], schema=APPRAISE_COLUMNS)
+        samples = set(["sample_1", "sample_2"])
 
         expected_targets = pl.DataFrame([
         ], schema=TARGETS_COLUMNS)
         expected_edges = pl.DataFrame([
         ], schema=EDGES_COLUMNS)
 
-        observed_targets, observed_edges = pipeline(unbinned)
+        observed_targets, observed_edges = pipeline(unbinned, samples)
         self.assertDataFrameEqual(expected_targets, observed_targets)
         self.assertDataFrameEqual(expected_edges, observed_edges)
 
@@ -73,6 +113,7 @@ def test_target_elusive_low_coverage(self):
             ["S3.1", "sample_1", "AAA", 5, 5, "Root", ""],
             ["S3.1", "sample_2", "AAA", 5, 4, "Root", ""],
         ], schema=APPRAISE_COLUMNS)
+        samples = set(["sample_1", "sample_2"])
 
         expected_targets = pl.DataFrame([
             ["S3.1", "sample_1", "AAA", 5, 5, "Root", "0"],
@@ -81,7 +122,7 @@ def test_target_elusive_low_coverage(self):
         expected_edges = pl.DataFrame([
         ], schema=EDGES_COLUMNS)
 
-        observed_targets, observed_edges = pipeline(unbinned)
+        observed_targets, observed_edges = pipeline(unbinned, samples)
         self.assertDataFrameEqual(expected_targets, observed_targets)
         self.assertDataFrameEqual(expected_edges, observed_edges)
 
@@ -92,6 +133,7 @@ def test_target_elusive_two_targets(self):
             ["S3.1", "sample_2", "AAA", 5, 10, "Root", ""],
             ["S3.1", "sample_2", "AAB", 5, 10, "Root", ""],
         ], schema=APPRAISE_COLUMNS)
+        samples = set(["sample_1", "sample_2"])
 
         expected_targets = pl.DataFrame([
             ["S3.1", "sample_1", "AAA", 5, 10, "Root", "0"],
@@ -103,7 +145,7 @@ def test_target_elusive_two_targets(self):
             ["match", 2, "sample_1,sample_2", "0,1"],
         ], schema=EDGES_COLUMNS)
 
-        observed_targets, observed_edges = pipeline(unbinned)
+        observed_targets, observed_edges = pipeline(unbinned, samples)
         self.assertDataFrameEqual(expected_targets, observed_targets)
         self.assertDataFrameEqual(expected_edges, observed_edges)
 
@@ -114,6 +156,7 @@ def test_target_elusive_multiple_genes(self):
             ["S3.1", "sample_2", "AAA", 5, 10, "Root", ""],
             ["S3.2", "sample_2", "AAB", 5, 10, "Root", ""],
         ], schema=APPRAISE_COLUMNS)
+        samples = set(["sample_1", "sample_2"])
 
         expected_targets = pl.DataFrame([
             ["S3.1", "sample_1", "AAA", 5, 10, "Root", "0"],
@@ -125,7 +168,7 @@ def test_target_elusive_multiple_genes(self):
             ["match", 2, "sample_1,sample_2", "0,1"],
         ], schema=EDGES_COLUMNS)
 
-        observed_targets, observed_edges = pipeline(unbinned)
+        observed_targets, observed_edges = pipeline(unbinned, samples)
         self.assertDataFrameEqual(expected_targets, observed_targets)
         self.assertDataFrameEqual(expected_edges, observed_edges)
 
@@ -136,6 +179,7 @@ def test_target_elusive_multiple_genes_same_sequence(self):
             ["S3.1", "sample_2", "AAA", 5, 10, "Root", ""],
             ["S3.2", "sample_2", "AAA", 5, 10, "Root", ""],
         ], schema=APPRAISE_COLUMNS)
+        samples = set(["sample_1", "sample_2"])
 
         expected_targets = pl.DataFrame([
             ["S3.1", "sample_1", "AAA", 5, 10, "Root", "0"],
@@ -147,7 +191,7 @@ def test_target_elusive_multiple_genes_same_sequence(self):
             ["match", 2, "sample_1,sample_2", "0,1"],
         ], schema=EDGES_COLUMNS)
 
-        observed_targets, observed_edges = pipeline(unbinned)
+        observed_targets, observed_edges = pipeline(unbinned, samples)
         self.assertDataFrameEqual(expected_targets, observed_targets)
         self.assertDataFrameEqual(expected_edges, observed_edges)
 
@@ -160,6 +204,7 @@ def test_target_elusive_three_sample_mix(self):
             ["S3.1", "sample_3", "AAA", 5, 10, "Root", ""],
             ["S3.1", "sample_3", "AAC", 5, 10, "Root", ""],
         ], schema=APPRAISE_COLUMNS)
+        samples = set(["sample_1", "sample_2", "sample_3"])
 
         expected_targets = pl.DataFrame([
             ["S3.1", "sample_1", "AAA", 5, 10, "Root", "0"],
@@ -175,7 +220,32 @@ def test_target_elusive_three_sample_mix(self):
             ["match", 2, "sample_2,sample_3", "0"],
         ], schema=EDGES_COLUMNS)
 
-        observed_targets, observed_edges = pipeline(unbinned)
+        observed_targets, observed_edges = pipeline(unbinned, samples)
+        self.assertDataFrameEqual(expected_targets, observed_targets)
+        self.assertDataFrameEqual(expected_edges, observed_edges)
+
+    def test_target_elusive_skip_samples(self):
+        unbinned = pl.DataFrame([
+            ["S3.1", "sample_1", "AAA", 5, 10, "Root", ""],
+            ["S3.1", "sample_1", "AAB", 5, 10, "Root", ""],
+            ["S3.1", "sample_2", "AAA", 5, 10, "Root", ""],
+            ["S3.1", "sample_2", "AAB", 5, 10, "Root", ""],
+            ["S3.1", "sample_3", "AAA", 5, 10, "Root", ""],
+            ["S3.1", "sample_3", "AAC", 5, 10, "Root", ""],
+        ], schema=APPRAISE_COLUMNS)
+        samples = set(["sample_1", "sample_2"])
+
+        expected_targets = pl.DataFrame([
+            ["S3.1", "sample_1", "AAA", 5, 10, "Root", "0"],
+            ["S3.1", "sample_1", "AAB", 5, 10, "Root", "1"],
+            ["S3.1", "sample_2", "AAA", 5, 10, "Root", "0"],
+            ["S3.1", "sample_2", "AAB", 5, 10, "Root", "1"],
+        ], schema=TARGETS_COLUMNS)
+        expected_edges = pl.DataFrame([
+            ["match", 2, "sample_1,sample_2", "0,1"],
+        ], schema=EDGES_COLUMNS)
+
+        observed_targets, observed_edges = pipeline(unbinned, samples)
         self.assertDataFrameEqual(expected_targets, observed_targets)
         self.assertDataFrameEqual(expected_edges, observed_edges)
 
@@ -188,6 +258,7 @@ def test_target_elusive_three_way_targets(self):
             ["S3.1", "sample_3", "AAA", 2, 4, "Root", ""],
             ["S3.1", "sample_3", "AAB", 2, 4, "Root", ""],
         ], schema=APPRAISE_COLUMNS)
+        samples = set(["sample_1", "sample_2", "sample_3"])
 
         expected_targets = pl.DataFrame([
             ["S3.1", "sample_1", "AAA", 3, 6, "Root", "0"],
@@ -202,7 +273,7 @@ def test_target_elusive_three_way_targets(self):
             ["pool", 3, "sample_1,sample_2,sample_3", "0,1"],
         ], schema=EDGES_COLUMNS)
 
-        observed_targets, observed_edges = pipeline(unbinned, MAX_COASSEMBLY_SAMPLES=3)
+        observed_targets, observed_edges = pipeline(unbinned, samples, MAX_COASSEMBLY_SAMPLES=3)
         self.assertDataFrameEqual(expected_targets, observed_targets)
         self.assertDataFrameEqual(expected_edges, observed_edges)
 
@@ -219,6 +290,7 @@ def test_target_elusive_four_way_targets(self):
             ["S3.1", "sample_4", "AAB", 2, 4, "Root", ""],
             ["S3.1", "sample_4", "AAC", 1, 3, "Root", ""],
         ], schema=APPRAISE_COLUMNS)
+        samples = set(["sample_1", "sample_2", "sample_3", "sample_4"])
 
         expected_targets = pl.DataFrame([
             ["S3.1", "sample_1", "AAA", 2, 4, "Root", "0"],
@@ -238,7 +310,7 @@ def test_target_elusive_four_way_targets(self):
             ["pool", 4, "sample_1,sample_2,sample_3,sample_4", "1"],
         ], schema=EDGES_COLUMNS)
 
-        observed_targets, observed_edges = pipeline(unbinned, MAX_COASSEMBLY_SAMPLES=4)
+        observed_targets, observed_edges = pipeline(unbinned, samples, MAX_COASSEMBLY_SAMPLES=4)
         self.assertDataFrameEqual(expected_targets, observed_targets)
         self.assertDataFrameEqual(expected_edges, observed_edges)
 
@@ -250,12 +322,13 @@ def test_target_elusive_benchmarking(self):
         unbinned = pl.DataFrame([
             [g, s, n, 2, 5.1, "Root", ""] for g in genes for s in samples for n in sequences
         ], schema=APPRAISE_COLUMNS)
+        samples = set(samples)
 
-        _, _ = pipeline(unbinned, MAX_COASSEMBLY_SAMPLES=4)
-        _, _ = pipeline(unbinned, MAX_COASSEMBLY_SAMPLES=4)
-        _, _ = pipeline(unbinned, MAX_COASSEMBLY_SAMPLES=4)
-        _, _ = pipeline(unbinned, MAX_COASSEMBLY_SAMPLES=4)
-        _, _ = pipeline(unbinned, MAX_COASSEMBLY_SAMPLES=4)
+        _, _ = pipeline(unbinned, samples, MAX_COASSEMBLY_SAMPLES=4)
+        _, _ = pipeline(unbinned, samples, MAX_COASSEMBLY_SAMPLES=4)
+        _, _ = pipeline(unbinned, samples, MAX_COASSEMBLY_SAMPLES=4)
+        _, _ = pipeline(unbinned, samples, MAX_COASSEMBLY_SAMPLES=4)
+        _, _ = pipeline(unbinned, samples, MAX_COASSEMBLY_SAMPLES=4)
 
         # Cross join, recursive time: 247.833s
         # Pooling time: 1.979s
@@ -267,6 +340,7 @@ def test_target_elusive_target_taxa(self):
             ["S3.1", "sample_2", "AAA", 5, 10, "Root; d__Bacteria; p__Planctomycetota", ""],
             ["S3.1", "sample_2", "AAB", 5, 10, "Root", ""],
         ], schema=APPRAISE_COLUMNS)
+        samples = set(["sample_1", "sample_2"])
 
         expected_targets = pl.DataFrame([
             ["S3.1", "sample_1", "AAA", 5, 10, "Root; d__Bacteria; p__Planctomycetota", "0"],
@@ -276,7 +350,7 @@ def test_target_elusive_target_taxa(self):
             ["match", 2, "sample_1,sample_2", "0"],
         ], schema=EDGES_COLUMNS)
 
-        observed_targets, observed_edges = pipeline(unbinned, TAXA_OF_INTEREST="p__Planctomycetota")
+        observed_targets, observed_edges = pipeline(unbinned, samples, TAXA_OF_INTEREST="p__Planctomycetota")
         self.assertDataFrameEqual(expected_targets, observed_targets)
         self.assertDataFrameEqual(expected_edges, observed_edges)
 
@@ -289,6 +363,7 @@ def test_target_elusive_single_assembly(self):
             ["S3.1", "sample_3", "AAA", 5, 10, "Root", ""],
             ["S3.1", "sample_3", "AAC", 5, 10, "Root", ""],
         ], schema=APPRAISE_COLUMNS)
+        samples = set(["sample_1", "sample_2", "sample_3"])
 
         expected_targets = pl.DataFrame([
             ["S3.1", "sample_1", "AAA", 5, 10, "Root", "0"],
@@ -304,7 +379,7 @@ def test_target_elusive_single_assembly(self):
             ["match", 2, "sample_2,sample_3", "0"],
         ], schema=EDGES_COLUMNS)
 
-        observed_targets, observed_edges = pipeline(unbinned, MAX_COASSEMBLY_SAMPLES=1)
+        observed_targets, observed_edges = pipeline(unbinned, samples, MAX_COASSEMBLY_SAMPLES=1)
         self.assertDataFrameEqual(expected_targets, observed_targets)
         self.assertDataFrameEqual(expected_edges, observed_edges)
 
diff --git a/test/test_update.py b/test/test_update.py
index 8aed3dcf..6a4aec0f 100644
--- a/test/test_update.py
+++ b/test/test_update.py
@@ -25,10 +25,16 @@
     os.path.join(path_to_data, "GB_GCA_013286235.1.fna"),
     os.path.join(path_to_data, "GB_GCA_013286235.2.fna"),
     ])
+THREE_GENOMES = " ".join([
+    os.path.join(path_to_data, "GB_GCA_013286235.1.fna"),
+    os.path.join(path_to_data, "GB_GCA_013286235.2.fna"),
+    os.path.join(path_to_data, "GB_GCA_013286235.3.fna"),
+    ])
 
 MOCK_COASSEMBLE = os.path.join(path_to_data, "mock_coassemble")
 MOCK_UNBINNED = os.path.join(MOCK_COASSEMBLE, "appraise", "unbinned.otu_table.tsv")
 MOCK_BINNED = os.path.join(MOCK_COASSEMBLE, "appraise", "binned.otu_table.tsv")
+MOCK_BINNED_THREE = os.path.join(MOCK_COASSEMBLE, "appraise", "binned_three.otu_table.tsv")
 MOCK_TARGETS = os.path.join(MOCK_COASSEMBLE, "target", "targets.tsv")
 MOCK_ELUSIVE_EDGES = os.path.join(MOCK_COASSEMBLE, "target", "elusive_edges.tsv")
 MOCK_ELUSIVE_CLUSTERS = os.path.join(MOCK_COASSEMBLE, "target", "elusive_clusters.tsv")
@@ -490,6 +496,41 @@ def test_update_specific_coassembly(self):
             self.assertFalse(os.path.exists(os.path.join("test", "coassemble", "mapping", "sample_4_unmapped.1.fq.gz")))
             self.assertFalse(os.path.exists(os.path.join("test", "coassemble", "mapping", "sample_4_unmapped.2.fq.gz")))
 
+    def test_update_duplicate_contig_names(self):
+        with in_tempdir():
+            cmd = (
+                f"ibis update "
+                f"--assemble-unmapped "
+                f"--forward {SAMPLE_READS_FORWARD} "
+                f"--reverse {SAMPLE_READS_REVERSE} "
+                f"--genomes {THREE_GENOMES} "
+                f"--coassemble-unbinned {MOCK_UNBINNED} "
+                f"--coassemble-binned {MOCK_BINNED_THREE} "
+                f"--coassemble-targets {MOCK_TARGETS} "
+                f"--coassemble-elusive-edges {MOCK_ELUSIVE_EDGES} "
+                f"--coassemble-elusive-clusters {MOCK_ELUSIVE_CLUSTERS} "
+                f"--coassemble-summary {MOCK_SUMMARY} "
+                f"--output test "
+                f"--conda-prefix {path_to_conda} "
+                f"--snakemake-args \" --notemp\" "
+            )
+            extern.run(cmd)
+            # try:
+            #     import subprocess
+            #     subprocess.run(cmd, shell=True, check=True)
+            # except:
+            #     pass
+            # import pdb; pdb.set_trace()
+
+            bins_reference_path = os.path.join("test", "coassemble", "mapping", "sample_1_reference.fna")
+            self.assertTrue(os.path.exists(bins_reference_path))
+            with open(bins_reference_path) as f:
+                file = f.read()
+                self.assertTrue(">GB_GCA_013286235.1~JABDGE010000038.1_13" in file)
+                self.assertTrue(">GB_GCA_013286235.1~JABDGE010000038.1_14" in file)
+                self.assertTrue(">GB_GCA_013286235.3~JABDGE010000038.1_13" in file)
+                self.assertTrue(">GB_GCA_013286235.3~JABDGE010000038.1_14" in file)
+
 
 if __name__ == '__main__':
     unittest.main()