Skip to content

Commit 4d89a97

Browse files
committed
add new baseline expression api
1 parent 273e2ee commit 4d89a97

File tree

6 files changed

+214
-1
lines changed

6 files changed

+214
-1
lines changed

app/models/Backend.scala

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ import gql.validators.QueryTermsValidator.*
1111

1212
import javax.inject.Inject
1313
import models.Helpers.*
14-
import models.db.{IntervalsQuery, QAOTF, QLITAGG, QW2V, TargetsQuery}
14+
import models.db.{IntervalsQuery, QAOTF, QLITAGG, QW2V, TargetsQuery, BaselineExpressionQuery}
1515
import models.entities.Publication.*
1616
import models.entities.Associations.*
17+
import models.entities.BaselineExpression.*
1718
import models.entities.Biosample.*
1819
import models.entities.CredibleSet.*
1920
import models.entities.Configuration.*
@@ -776,6 +777,37 @@ class Backend @Inject() (implicit
776777
esRetriever.getByIds(targetIndexName, ids, fromJsValue[Expressions])
777778
}
778779

780+
def getBaselineExpression(targetId: String,
781+
pagination: Option[Pagination]
782+
): Future[BaselineExpression] = {
783+
val page = pagination.getOrElse(Pagination.mkDefault)
784+
val tableName = getTableWithPrefixOrDefault(
785+
defaultOTSettings.clickhouse.baselineExpression.name
786+
)
787+
val baselineExpressionQuery = BaselineExpressionQuery(
788+
targetId,
789+
tableName,
790+
page.index,
791+
page.size
792+
)
793+
val total: Int = dbRetriever
794+
.executeQuery[Int, Query](baselineExpressionQuery.totals)
795+
.map {
796+
case Seq(totalCount) => totalCount
797+
case _ => 0
798+
}
799+
.await
800+
logger.info(s"Total baseline expressions found: $total")
801+
802+
val results =
803+
if total == 0 then Future.successful(BaselineExpression(total, Vector.empty))
804+
else
805+
dbRetriever
806+
.executeQuery[BaselineExpressionRow, Query](baselineExpressionQuery.query)
807+
.map(baselineExpressionRows => BaselineExpression(total, baselineExpressionRows))
808+
results
809+
}
810+
779811
def getReactomeNodes(ids: Seq[String]): Future[IndexedSeq[Reactome]] = {
780812
val targetIndexName = getIndexOrDefault("reactome")
781813

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package models.db
2+
3+
import esecuele.Column.column
4+
import esecuele.Column.literal
5+
import esecuele._
6+
import play.api.Logging
7+
8+
case class BaselineExpressionQuery(targetId: String, tableName: String, offset: Int, size: Int)
9+
extends Queryable
10+
with Logging {
11+
12+
private val positional_query = Where(
13+
Functions.equals(column("targetId"), literal(targetId))
14+
)
15+
16+
val totals: Query =
17+
Query(
18+
Select(Functions.count(Column.star) :: Nil),
19+
From(column(tableName)),
20+
positional_query
21+
)
22+
23+
override val query: Query =
24+
Query(
25+
Select(
26+
Column.star :: Nil
27+
),
28+
From(column(tableName)),
29+
positional_query,
30+
OrderBy(column("targetId") :: Nil),
31+
Limit(offset, size)
32+
)
33+
}

app/models/entities/Configuration.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ object Configuration {
6060
*/
6161
case class ClickhouseSettings(
6262
defaultDatabaseName: String,
63+
baselineExpression: DbTableSettings,
6364
intervals: DbTableSettings,
6465
target: TargetSettings,
6566
disease: DiseaseSettings,

app/models/entities/Expressions.scala

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import play.api.libs.json.Reads._
55
import play.api.libs.functional.syntax._
66
import play.api.libs.json.JsonConfiguration.Aux
77
import play.api.libs.json.JsonNaming.SnakeCase
8+
import slick.jdbc.GetResult
89

910
case class Tissue(id: String, label: String, anatomicalSystems: Seq[String], organs: Seq[String])
1011

@@ -51,3 +52,79 @@ object Expressions {
5152
(__ \ "tissues").readWithDefault[Seq[Expression]](Seq.empty)
5253
)(Expressions.apply)
5354
}
55+
56+
case class BaselineExpressionRow(
57+
targetId: String,
58+
targetFromSourceId: Option[String],
59+
tissueBiosampleId: Option[String],
60+
tissueBiosampleParentId: Option[String],
61+
tissueBiosampleFromSource: Option[String],
62+
celltypeBiosampleId: Option[String],
63+
celltypeBiosampleParentId: Option[String],
64+
celltypeBiosampleFromSource: Option[String],
65+
min: Option[Double],
66+
q1: Option[Double],
67+
median: Option[Double],
68+
q3: Option[Double],
69+
max: Option[Double],
70+
distribution_score: Double,
71+
specificity_score: Option[Double],
72+
datasourceId: String,
73+
datatypeId: String,
74+
unit: String
75+
)
76+
77+
case class BaselineExpression(
78+
count: Long,
79+
rows: Vector[BaselineExpressionRow]
80+
)
81+
82+
object BaselineExpression {
83+
val empty: BaselineExpression = BaselineExpression(0, Vector.empty)
84+
85+
implicit val getBaselineExpressionRowFromDB: GetResult[BaselineExpressionRow] =
86+
GetResult { r =>
87+
val targetId: String = r.<<
88+
val targetFromSourceId: Option[String] = r.<<?
89+
val tissueBiosampleId: Option[String] = r.<<?
90+
val tissueBiosampleParentId: Option[String] = r.<<?
91+
val tissueBiosampleFromSource: Option[String] = r.<<?
92+
val celltypeBiosampleId: Option[String] = r.<<?
93+
val celltypeBiosampleParentId: Option[String] = r.<<?
94+
val celltypeBiosampleFromSource: Option[String] = r.<<?
95+
val min: Option[Double] = r.<<?
96+
val q1: Option[Double] = r.<<?
97+
val median: Option[Double] = r.<<?
98+
val q3: Option[Double] = r.<<?
99+
val max: Option[Double] = r.<<?
100+
val distribution_score: Double = r.<<
101+
val specificity_score: Option[Double] = r.<<?
102+
val datasourceId: String = r.<<
103+
val datatypeId: String = r.<<
104+
val unit: String = r.<<
105+
106+
BaselineExpressionRow(
107+
targetId,
108+
targetFromSourceId,
109+
tissueBiosampleId,
110+
tissueBiosampleParentId,
111+
tissueBiosampleFromSource,
112+
celltypeBiosampleId,
113+
celltypeBiosampleParentId,
114+
celltypeBiosampleFromSource,
115+
min,
116+
q1,
117+
median,
118+
q3,
119+
max,
120+
distribution_score,
121+
specificity_score,
122+
datasourceId,
123+
datatypeId,
124+
unit
125+
)
126+
}
127+
128+
implicit val BaselineExpressionRowImp: OFormat[BaselineExpressionRow] =
129+
Json.format[BaselineExpressionRow]
130+
}

app/models/gql/Objects.scala

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,14 @@ object Objects extends Logging {
310310
case None => Seq.empty
311311
}
312312
),
313+
Field(
314+
"baselineExpression",
315+
baselineExpressionImp,
316+
description = Some("Baseline expression"),
317+
arguments = pageArg :: Nil,
318+
complexity = Some(complexityCalculator(pageArg)),
319+
resolve = ctx => ctx.ctx.getBaselineExpression(ctx.value.id, ctx.arg(pageArg))
320+
),
313321
Field(
314322
"knownDrugs",
315323
OptionType(knownDrugsImp),
@@ -849,6 +857,64 @@ object Objects extends Logging {
849857
"Array of structs containing expression data relevant to a particular gene"
850858
)
851859
)
860+
implicit lazy val baselineExpressionRowImp: ObjectType[Backend, BaselineExpressionRow] =
861+
deriveObjectType[Backend, BaselineExpressionRow](
862+
ReplaceField(
863+
"tissueBiosampleId",
864+
Field(
865+
"tissueBiosample",
866+
OptionType(biosampleImp),
867+
Some("Tissue biosample entity"),
868+
resolve = r =>
869+
r.value.tissueBiosampleId match {
870+
case Some(id) => biosamplesFetcher.deferOpt(id)
871+
case None => Future.successful(None)
872+
}
873+
)
874+
),
875+
ReplaceField(
876+
"tissueBiosampleParentId",
877+
Field(
878+
"tissueBiosampleParent",
879+
OptionType(biosampleImp),
880+
Some("Tissue biosample parent entity"),
881+
resolve = r =>
882+
r.value.tissueBiosampleParentId match {
883+
case Some(id) => biosamplesFetcher.deferOpt(id)
884+
case None => Future.successful(None)
885+
}
886+
)
887+
),
888+
ReplaceField(
889+
"celltypeBiosampleId",
890+
Field(
891+
"celltypeBiosample",
892+
OptionType(biosampleImp),
893+
Some("Cell type biosample entity"),
894+
resolve = r =>
895+
r.value.celltypeBiosampleId match {
896+
case Some(id) => biosamplesFetcher.deferOpt(id)
897+
case None => Future.successful(None)
898+
}
899+
)
900+
),
901+
ReplaceField(
902+
"celltypeBiosampleParentId",
903+
Field(
904+
"celltypeBiosampleParent",
905+
OptionType(biosampleImp),
906+
Some("Cell type biosample parent entity"),
907+
resolve = r =>
908+
r.value.celltypeBiosampleParentId match {
909+
case Some(id) => biosamplesFetcher.deferOpt(id)
910+
case None => Future.successful(None)
911+
}
912+
)
913+
)
914+
)
915+
916+
implicit lazy val baselineExpressionImp: ObjectType[Backend, BaselineExpression] =
917+
deriveObjectType[Backend, BaselineExpression]()
852918

853919
implicit val adverseEventImp: ObjectType[Backend, AdverseEvent] =
854920
deriveObjectType[Backend, AdverseEvent](

conf/application.conf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ slick.dbs {
2020
ot {
2121
clickhouse {
2222
defaultDatabaseName = "ot"
23+
baselineExpression {
24+
label = "Baseline expression table"
25+
name = "baseline_expression"
26+
}
2327
intervals {
2428
label = "Intervals table"
2529
name = "intervals"

0 commit comments

Comments
 (0)