Skip to content

Commit 22f65d1

Browse files
committed
add new baseline expression api
1 parent 9bc2925 commit 22f65d1

6 files changed

Lines changed: 214 additions & 1 deletion

File tree

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
@@ -307,6 +307,14 @@ object Objects extends Logging {
307307
case None => Seq.empty
308308
}
309309
),
310+
Field(
311+
"baselineExpression",
312+
baselineExpressionImp,
313+
description = Some("Baseline expression"),
314+
arguments = pageArg :: Nil,
315+
complexity = Some(complexityCalculator(pageArg)),
316+
resolve = ctx => ctx.ctx.getBaselineExpression(ctx.value.id, ctx.arg(pageArg))
317+
),
310318
Field(
311319
"knownDrugs",
312320
OptionType(knownDrugsImp),
@@ -846,6 +854,64 @@ object Objects extends Logging {
846854
"Array of structs containing expression data relevant to a particular gene"
847855
)
848856
)
857+
implicit lazy val baselineExpressionRowImp: ObjectType[Backend, BaselineExpressionRow] =
858+
deriveObjectType[Backend, BaselineExpressionRow](
859+
ReplaceField(
860+
"tissueBiosampleId",
861+
Field(
862+
"tissueBiosample",
863+
OptionType(biosampleImp),
864+
Some("Tissue biosample entity"),
865+
resolve = r =>
866+
r.value.tissueBiosampleId match {
867+
case Some(id) => biosamplesFetcher.deferOpt(id)
868+
case None => Future.successful(None)
869+
}
870+
)
871+
),
872+
ReplaceField(
873+
"tissueBiosampleParentId",
874+
Field(
875+
"tissueBiosampleParent",
876+
OptionType(biosampleImp),
877+
Some("Tissue biosample parent entity"),
878+
resolve = r =>
879+
r.value.tissueBiosampleParentId match {
880+
case Some(id) => biosamplesFetcher.deferOpt(id)
881+
case None => Future.successful(None)
882+
}
883+
)
884+
),
885+
ReplaceField(
886+
"celltypeBiosampleId",
887+
Field(
888+
"celltypeBiosample",
889+
OptionType(biosampleImp),
890+
Some("Cell type biosample entity"),
891+
resolve = r =>
892+
r.value.celltypeBiosampleId match {
893+
case Some(id) => biosamplesFetcher.deferOpt(id)
894+
case None => Future.successful(None)
895+
}
896+
)
897+
),
898+
ReplaceField(
899+
"celltypeBiosampleParentId",
900+
Field(
901+
"celltypeBiosampleParent",
902+
OptionType(biosampleImp),
903+
Some("Cell type biosample parent entity"),
904+
resolve = r =>
905+
r.value.celltypeBiosampleParentId match {
906+
case Some(id) => biosamplesFetcher.deferOpt(id)
907+
case None => Future.successful(None)
908+
}
909+
)
910+
)
911+
)
912+
913+
implicit lazy val baselineExpressionImp: ObjectType[Backend, BaselineExpression] =
914+
deriveObjectType[Backend, BaselineExpression]()
849915

850916
implicit val adverseEventImp: ObjectType[Backend, AdverseEvent] =
851917
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)