Skip to content

Commit

Permalink
feat: provide full reference genome, and database config to new endpo…
Browse files Browse the repository at this point in the history
…ints
  • Loading branch information
JonasKellerer committed Jan 13, 2024
1 parent 534ac3a commit e406023
Show file tree
Hide file tree
Showing 21 changed files with 216 additions and 119 deletions.
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
package org.genspectrum.lapis

import org.genspectrum.lapis.config.ReferenceGenome
import org.genspectrum.lapis.config.ReferenceGenomeSchema
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class Lapisv2Application

fun main(args: Array<String>) {
val referenceGenomeArgs = ReferenceGenome.readFromFileFromProgramArgsOrEnv(args).toSpringApplicationArgs()
val referenceGenomeSchemaArgs = ReferenceGenomeSchema.readFromFileFromProgramArgsOrEnv(
args,
).toSpringApplicationArgs()

try {
runApplication<Lapisv2Application>(*(args + referenceGenomeArgs))
runApplication<Lapisv2Application>(*(args + referenceGenomeSchemaArgs))
} catch (e: Exception) {
e.printStackTrace()
throw e
Expand Down
29 changes: 22 additions & 7 deletions lapis2/src/main/kotlin/org/genspectrum/lapis/LapisSpringConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ import com.fasterxml.jackson.module.kotlin.readValue
import mu.KotlinLogging
import org.genspectrum.lapis.auth.DataOpennessAuthorizationFilterFactory
import org.genspectrum.lapis.config.DatabaseConfig
import org.genspectrum.lapis.config.NO_REFERENCE_GENOME_FILENAME_ERROR_MESSAGE
import org.genspectrum.lapis.config.REFERENCE_GENOME_ENV_VARIABLE_NAME
import org.genspectrum.lapis.config.REFERENCE_GENOME_FILENAME_ARGS_NAME
import org.genspectrum.lapis.config.REFERENCE_GENOME_GENES_APPLICATION_ARG_PREFIX
import org.genspectrum.lapis.config.REFERENCE_GENOME_SEGMENTS_APPLICATION_ARG_PREFIX
import org.genspectrum.lapis.config.ReferenceGenome
import org.genspectrum.lapis.config.ReferenceSequence
import org.genspectrum.lapis.config.ReferenceGenomeSchema
import org.genspectrum.lapis.config.ReferenceSequenceSchema
import org.genspectrum.lapis.config.SequenceFilterFields
import org.genspectrum.lapis.logging.RequestContext
import org.genspectrum.lapis.logging.RequestContextLogger
Expand All @@ -27,8 +31,8 @@ class LapisSpringConfig {
fun openAPI(
sequenceFilterFields: SequenceFilterFields,
databaseConfig: DatabaseConfig,
referenceGenome: ReferenceGenome,
) = buildOpenApiSchema(sequenceFilterFields, databaseConfig, referenceGenome)
referenceGenomeSchema: ReferenceGenomeSchema,
) = buildOpenApiSchema(sequenceFilterFields, databaseConfig, referenceGenomeSchema)

@Bean
fun databaseConfig(
Expand Down Expand Up @@ -70,11 +74,22 @@ class LapisSpringConfig {
) = dataOpennessAuthorizationFilterFactory.create()

@Bean
fun referenceGenome(
fun referenceGenomeSchema(
@Value("\${$REFERENCE_GENOME_SEGMENTS_APPLICATION_ARG_PREFIX}") nucleotideSegments: List<String>,
@Value("\${$REFERENCE_GENOME_GENES_APPLICATION_ARG_PREFIX}") genes: List<String>,
) = ReferenceGenome(
nucleotideSegments.map { ReferenceSequence(it) },
genes.map { ReferenceSequence(it) },
) = ReferenceGenomeSchema(
nucleotideSegments.map { ReferenceSequenceSchema(it) },
genes.map { ReferenceSequenceSchema(it) },
)

@Bean
fun referenceGenome(
@Value("\${$REFERENCE_GENOME_FILENAME_ARGS_NAME:#{null}}") referenceGenomeFilename: String?,
): ReferenceGenome {
val filename = referenceGenomeFilename
?: System.getenv(REFERENCE_GENOME_ENV_VARIABLE_NAME)
?: throw IllegalArgumentException(NO_REFERENCE_GENOME_FILENAME_ERROR_MESSAGE)

return ReferenceGenome.readFromFile(filename)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,30 @@ import java.io.File
const val REFERENCE_GENOME_SEGMENTS_APPLICATION_ARG_PREFIX = "referenceGenome.segments"
const val REFERENCE_GENOME_GENES_APPLICATION_ARG_PREFIX = "referenceGenome.genes"

private const val ENV_VARIABLE_NAME = "LAPIS_REFERENCE_GENOME_FILENAME"
private const val ARGS_NAME = "referenceGenomeFilename"
const val REFERENCE_GENOME_ENV_VARIABLE_NAME = "LAPIS_REFERENCE_GENOME_FILENAME"
const val REFERENCE_GENOME_FILENAME_ARGS_NAME = "referenceGenomeFilename"

const val NO_REFERENCE_GENOME_FILENAME_ERROR_MESSAGE =
"""No reference genome filename specified.
Please specify a reference genome filename using the --$REFERENCE_GENOME_FILENAME_ARGS_NAME argument
or the $REFERENCE_GENOME_ENV_VARIABLE_NAME environment variable."""

@JsonIgnoreProperties(ignoreUnknown = true)
class ReferenceGenome(val nucleotideSequences: List<ReferenceSequence>, val genes: List<ReferenceSequence>) {
private val nucleotideSequenceNames: Map<LowercaseName, ReferenceSequence> = nucleotideSequences
class ReferenceGenomeSchema(
val nucleotideSequences: List<ReferenceSequenceSchema>,
val genes: List<ReferenceSequenceSchema>,
) {
private val nucleotideSequenceNames: Map<LowercaseName, ReferenceSequenceSchema> = nucleotideSequences
.associateBy { it.name.lowercase() }
private val geneNames: Map<LowercaseName, ReferenceSequence> = genes
private val geneNames: Map<LowercaseName, ReferenceSequenceSchema> = genes
.associateBy { it.name.lowercase() }

fun getNucleotideSequenceFromLowercaseName(lowercaseName: LowercaseName): ReferenceSequence {
fun getNucleotideSequenceFromLowercaseName(lowercaseName: LowercaseName): ReferenceSequenceSchema {
return nucleotideSequenceNames[lowercaseName]
?: throw BadRequestException("Unknown nucleotide sequence from lower case: $lowercaseName")
}

fun getGeneFromLowercaseName(lowercaseName: LowercaseName): ReferenceSequence {
fun getGeneFromLowercaseName(lowercaseName: LowercaseName): ReferenceSequenceSchema {
return geneNames[lowercaseName]
?: throw BadRequestException("Unknown gene from lower case: $lowercaseName")
}
Expand All @@ -34,23 +42,20 @@ class ReferenceGenome(val nucleotideSequences: List<ReferenceSequence>, val gene
}

companion object {
fun readFromFileFromProgramArgsOrEnv(args: Array<String>): ReferenceGenome {
fun readFromFileFromProgramArgsOrEnv(args: Array<String>): ReferenceGenomeSchema {
val filename = readFilenameFromProgramArgs(args)
?: System.getenv(ENV_VARIABLE_NAME)
?: throw IllegalArgumentException(
"No reference genome filename specified. Please specify a reference genome filename using the " +
"--$ARGS_NAME argument or the $ENV_VARIABLE_NAME environment variable.",
)
?: System.getenv(REFERENCE_GENOME_ENV_VARIABLE_NAME)
?: throw IllegalArgumentException(NO_REFERENCE_GENOME_FILENAME_ERROR_MESSAGE)

return readFromFile(filename)
}

fun readFromFile(filename: String): ReferenceGenome {
fun readFromFile(filename: String): ReferenceGenomeSchema {
return jacksonObjectMapper().readValue(File(filename))
}

private fun readFilenameFromProgramArgs(args: Array<String>): String? {
val referenceGenomeArg = args.find { it.startsWith("--$ARGS_NAME=") }
val referenceGenomeArg = args.find { it.startsWith("--$REFERENCE_GENOME_FILENAME_ARGS_NAME=") }
return referenceGenomeArg?.substringAfter("=")
}
}
Expand All @@ -66,6 +71,22 @@ class ReferenceGenome(val nucleotideSequences: List<ReferenceSequence>, val gene
}

@JsonIgnoreProperties(ignoreUnknown = true)
data class ReferenceSequenceSchema(
val name: String,
)

data class ReferenceGenome(
val nucleotideSequences: List<ReferenceSequence>,
val genes: List<ReferenceSequence>,
) {
companion object {
fun readFromFile(filename: String): ReferenceGenome {
return jacksonObjectMapper().readValue(File(filename))
}
}
}

data class ReferenceSequence(
val name: String,
val sequence: String,
)
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ const val AMINO_ACID_INSERTIONS_ENDPOINT_DESCRIPTION =
"""Returns a list of mutations along with the counts and proportions whose proportions are greater
than or equal to the specified minProportion. Only sequences matching the specified sequence filters are
considered."""
const val INFO_ENDPOINT_DESCRIPTION = "Returns information about LAPIS"
const val INFO_ENDPOINT_DESCRIPTION = "Returns information about LAPIS."
const val DATABASE_CONFIG_ENDPOINT_DESCRIPTION = "Returns the database configuration."
const val REFERENCE_GENOME_ENDPOINT_DESCRIPTION = "Returns the reference genome."
const val ALIGNED_AMINO_ACID_SEQUENCE_ENDPOINT_DESCRIPTION =
"""Returns a string of fasta formated aligned amino acid sequences. Only sequences matching the specified
sequence filters are considered."""
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.genspectrum.lapis.controller

import io.swagger.v3.oas.annotations.Operation
import org.genspectrum.lapis.config.DatabaseConfig
import org.genspectrum.lapis.config.ReferenceGenome
import org.genspectrum.lapis.model.SiloQueryModel
import org.genspectrum.lapis.request.LapisInfo
import org.springframework.http.MediaType
Expand All @@ -9,14 +11,28 @@ import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

const val INFO_ROUTE = "/info"
const val DATABASE_CONFIG_ROUTE = "/databaseConfig"
const val REFERENCE_GENOME_ROUTE = "/referenceGenome"

@RestController
@RequestMapping("/sample")
class InfoController(private val siloQueryModel: SiloQueryModel) {
class InfoController(
private val siloQueryModel: SiloQueryModel,
private val databaseConfig: DatabaseConfig,
private val referenceGenome: ReferenceGenome,
) {
@GetMapping(INFO_ROUTE, produces = [MediaType.APPLICATION_JSON_VALUE])
@Operation(description = INFO_ENDPOINT_DESCRIPTION)
fun getInfo(): LapisInfo {
val siloInfo = siloQueryModel.getInfo()
return LapisInfo(siloInfo.dataVersion)
}

@GetMapping(DATABASE_CONFIG_ROUTE, produces = [MediaType.APPLICATION_JSON_VALUE])
@Operation(description = DATABASE_CONFIG_ENDPOINT_DESCRIPTION)
fun getDatabaseConfig(): DatabaseConfig = databaseConfig

@GetMapping(REFERENCE_GENOME_ROUTE, produces = [MediaType.APPLICATION_JSON_VALUE])
@Operation(description = REFERENCE_GENOME_ENDPOINT_DESCRIPTION)
fun getReferenceGenome(): ReferenceGenome = referenceGenome
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package org.genspectrum.lapis.controller
import io.swagger.v3.oas.annotations.Parameter
import io.swagger.v3.oas.annotations.media.Schema
import org.genspectrum.lapis.config.REFERENCE_GENOME_SEGMENTS_APPLICATION_ARG_PREFIX
import org.genspectrum.lapis.config.ReferenceGenome
import org.genspectrum.lapis.config.ReferenceGenomeSchema
import org.genspectrum.lapis.logging.RequestContext
import org.genspectrum.lapis.model.SiloQueryModel
import org.genspectrum.lapis.openApi.AminoAcidInsertions
Expand Down Expand Up @@ -41,7 +41,7 @@ const val IS_SINGLE_SEGMENT_SEQUENCE_EXPRESSION =
class SingleSegmentedSequenceController(
private val siloQueryModel: SiloQueryModel,
private val requestContext: RequestContext,
private val referenceGenome: ReferenceGenome,
private val referenceGenomeSchema: ReferenceGenomeSchema,
) {
@GetMapping(ALIGNED_NUCLEOTIDE_SEQUENCES_ROUTE, produces = ["text/x-fasta"])
@LapisAlignedSingleSegmentedNucleotideSequenceResponse
Expand Down Expand Up @@ -87,7 +87,7 @@ class SingleSegmentedSequenceController(
return siloQueryModel.getGenomicSequence(
request,
SequenceType.ALIGNED,
referenceGenome.nucleotideSequences[0].name,
referenceGenomeSchema.nucleotideSequences[0].name,
)
}

Expand All @@ -103,7 +103,7 @@ class SingleSegmentedSequenceController(
return siloQueryModel.getGenomicSequence(
request,
SequenceType.ALIGNED,
referenceGenome.nucleotideSequences[0].name,
referenceGenomeSchema.nucleotideSequences[0].name,
)
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.genspectrum.lapis.model

import org.genspectrum.lapis.config.ReferenceGenome
import org.genspectrum.lapis.config.ReferenceGenomeSchema
import org.genspectrum.lapis.request.MutationProportionsRequest
import org.genspectrum.lapis.request.SequenceFiltersRequest
import org.genspectrum.lapis.request.SequenceFiltersRequestWithFields
Expand All @@ -20,7 +20,7 @@ import org.springframework.stereotype.Component
class SiloQueryModel(
private val siloClient: SiloClient,
private val siloFilterExpressionMapper: SiloFilterExpressionMapper,
private val referenceGenome: ReferenceGenome,
private val referenceGenomeSchema: ReferenceGenomeSchema,
) {
fun getAggregated(sequenceFilters: SequenceFiltersRequestWithFields) =
siloClient.sendQuery(
Expand Down Expand Up @@ -51,7 +51,7 @@ class SiloQueryModel(
)
return data.map { it ->
val sequenceName =
if (referenceGenome.isSingleSegmented()) it.mutation else "${it.sequenceName}:${it.mutation}"
if (referenceGenomeSchema.isSingleSegmented()) it.mutation else "${it.sequenceName}:${it.mutation}"

NucleotideMutationResponse(
sequenceName,
Expand Down Expand Up @@ -110,7 +110,7 @@ class SiloQueryModel(
)

return data.map { it ->
val sequenceName = if (referenceGenome.isSingleSegmented()) "" else "${it.sequenceName}:"
val sequenceName = if (referenceGenomeSchema.isSingleSegmented()) "" else "${it.sequenceName}:"

NucleotideInsertionResponse(
"ins_${sequenceName}${it.position}:${it.insertions}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import VariantQueryParser.OrContext
import VariantQueryParser.PangolineageQueryContext
import org.antlr.v4.runtime.RuleContext
import org.antlr.v4.runtime.tree.ParseTreeListener
import org.genspectrum.lapis.config.ReferenceGenome
import org.genspectrum.lapis.config.ReferenceGenomeSchema
import org.genspectrum.lapis.request.LAPIS_INSERTION_AMBIGUITY_SYMBOL
import org.genspectrum.lapis.request.SILO_INSERTION_AMBIGUITY_SYMBOL
import org.genspectrum.lapis.silo.AminoAcidInsertionContains
Expand All @@ -34,7 +34,9 @@ import org.genspectrum.lapis.silo.PangoLineageEquals
import org.genspectrum.lapis.silo.SiloFilterExpression
import org.genspectrum.lapis.silo.StringEquals

class VariantQueryCustomListener(val referenceGenome: ReferenceGenome) : VariantQueryBaseListener(), ParseTreeListener {
class VariantQueryCustomListener(val referenceGenomeSchema: ReferenceGenomeSchema) :
VariantQueryBaseListener(),
ParseTreeListener {
private val expressionStack = ArrayDeque<SiloFilterExpression>()

fun getVariantQueryExpression(): SiloFilterExpression {
Expand Down Expand Up @@ -110,7 +112,7 @@ class VariantQueryCustomListener(val referenceGenome: ReferenceGenome) : Variant
return
}
val position = ctx.position().text.toInt()
val gene = referenceGenome.getGeneFromLowercaseName(ctx.gene().text.lowercase()).name
val gene = referenceGenomeSchema.getGeneFromLowercaseName(ctx.gene().text.lowercase()).name

val expression = when (val aaSymbol = ctx.possiblyAmbiguousAaSymbol()) {
null -> HasAminoAcidMutation(gene, position)
Expand All @@ -122,7 +124,7 @@ class VariantQueryCustomListener(val referenceGenome: ReferenceGenome) : Variant

override fun enterAaInsertionQuery(ctx: AaInsertionQueryContext) {
val value = ctx.aaInsertionSymbol().joinToString("", transform = ::mapInsertionSymbol)
val gene = referenceGenome.getGeneFromLowercaseName(ctx.gene().text.lowercase()).name
val gene = referenceGenomeSchema.getGeneFromLowercaseName(ctx.gene().text.lowercase()).name

expressionStack.addLast(
AminoAcidInsertionContains(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ import VariantQueryParser
import org.antlr.v4.runtime.CharStreams
import org.antlr.v4.runtime.CommonTokenStream
import org.antlr.v4.runtime.tree.ParseTreeWalker
import org.genspectrum.lapis.config.ReferenceGenome
import org.genspectrum.lapis.config.ReferenceGenomeSchema
import org.genspectrum.lapis.silo.SiloFilterExpression
import org.springframework.stereotype.Component

@Component
class VariantQueryFacade(val referenceGenome: ReferenceGenome) {
class VariantQueryFacade(val referenceGenomeSchema: ReferenceGenomeSchema) {
fun map(variantQuery: String): SiloFilterExpression {
val lexer = VariantQueryLexer(CharStreams.fromString(variantQuery))
val tokens = CommonTokenStream(lexer)
val parser = VariantQueryParser(tokens)
val listener = VariantQueryCustomListener(referenceGenome)
val listener = VariantQueryCustomListener(referenceGenomeSchema)

val walker = ParseTreeWalker()
walker.walk(listener, parser.start())
Expand Down
Loading

0 comments on commit e406023

Please sign in to comment.