Repository: phac-nml/staramr (language: python)

Issue:
Error: multiple matches for gene=gyrA
Hi,

Getting following error when running staramr on one of my E coli assemblies: `staramr.exceptions.GenotypePhenotypeMatchException.GenotypePhenotypeMatchException: Error, multiple matches for gene=gyrA, codon_mutation=CodonMutationPosition(_database_amr_gene_start=1, _nucleotide_position_amr_gene=259, _codon_start=87, _database_amr_gene_codon=GAC, _input_genome_codon=AAC)`

See full error message below:

```
--- Logging error ---
Traceback (most recent call last):
  File "~/lib/python3.6/site-packages/staramr/blast/results/BlastResultsParser.py", line 67, in parse_results
    self._handle_blast_hit(file, database_name, blast_out, results, hit_seq_records)
  File "~/lib/python3.6/site-packages/staramr/blast/results/BlastResultsParser.py", line 109, in _handle_blast_hit
    blast_results = self._get_result_rows(hit, database_name)
  File "~/lib/python3.6/site-packages/staramr/blast/results/pointfinder/BlastResultsParserPointfinder.py", line 79, in _get_result_rows
    database_resistance_mutations = self._blast_database.get_resistance_codons(gene, database_mutations)
  File "~/lib/python3.6/site-packages/staramr/blast/pointfinder/PointfinderBlastDatabase.py", line 54, in get_resistance_codons
    return self._pointfinder_info.get_resistance_codons(gene, codon_mutations)
  File "~/lib/python3.6/site-packages/staramr/blast/pointfinder/PointfinderDatabaseInfo.py", line 107, in get_resistance_codons
    match = self._get_resistance_codon_match(gene, codon_mutation)
  File "~/lib/python3.6/site-packages/staramr/blast/pointfinder/PointfinderDatabaseInfo.py", line 76, in _get_resistance_codon_match
    raise GenotypePhenotypeMatchException("Error, multiple matches for gene=" + str(gene) + ", codon_mutation=" + str(codon_mutation))
staramr.exceptions.GenotypePhenotypeMatchException.GenotypePhenotypeMatchException: Error, multiple matches for gene=gyrA, codon_mutation=CodonMutationPosition(_database_amr_gene_start=1, _nucleotide_position_amr_gene=259, _codon_start=87, _database_amr_gene_codon=GAC, _input_genome_codon=AAC)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "~/lib/python3.6/logging/__init__.py", line 994, in emit
    msg = self.format(record)
  File "~/lib/python3.6/logging/__init__.py", line 840, in format
    return fmt.format(record)
  File "~/lib/python3.6/logging/__init__.py", line 577, in format
    record.message = record.getMessage()
  File "~/lib/python3.6/logging/__init__.py", line 338, in getMessage
    msg = msg % self.args
TypeError: not all arguments converted during string formatting
Call stack:
  File "~/bin/staramr", line 68, in <module>
    args.run_command(args)
  File "~/lib/python3.6/site-packages/staramr/subcommand/Search.py", line 488, in run
    unacceptable_num_contigs= args.unacceptable_num_contigs)
  File "~/lib/python3.6/site-packages/staramr/subcommand/Search.py", line 290, in _generate_results
    report_all_blast, ignore_invalid_files, mlst_scheme)
  File "~/lib/python3.6/site-packages/staramr/detection/AMRDetection.py", line 195, in run_amr_detection
    plength_threshold_pointfinder, report_all)
  File "~/lib/python3.6/site-packages/staramr/detection/AMRDetectionResistance.py", line 56, in _create_pointfinder_dataframe
    return pointfinder_parser.parse_results()
  File "~/lib/python3.6/site-packages/staramr/blast/results/BlastResultsParser.py", line 69, in parse_results
    logger.error(f"Error with results from file [{file}]", e)
Message: 'Error with results from file [11008_assembly_assembly.fasta]'
Arguments: (GenotypePhenotypeMatchException('Error, multiple matches for gene=gyrA, codon_mutation=CodonMutationPosition(_database_amr_gene_start=1, _nucleotide_position_amr_gene=259, _codon_start=87, _database_amr_gene_codon=GAC, _input_genome_codon=AAC)',),)
2023-04-23 11:40:00 ERROR: Error, multiple matches for gene=gyrA, codon_mutation=CodonMutationPosition(_database_amr_gene_start=1, _nucleotide_position_amr_gene=259, _codon_start=87, _database_amr_gene_codon=GAC, _input_genome_codon=AAC)
Traceback (most recent call last):
 File "~/bin/staramr", line 68, in <module>
    args.run_command(args)
  File "~/lib/python3.6/site-packages/staramr/subcommand/Search.py", line 488, in run
    unacceptable_num_contigs= args.unacceptable_num_contigs)
  File "~/lib/python3.6/site-packages/staramr/subcommand/Search.py", line 290, in _generate_results
    report_all_blast, ignore_invalid_files, mlst_scheme)
  File "~/lib/python3.6/site-packages/staramr/detection/AMRDetection.py", line 195, in run_amr_detection
    plength_threshold_pointfinder, report_all)
  File "~/lib/python3.6/site-packages/staramr/detection/AMRDetectionResistance.py", line 56, in _create_pointfinder_dataframe
    return pointfinder_parser.parse_results()
  File "~/lib/python3.6/site-packages/staramr/blast/results/BlastResultsParser.py", line 70, in parse_results
    raise e
  File "~/lib/python3.6/site-packages/staramr/blast/results/BlastResultsParser.py", line 67, in parse_results
    self._handle_blast_hit(file, database_name, blast_out, results, hit_seq_records)
  File "~/lib/python3.6/site-packages/staramr/blast/results/BlastResultsParser.py", line 109, in _handle_blast_hit
    blast_results = self._get_result_rows(hit, database_name)
  File "~/lib/python3.6/site-packages/staramr/blast/results/pointfinder/BlastResultsParserPointfinder.py", line 79, in _get_result_rows
    database_resistance_mutations = self._blast_database.get_resistance_codons(gene, database_mutations)
  File "~/lib/python3.6/site-packages/staramr/blast/pointfinder/PointfinderBlastDatabase.py", line 54, in get_resistance_codons
    return self._pointfinder_info.get_resistance_codons(gene, codon_mutations)
  File "~/lib/python3.6/site-packages/staramr/blast/pointfinder/PointfinderDatabaseInfo.py", line 107, in get_resistance_codons
    match = self._get_resistance_codon_match(gene, codon_mutation)
  File "~/lib/python3.6/site-packages/staramr/blast/pointfinder/PointfinderDatabaseInfo.py", line 76, in _get_resistance_codon_match
    raise GenotypePhenotypeMatchException("Error, multiple matches for gene=" + str(gene) + ", codon_mutation=" + str(codon_mutation))
staramr.exceptions.GenotypePhenotypeMatchException.GenotypePhenotypeMatchException: Error, multiple matches for gene=gyrA, codon_mutation=CodonMutationPosition(_database_amr_gene_start=1, _nucleotide_position_amr_gene=259, _codon_start=87, _database_amr_gene_codon=GAC, _input_genome_codon=AAC)
```


Discussion / hints:
Thank you for reporting. We will try to find a fix for you.
Noting that this particular exception appears to exist in StarAMR version [0.8.0](https://github.com/phac-nml/staramr/blob/0.8.0/staramr/blast/pointfinder/PointfinderDatabaseInfo.py#L76), but not in [0.9.0](https://github.com/phac-nml/staramr/blob/0.9.0/staramr/blast/pointfinder/PointfinderDatabaseInfo.py) and onwards.

Fix patch:
diff --git a/staramr/blast/pointfinder/PointfinderDatabaseInfo.py b/staramr/blast/pointfinder/PointfinderDatabaseInfo.py
index 16208bf..fa31123 100644
--- a/staramr/blast/pointfinder/PointfinderDatabaseInfo.py
+++ b/staramr/blast/pointfinder/PointfinderDatabaseInfo.py
@@ -6,8 +6,6 @@
 from staramr.blast.results.pointfinder.codon.CodonMutationPosition import CodonMutationPosition
 from staramr.blast.results.pointfinder.codon.CodonInsertionPosition import CodonInsertionPosition
 
-from staramr.exceptions.GenotypePhenotypeMatchException import GenotypePhenotypeMatchException
-
 """
 A Class storing information about the specific PointFinder database.
 """


Test patch:
diff --git a/staramr/tests/integration/data/gyrA-D87N.fsa b/staramr/tests/integration/data/gyrA-D87N.fsa
new file mode 100644
index 0000000..47b9e2c
--- /dev/null
+++ b/staramr/tests/integration/data/gyrA-D87N.fsa
@@ -0,0 +1,39 @@
+>gyrA_1_CP073768.1
+ATGAGCGACCTTGCGAGAGAAATTACACCGGTCAACATTGAGGAAGAGCTGAAGAGCTCCTATCTGGATT
+ATGCGATGTCGGTCATTGTTGGCCGTGCGCTGCCAGATGTCCGAGATGGCCTGAAGCCGGTACACCGTCG
+CGTACTTTACGCCATGAACGTACTAGGCAATGACTGGAACAAAGCCTATAAAAAATCTGCCCGTGTCGTT
+GGTGACGTAATCGGTAAATACCATCCCCATGGTGACTCGGCGGTCTATaacACGATCGTCCGCATGGCGC
+AGCCATTCTCGCTGCGTTATATGCTGGTAGACGGTCAGGGTAACTTCGGTTCTATCGACGGCGACTCTGC
+GGCGGCAATGCGTTATACGGAAATCCGTCTGGCGAAAATTGCCCATGAACTGATGGCCGATCTCGAAAAA
+GAGACGGTCGATTTCGTTGATAACTATGACGGCACGGAAAAAATTCCGGACGTCATGCCAACCAAAATTC
+CTAACCTGCTGGTGAACGGTTCTTCCGGTATCGCCGTAGGTATGGCAACCAACATCCCGCCGCACAACCT
+GACGGAAGTCATCAACGGTTGTCTGGCGTATATTGATGATGAAGACATCAGCATTGAAGGGCTGATGGAA
+CACATCCCGGGGCCGGACTTCCCGACGGCGGCAATCATTAACGGTCGTCGCGGTATTGAAGAAGCTTACC
+GTACCGGTCGCGGCAAGGTGTATATCCGCGCTCGCGCAGAAGTGGAAGTTGACGCCAAAACCGGTCGTGA
+AACCATTATCGTCCACGAAATTCCGTATCAGGTAAACAAAGCGCGCCTGATCGAGAAGATTGCGGAACTG
+GTAAAAGAAAAACGCGTGGAAGGCATCAGCGCGCTGCGTGACGAGTCTGACAAAGACGGTATGCGCATCG
+TGATTGAAGTGAAACGCGATGCGGTCGGTGAAGTTGTGCTCAACAACCTCTACTCCCAGACCCAGTTGCA
+GGTTTCTTTCGGTATCAACATGGTGGCATTGCACCATGGTCAGCCGAAGATCATGAACCTGAAAGACATC
+ATCGCGGCGTTTGTTCGTCACCGCCGTGAAGTGGTGACCCGTCGTACTATTTTCGAACTGCGTAAAGCTC
+GCGATCGTGCTCATATCCTTGAAGCATTAGCCGTGGCGCTGGCGAACATCGACCCGATCATCGAACTGAT
+CCGTCATGCGCCGACGCCTGCAGAAGCGAAAACTGCGCTGGTTGCTAATCCGTGGCAGCTGGGCAACGTT
+GCCGCGATGCTCGAACGTGCTGGCGACGATGCTGCGCGTCCGGAATGGCTGGAGCCAGAGTTCGGCGTGC
+GTGATGGTCTGTACTACCTGACCGAACAGCAAGCTCAGGCGATTCTGGATCTGCGTTTGCAGAAACTGAC
+CGGTCTTGAGCACGAAAAACTGCTCGACGAATACAAAGAGCTGCTGGATCAGATCGCGGAACTGTTGCGT
+ATTCTTGGTAGCGCCGATCGTCTGATGGAAGTGATCCGTGAAGAGCTGGAGCTGGTTCGTGAACAGTTCG
+GTGACAAACGTCGTACTGAAATCACCGCCAACAGCGCAGACATCAACCTGGAAGATCTGATCACCCAGGA
+AGATGTGGTCGTGACGCTCTCTCACCAGGGCTACGTTAAGTATCAGCCGCTTTCTGAATACGAAGCGCAG
+CGTCGTGGCGGGAAAGGTAAATCTGCCGCACGTATTAAAGAAGAAGACTTTATCGACCGACTGCTGGTGG
+CGAACACTCACGACCATATTCTGTGCTTCTCCAGCCGTGGTCGCGTCTATTCGATGAAAGTTTATCAGTT
+GCCGGAAGCCACTCGTGGCGCGCGCGGTCGTCCGATCGTCAACCTGCTGCCGCTGGAGCAGGACGAACGT
+ATCACTGCGATCCTGCCAGTGACCGAGTTTGAAGAAGGCGTGAAAGTCTTCATGGCGACCGCTAACGGTA
+CCGTGAAGAAAACTGTCCTCACCGAGTTCAACCGTCTGCGTACCGCCGGTAAAGTGGCGATCAAACTGGT
+TGACGGCGATGAGCTGATCGGCGTTGACCTGACCAGCGGCGAAGACGAAGTAATGCTGTTCTCCGCTGAA
+GGTAAAGTGGTGCGCTTTAAAGAGTCTTCTGTCCGTGCGATGGGCTGCAACACCACCGGTGTTCGCGGTA
+TTCGCTTAGGTGAAGGCGATAAAGTCGTCTCTCTGATCGTGCCTCGTGGCGATGGCGCAATCCTCACCGC
+AACGCAAAACGGTTACGGTAAACGTACCGCAGTGGCGGAATACCCAACCAAGTCGCGTGCGACGAAAGGG
+GTTATCTCCATCAAGGTTACCGAACGTAACGGTTTAGTTGTTGGCGCGGTACAGGTAGATGACTGCGACC
+AGATCATGATGATCACCGATGCCGGTACGCTGGTACGTACTCGCGTTTCGGAAATCAGCATCGTGGGCCG
+TAACACCCAGGGCGTGATCCTCATCCGTACTGCGGAAGATGAAAACGTAGTGGGTCTGCAACGTGTTGCT
+GAACCGGTTGACGAGGAAGATCTGGATACCATCGACGGCAGTGCCGCGGAAGGGGACGATGAAATCGCTC
+CGGAAGTGGACGTTGACGACGAGCCAGAAGAAGAATAA
diff --git a/staramr/tests/integration/detection/test_AMRDetection.py b/staramr/tests/integration/detection/test_AMRDetection.py
index 86fda1e..61aa6c0 100644
--- a/staramr/tests/integration/detection/test_AMRDetection.py
+++ b/staramr/tests/integration/detection/test_AMRDetection.py
@@ -1628,6 +1628,46 @@ def testResfinderCGEPredictedPhenotypes(self):
                          'Amoxicillin, Amoxicillin+Clavulanic acid, Ampicillin, Ampicillin+Clavulanic acid, Cefepime, Cefixime, Cefotaxime, Cefoxitin, Ceftazidime, Ertapenem, Imipenem, Meropenem, Piperacillin, Piperacillin+Tazobactam',
                          msg='Wrong CGE-predicted phenotypes.')
 
+    def testPointfinderEcoliD87NSuccess(self):
+        # Specifically tests how to handle duplicate entries in the Pointfinder database.
+        pointfinder_database = PointfinderBlastDatabase(self.pointfinder_dir, 'escherichia_coli')
+        blast_handler = JobHandler({'resfinder': self.resfinder_database, 'pointfinder': pointfinder_database}, 2,
+                                     self.blast_out.name)
+        amr_detection = AMRDetectionResistance(self.resfinder_database, self.resfinder_drug_table,
+                                               self.cge_drug_table, blast_handler,
+                                               self.pointfinder_drug_table, pointfinder_database,
+                                               output_dir=self.outdir.name)
+
+        file = path.join(self.test_data_dir, "gyrA-D87N.fsa")
+        files = [file]
+        amr_detection.run_amr_detection(files, 99, 99, 90, 90,0,0,0,0,0)
+
+        pointfinder_results = amr_detection.get_pointfinder_results()
+        self.assertEqual(len(pointfinder_results.index), 1, 'Wrong number of rows in result')
+
+        result = pointfinder_results[pointfinder_results['Gene'] == 'gyrA (D87N)']
+        self.assertEqual(len(result.index), 1, 'Wrong number of results detected')
+        self.assertEqual(result.index[0], 'gyrA-D87N', msg='Wrong file')
+        self.assertEqual(result['Type'].iloc[0], 'codon', msg='Wrong type')
+        self.assertEqual(result['Position'].iloc[0], 87, msg='Wrong codon position')
+        self.assertEqual(result['Mutation'].iloc[0], 'GAC -> AAC (D -> N)', msg='Wrong mutation')
+        self.assertAlmostEqual(result['%Identity'].iloc[0], 99.96, places=2, msg='Wrong pid')
+        self.assertAlmostEqual(result['%Overlap'].iloc[0], 100.00, places=2, msg='Wrong overlap')
+        self.assertEqual(result['HSP Length/Total Length'].iloc[0], '2628/2628', msg='Wrong lengths')
+        self.assertEqual(result['Predicted Phenotype'].iloc[0], 'ciprofloxacin I/R, nalidixic acid',
+                         'Wrong phenotype')
+        self.assertEqual(result['CGE Predicted Phenotype'].iloc[0], 'Nalidixic acid;Nalidixic acid,Ciprofloxacin', 'Wrong phenotype')
+        self.assertEqual(result['CGE Notes'].iloc[0], 'D87G or D87Y confer resistance to nalidixic acid only, if occurring alone. Unknown phenotype if D87H occurs alone;D87G or D87Y confer resistance to nalidixic acid only, if occurring alone. Unknown phenotype if D87H occurs alone', msg='The notes do not match.')
+
+        hit_file = path.join(self.outdir.name, 'pointfinder_gyrA-D87N.fsa')
+        records = SeqIO.to_dict(SeqIO.parse(hit_file, 'fasta'))
+
+        self.assertEqual(len(records), 1, 'Wrong number of hit records')
+
+        expected_records = SeqIO.to_dict(SeqIO.parse(file, 'fasta'))
+        self.assertEqual(expected_records['gyrA_1_CP073768.1'].seq.upper(), records['gyrA'].seq.upper(), "records don't match")
+
+
 
 if __name__ == '__main__':
     unittest.main()