Skip to content

Commit

Permalink
feat: implement amino acid mutations in advanced variant queries
Browse files Browse the repository at this point in the history
issue: #287
This also fixed nucleotide mutations: When no "second symbol" is given, it should result in a "has mutation" filter
  • Loading branch information
fengelniederhammer committed Jul 17, 2023
1 parent f33c0ff commit 85d0d82
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,14 @@ nOfExprs: expr (',' expr)*;
nucleotideInsertionQuery: insertionKeyword position ':' (possibleAmbiguousNucleotideSymbol | '?')+;
insertionKeyword: 'ins_' | 'INS_';

aaMutationQuery: gene ':' aaSymbol? position possibleAmbiguousAaSymbol?;
aaMutationQuery: gene ':' aaSymbol? position possiblyAmbiguousAaSymbol?;
aaSymbol: A | R | N | D | C | E | Q | G | H | I | L | K | M | F | P | S | T | W | Y | V | ASTERISK;
ambiguousAaSymbol: X | MINUS | DOT;
possibleAmbiguousAaSymbol: aaSymbol | ambiguousAaSymbol;
possiblyAmbiguousAaSymbol: aaSymbol | ambiguousAaSymbol;
gene: covidGene;
covidGene : E | M | N | S | ORF;

aaInsertionQuery: insertionKeyword gene ':' position ':' (possibleAmbiguousAaSymbol | '?')+;
aaInsertionQuery: insertionKeyword gene ':' position ':' (possiblyAmbiguousAaSymbol | '?')+;

nextcladePangolineageQuery: nextcladePangoLineagePrefix pangolineageQuery;
nextcladePangoLineagePrefix: 'nextcladePangoLineage:' | 'NEXTCLADEPANGOLINEAGE:';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ import VariantQueryParser.NucleotideMutationQueryContext
import VariantQueryParser.OrContext
import VariantQueryParser.PangolineageQueryContext
import org.antlr.v4.runtime.tree.ParseTreeListener
import org.genspectrum.lapis.silo.AminoAcidSymbolEquals
import org.genspectrum.lapis.silo.And
import org.genspectrum.lapis.silo.HasAminoAcidMutation
import org.genspectrum.lapis.silo.HasNucleotideMutation
import org.genspectrum.lapis.silo.Maybe
import org.genspectrum.lapis.silo.NOf
import org.genspectrum.lapis.silo.Not
Expand All @@ -36,10 +39,13 @@ class VariantQueryCustomListener : VariantQueryBaseListener(), ParseTreeListener
return
}
val position = ctx.position().text.toInt()
val secondSymbol = ctx.nucleotideMutationQuerySecondSymbol()?.text ?: "-"

val expr = NucleotideSymbolEquals(null, position, secondSymbol)
expressionStack.addLast(expr)
val expression = when (val secondSymbol = ctx.nucleotideMutationQuerySecondSymbol()) {
null -> HasNucleotideMutation(null, position)
else -> NucleotideSymbolEquals(null, position, secondSymbol.text)
}

expressionStack.addLast(expression)
}

override fun enterPangolineageQuery(ctx: PangolineageQueryContext?) {
Expand Down Expand Up @@ -94,7 +100,17 @@ class VariantQueryCustomListener : VariantQueryBaseListener(), ParseTreeListener
}

override fun enterAaMutationQuery(ctx: AaMutationQueryContext?) {
throw SiloNotImplementedError("Amino acid mutations are not supported yet.", NotImplementedError())
if (ctx == null) {
return
}
val position = ctx.position().text.toInt()

val expression = when (val aaSymbol = ctx.possiblyAmbiguousAaSymbol()) {
null -> HasAminoAcidMutation(ctx.gene().text, position)
else -> AminoAcidSymbolEquals(ctx.gene().text, position, aaSymbol.text)
}

expressionStack.addLast(expression)
}

override fun enterAaInsertionQuery(ctx: AaInsertionQueryContext?) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package org.genspectrum.lapis.model

import org.genspectrum.lapis.silo.AminoAcidSymbolEquals
import org.genspectrum.lapis.silo.And
import org.genspectrum.lapis.silo.HasAminoAcidMutation
import org.genspectrum.lapis.silo.HasNucleotideMutation
import org.genspectrum.lapis.silo.Maybe
import org.genspectrum.lapis.silo.NOf
import org.genspectrum.lapis.silo.Not
import org.genspectrum.lapis.silo.NucleotideSymbolEquals
import org.genspectrum.lapis.silo.Or
import org.genspectrum.lapis.silo.PangoLineageEquals
import org.hamcrest.MatcherAssert
import org.hamcrest.Matchers
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
Expand Down Expand Up @@ -47,7 +50,7 @@ class VariantQueryFacadeTest {
),
),
),
Not(NucleotideSymbolEquals(null, 600, "-")),
Not(HasNucleotideMutation(null, 600)),
),
),
Maybe(
Expand Down Expand Up @@ -75,7 +78,7 @@ class VariantQueryFacadeTest {
),
)

MatcherAssert.assertThat(result, Matchers.equalTo(expectedResult))
assertThat(result, equalTo(expectedResult))
}

@Test
Expand All @@ -85,12 +88,21 @@ class VariantQueryFacadeTest {
val result = underTest.map(variantQuery)

val expectedResult = NucleotideSymbolEquals(null, 300, "G")
MatcherAssert.assertThat(result, Matchers.equalTo(expectedResult))
assertThat(result, equalTo(expectedResult))
}

@Test
fun `given a variantQuery with mutation with position only then should return HasNucleotideMutation filter`() {
val variantQuery = "400"

val result = underTest.map(variantQuery)

assertThat(result, equalTo(HasNucleotideMutation(null, 400)))
}

@Test
fun `given a variantQuery with an 'And' expression then map should return the corresponding SiloQuery`() {
val variantQuery = "300G & 400"
val variantQuery = "300G & 400-"

val result = underTest.map(variantQuery)

Expand All @@ -100,7 +112,7 @@ class VariantQueryFacadeTest {
NucleotideSymbolEquals(null, 400, "-"),
),
)
MatcherAssert.assertThat(result, Matchers.equalTo(expectedResult))
assertThat(result, equalTo(expectedResult))
}

@Test
Expand All @@ -120,7 +132,7 @@ class VariantQueryFacadeTest {
NucleotideSymbolEquals(null, 500, "B"),
),
)
MatcherAssert.assertThat(result, Matchers.equalTo(expectedResult))
assertThat(result, equalTo(expectedResult))
}

@Test
Expand All @@ -130,12 +142,12 @@ class VariantQueryFacadeTest {
val result = underTest.map(variantQuery)

val expectedResult = Not(NucleotideSymbolEquals(null, 300, "G"))
MatcherAssert.assertThat(result, Matchers.equalTo(expectedResult))
assertThat(result, equalTo(expectedResult))
}

@Test
fun `given a variant variantQuery with an 'Or' expression then map should return the corresponding SiloQuery`() {
val variantQuery = "300G | 400"
val variantQuery = "300G | 400-"

val result = underTest.map(variantQuery)

Expand All @@ -145,7 +157,7 @@ class VariantQueryFacadeTest {
NucleotideSymbolEquals(null, 400, "-"),
),
)
MatcherAssert.assertThat(result, Matchers.equalTo(expectedResult))
assertThat(result, equalTo(expectedResult))
}

@Test
Expand All @@ -165,7 +177,7 @@ class VariantQueryFacadeTest {
),
),
)
MatcherAssert.assertThat(result, Matchers.equalTo(expectedResult))
assertThat(result, equalTo(expectedResult))
}

@Test
Expand All @@ -175,7 +187,7 @@ class VariantQueryFacadeTest {
val result = underTest.map(variantQuery)

val expectedResult = Maybe(NucleotideSymbolEquals(null, 300, "G"))
MatcherAssert.assertThat(result, Matchers.equalTo(expectedResult))
assertThat(result, equalTo(expectedResult))
}

@Test
Expand All @@ -185,7 +197,7 @@ class VariantQueryFacadeTest {
val result = underTest.map(variantQuery)

val expectedResult = PangoLineageEquals("pango_lineage", "A.1.2.3", false)
MatcherAssert.assertThat(result, Matchers.equalTo(expectedResult))
assertThat(result, equalTo(expectedResult))
}

@Test
Expand All @@ -195,7 +207,7 @@ class VariantQueryFacadeTest {
val result = underTest.map(variantQuery)

val expectedResult = PangoLineageEquals("pango_lineage", "A.1.2.3", true)
MatcherAssert.assertThat(result, Matchers.equalTo(expectedResult))
assertThat(result, equalTo(expectedResult))
}

@Test
Expand All @@ -213,7 +225,7 @@ class VariantQueryFacadeTest {
NucleotideSymbolEquals(null, 345, "G"),
),
)
MatcherAssert.assertThat(result, Matchers.equalTo(expectedResult))
assertThat(result, equalTo(expectedResult))
}

@Test
Expand All @@ -231,7 +243,7 @@ class VariantQueryFacadeTest {
NucleotideSymbolEquals(null, 345, "G"),
),
)
MatcherAssert.assertThat(result, Matchers.equalTo(expectedResult))
assertThat(result, equalTo(expectedResult))
}

@Test
Expand All @@ -240,22 +252,46 @@ class VariantQueryFacadeTest {

val exception = assertThrows<SiloNotImplementedError> { underTest.map(variantQuery) }

MatcherAssert.assertThat(
assertThat(
exception.message,
Matchers.equalTo("Nucleotide insertions are not supported yet."),
equalTo("Nucleotide insertions are not supported yet."),
)
}

@Test
fun `given a variant variantQuery with a 'AA mutation' expression then map should throw an error`() {
fun `given amino acidAA mutation expression then should map to AminoAcidSymbolEquals`() {
val variantQuery = "S:N501Y"

val exception = assertThrows<SiloNotImplementedError> { underTest.map(variantQuery) }
val result = underTest.map(variantQuery)

MatcherAssert.assertThat(
exception.message,
Matchers.equalTo("Amino acid mutations are not supported yet."),
)
assertThat(result, equalTo(AminoAcidSymbolEquals("S", 501, "Y")))
}

@Test
fun `given amino acid mutation expression without first symbol then should map to AminoAcidSymbolEquals`() {
val variantQuery = "S:501Y"

val result = underTest.map(variantQuery)

assertThat(result, equalTo(AminoAcidSymbolEquals("S", 501, "Y")))
}

@Test
fun `given amino acid mutation expression without second symbol then should return HasAminoAcidMutation`() {
val variantQuery = "S:N501"

val result = underTest.map(variantQuery)

assertThat(result, equalTo(HasAminoAcidMutation("S", 501)))
}

@Test
fun `given amino acid mutation expression without any symbol then should return HasAminoAcidMutation`() {
val variantQuery = "S:501"

val result = underTest.map(variantQuery)

assertThat(result, equalTo(HasAminoAcidMutation("S", 501)))
}

@Test
Expand All @@ -264,9 +300,9 @@ class VariantQueryFacadeTest {

val exception = assertThrows<SiloNotImplementedError> { underTest.map(variantQuery) }

MatcherAssert.assertThat(
assertThat(
exception.message,
Matchers.equalTo("Amino acid insertions are not supported yet."),
equalTo("Amino acid insertions are not supported yet."),
)
}

Expand All @@ -276,9 +312,9 @@ class VariantQueryFacadeTest {

val exception = assertThrows<SiloNotImplementedError> { underTest.map(variantQuery) }

MatcherAssert.assertThat(
assertThat(
exception.message,
Matchers.equalTo("Nextclade pango lineages are not supported yet."),
equalTo("Nextclade pango lineages are not supported yet."),
)
}

Expand All @@ -288,9 +324,9 @@ class VariantQueryFacadeTest {

val exception = assertThrows<SiloNotImplementedError> { underTest.map(variantQuery) }

MatcherAssert.assertThat(
assertThat(
exception.message,
Matchers.equalTo("Nextstrain clade lineages are not supported yet."),
equalTo("Nextstrain clade lineages are not supported yet."),
)
}

Expand All @@ -300,9 +336,9 @@ class VariantQueryFacadeTest {

val exception = assertThrows<SiloNotImplementedError> { underTest.map(variantQuery) }

MatcherAssert.assertThat(
assertThat(
exception.message,
Matchers.equalTo("Gisaid clade lineages are not supported yet."),
equalTo("Gisaid clade lineages are not supported yet."),
)
}
}

0 comments on commit 85d0d82

Please sign in to comment.