aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/xyz/driver/pdsuidomain
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/scala/xyz/driver/pdsuidomain')
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/entities/Criterion.scala2
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/entities/EligibilityArm.scala42
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/entities/Intervention.scala2
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/entities/SlotArmEligibilityArm.scala13
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/entities/SlotArms.scala20
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/entities/Trial.scala20
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/fakes/entities/trialcuration.scala8
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/formats/json/criterion/ApiCriterion.scala4
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/formats/json/criterion/ApiNewCriterion.scala2
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/formats/json/criterion/ApiUpdateCriterion.scala4
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/formats/json/eligibilityarm/ApiEligibilityArm.scala57
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/formats/json/eligibilityarm/ApiEligibilityCreateArm.scala33
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/formats/json/eligibilityarm/ApiPartialEligibilityArm.scala23
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/formats/json/slotarm/ApiPartialSlotArm.scala14
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/formats/json/slotarm/ApiSlotArm.scala35
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/formats/json/slotarm/ApiSlotCreateArm.scala20
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/criterion.scala6
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/intervention.scala4
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/trial.scala4
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/formats/json/trial/ApiTrial.scala11
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/services/CriterionService.scala4
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/services/EligibilityArmService.scala139
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/services/SlotArmService.scala125
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/services/fake/FakeTrialService.scala3
24 files changed, 561 insertions, 34 deletions
diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/Criterion.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/Criterion.scala
index 2432251..26ced22 100644
--- a/src/main/scala/xyz/driver/pdsuidomain/entities/Criterion.scala
+++ b/src/main/scala/xyz/driver/pdsuidomain/entities/Criterion.scala
@@ -30,7 +30,7 @@ object Criterion {
}
}
-final case class CriterionArm(criterionId: LongId[Criterion], armId: LongId[Arm])
+final case class CriterionArm(criterionId: LongId[Criterion], armId: LongId[EligibilityArm])
object CriterionArm {
diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/EligibilityArm.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/EligibilityArm.scala
new file mode 100644
index 0000000..50b49ad
--- /dev/null
+++ b/src/main/scala/xyz/driver/pdsuidomain/entities/EligibilityArm.scala
@@ -0,0 +1,42 @@
+package xyz.driver.pdsuidomain.entities
+
+import java.time.LocalDateTime
+
+import xyz.driver.entities.patient.CancerType
+import xyz.driver.pdsuicommon.domain.{LongId, StringId}
+import xyz.driver.pdsuicommon.logging._
+
+final case class EligibilityArm(id: LongId[EligibilityArm],
+ name: String,
+ originalName: String,
+ trialId: StringId[Trial],
+ deleted: Option[LocalDateTime] = None)
+
+object EligibilityArm {
+
+ implicit def toPhiString(x: EligibilityArm): PhiString = {
+ import x._
+ phi"Arm(id=$id, name=${Unsafe(x.name)}, trialId=${Unsafe(x.trialId)})"
+ }
+}
+
+final case class EligibilityArmDisease(eligibilityArmId: LongId[EligibilityArm], disease: CancerType)
+
+object EligibilityArmDisease {
+
+ implicit def toPhiString(x: EligibilityArmDisease): PhiString = {
+ phi"EligibilityArmDisease(eligibilityArmId=${Unsafe(x.eligibilityArmId)}, disease=${Unsafe(x.disease)})"
+ }
+
+}
+
+final case class EligibilityArmWithDiseases(eligibilityArm: EligibilityArm,
+ eligibilityArmDiseases: Seq[EligibilityArmDisease])
+
+object EligibilityArmWithDiseases {
+
+ implicit def toPhiString(x: EligibilityArmWithDiseases): PhiString = {
+ import x._
+ phi"EligibilityArmWithDiseases(eligibilityArm=$eligibilityArm, eligibilityArmDiseases=$eligibilityArmDiseases)"
+ }
+}
diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/Intervention.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/Intervention.scala
index 9ccc267..a838d64 100644
--- a/src/main/scala/xyz/driver/pdsuidomain/entities/Intervention.scala
+++ b/src/main/scala/xyz/driver/pdsuidomain/entities/Intervention.scala
@@ -188,7 +188,7 @@ object InterventionType {
}
}
-final case class InterventionArm(armId: LongId[Arm], interventionId: LongId[Intervention])
+final case class InterventionArm(armId: LongId[SlotArm], interventionId: LongId[Intervention])
object InterventionArm {
implicit def toPhiString(x: InterventionArm): PhiString = {
diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/SlotArmEligibilityArm.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/SlotArmEligibilityArm.scala
new file mode 100644
index 0000000..70114de
--- /dev/null
+++ b/src/main/scala/xyz/driver/pdsuidomain/entities/SlotArmEligibilityArm.scala
@@ -0,0 +1,13 @@
+package xyz.driver.pdsuidomain.entities
+
+import xyz.driver.pdsuicommon.domain.LongId
+import xyz.driver.pdsuicommon.logging._
+
+final case class SlotArmEligibilityArm(slotArmId: LongId[SlotArm], eligibilityArmId: LongId[EligibilityArm])
+
+object SlotArmEligibilityArm {
+ implicit def toPhiString(x: SlotArmEligibilityArm): PhiString = {
+ import x._
+ phi"SlotArmEligibilityArm(slotArmId=$slotArmId, eligibilityArmId=$eligibilityArmId)"
+ }
+}
diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/SlotArms.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/SlotArms.scala
new file mode 100644
index 0000000..932de38
--- /dev/null
+++ b/src/main/scala/xyz/driver/pdsuidomain/entities/SlotArms.scala
@@ -0,0 +1,20 @@
+package xyz.driver.pdsuidomain.entities
+
+import java.time.LocalDateTime
+
+import xyz.driver.pdsuicommon.domain.{LongId, StringId}
+import xyz.driver.pdsuicommon.logging._
+
+final case class SlotArm(id: LongId[SlotArm],
+ name: String,
+ originalName: String,
+ trialId: StringId[Trial],
+ deleted: Option[LocalDateTime] = None)
+
+object SlotArm {
+
+ implicit def toPhiString(x: SlotArm): PhiString = {
+ import x._
+ phi"Arm(id=$id, name=${Unsafe(x.name)}, trialId=${Unsafe(x.trialId)})"
+ }
+}
diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/Trial.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/Trial.scala
index 7584e9c..2aa7fe1 100644
--- a/src/main/scala/xyz/driver/pdsuidomain/entities/Trial.scala
+++ b/src/main/scala/xyz/driver/pdsuidomain/entities/Trial.scala
@@ -2,7 +2,6 @@ package xyz.driver.pdsuidomain.entities
import java.time.LocalDateTime
-import xyz.driver.entities.patient.CancerType
import xyz.driver.pdsuicommon.domain.{LongId, StringId, User, UuidId}
import xyz.driver.pdsuicommon.logging._
import xyz.driver.pdsuicommon.utils.Utils
@@ -59,6 +58,24 @@ object Trial {
}
final case class Locations(locations: List[String])
+//
+// sealed trait Condition
+//
+// object Condition {
+//
+// case object Breast extends Condition
+// case object Lung extends Condition
+// case object Prostate extends Condition
+//
+// def fromString(condition: String): Option[Condition] = condition match {
+// case "Breast" => Some(Breast)
+// case "Lung" => Some(Lung)
+// case "Prostate" => Some(Prostate)
+// case _ => None
+// }
+//
+// val All: Set[Condition] = Set(Breast, Lung, Prostate)
+// }
}
final case class Trial(id: StringId[Trial],
@@ -69,7 +86,6 @@ final case class Trial(id: StringId[Trial],
previousAssignee: Option[StringId[User]],
lastActiveUserId: Option[StringId[User]],
lastUpdate: LocalDateTime,
- disease: CancerType,
phase: String,
hypothesisId: Option[UuidId[Hypothesis]],
studyDesignId: Option[LongId[StudyDesign]],
diff --git a/src/main/scala/xyz/driver/pdsuidomain/fakes/entities/trialcuration.scala b/src/main/scala/xyz/driver/pdsuidomain/fakes/entities/trialcuration.scala
index 069d1f1..ade0115 100644
--- a/src/main/scala/xyz/driver/pdsuidomain/fakes/entities/trialcuration.scala
+++ b/src/main/scala/xyz/driver/pdsuidomain/fakes/entities/trialcuration.scala
@@ -1,14 +1,13 @@
package xyz.driver.pdsuidomain.fakes.entities
import xyz.driver.entities.labels.{Label, LabelCategory}
-import xyz.driver.entities.patient.CancerType
import xyz.driver.pdsuicommon.domain.{LongId, User}
import xyz.driver.pdsuidomain.entities._
import xyz.driver.pdsuidomain.services.CriterionService.RichCriterion
object trialcuration {
- import xyz.driver.core.generators
import common._
+ import xyz.driver.core.generators
import xyz.driver.pdsuidomain.entities.InterventionType._
def nextTrial(): Trial = Trial(
@@ -20,7 +19,6 @@ object trialcuration {
previousAssignee = Option(nextStringId[User]),
lastActiveUserId = Option(nextStringId[User]),
lastUpdate = nextLocalDateTime,
- disease = generators.oneOf[CancerType](CancerType.Breast, CancerType.Lung, CancerType.Prostate),
phase = generators.nextString(),
hypothesisId = Option(nextUuidId[Hypothesis]),
studyDesignId = Option(nextLongId[StudyDesign]),
@@ -63,7 +61,7 @@ object trialcuration {
val criterion = nextCriterion()
RichCriterion(
criterion = criterion,
- armIds = Seq(nextLongId[Arm], nextLongId[Arm]),
+ armIds = Seq(nextLongId[EligibilityArm], nextLongId[EligibilityArm]),
labels = Seq(
nextCriterionLabel(criterion.id),
nextCriterionLabel(criterion.id)
@@ -86,7 +84,7 @@ object trialcuration {
def nextInterventionArm(interventionId: LongId[Intervention]): InterventionArm = InterventionArm(
interventionId = interventionId,
- armId = nextLongId[Arm]
+ armId = nextLongId[SlotArm]
)
def nextInterventionWithArms(): InterventionWithArms = {
diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/criterion/ApiCriterion.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/criterion/ApiCriterion.scala
index 43b5494..239adb1 100644
--- a/src/main/scala/xyz/driver/pdsuidomain/formats/json/criterion/ApiCriterion.scala
+++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/criterion/ApiCriterion.scala
@@ -4,7 +4,7 @@ import xyz.driver.pdsuicommon.json.Serialization.seqJsonFormat
import play.api.libs.functional.syntax._
import play.api.libs.json._
import xyz.driver.pdsuicommon.domain.{LongId, StringId}
-import xyz.driver.pdsuidomain.entities.{Arm, Criterion, Trial}
+import xyz.driver.pdsuidomain.entities.{EligibilityArm, Criterion, Trial}
import xyz.driver.pdsuidomain.formats.json.label.ApiCriterionLabel
import xyz.driver.pdsuidomain.services.CriterionService.RichCriterion
@@ -26,7 +26,7 @@ final case class ApiCriterion(id: Long,
meta.getOrElse(""),
inclusion
),
- armIds = arms.map(LongId[Arm]),
+ armIds = arms.map(LongId[EligibilityArm]),
labels = labels.map(_.toDomain(LongId[Criterion](id)))
)
}
diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/criterion/ApiNewCriterion.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/criterion/ApiNewCriterion.scala
index d72a9b4..4b85f83 100644
--- a/src/main/scala/xyz/driver/pdsuidomain/formats/json/criterion/ApiNewCriterion.scala
+++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/criterion/ApiNewCriterion.scala
@@ -25,7 +25,7 @@ final case class ApiNewCriterion(meta: Option[String],
text = text,
inclusion = inclusion
),
- armIds = arms.getOrElse(Seq.empty).map(LongId[Arm]),
+ armIds = arms.getOrElse(Seq.empty).map(LongId[EligibilityArm]),
labels = labels.map(_.toDomain(LongId(Long.MaxValue))) // A developer should specify right criterionId himself
)
}
diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/criterion/ApiUpdateCriterion.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/criterion/ApiUpdateCriterion.scala
index 77989f0..a700309 100644
--- a/src/main/scala/xyz/driver/pdsuidomain/formats/json/criterion/ApiUpdateCriterion.scala
+++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/criterion/ApiUpdateCriterion.scala
@@ -2,7 +2,7 @@ package xyz.driver.pdsuidomain.formats.json.criterion
import xyz.driver.pdsuicommon.domain.LongId
import xyz.driver.pdsuicommon.json.Serialization.seqJsonFormat
-import xyz.driver.pdsuidomain.entities.{Arm, Criterion}
+import xyz.driver.pdsuidomain.entities.{EligibilityArm, Criterion}
import org.davidbild.tristate._
import org.davidbild.tristate.contrib.play.ToJsPathOpsFromJsPath
import play.api.libs.functional.syntax._
@@ -19,7 +19,7 @@ final case class ApiUpdateCriterion(meta: Tristate[String],
def applyTo(orig: RichCriterion): RichCriterion = RichCriterion(
criterion = applyTo(orig.criterion),
- armIds = arms.cata(_.map(LongId[Arm]), Seq.empty, orig.armIds),
+ armIds = arms.cata(_.map(LongId[EligibilityArm]), Seq.empty, orig.armIds),
labels = labels.cata(_.map(_.toDomain(orig.criterion.id)), Seq.empty, orig.labels)
)
diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/eligibilityarm/ApiEligibilityArm.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/eligibilityarm/ApiEligibilityArm.scala
new file mode 100644
index 0000000..71423e8
--- /dev/null
+++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/eligibilityarm/ApiEligibilityArm.scala
@@ -0,0 +1,57 @@
+package xyz.driver.pdsuidomain.formats.json.eligibilityarm
+
+import play.api.libs.functional.syntax._
+import play.api.libs.json._
+import xyz.driver.entities.patient.CancerType
+import xyz.driver.pdsuicommon.domain.{LongId, StringId}
+import xyz.driver.pdsuidomain.entities.{EligibilityArm, EligibilityArmDisease, EligibilityArmWithDiseases}
+
+final case class ApiEligibilityArm(id: Long,
+ name: String,
+ originalName: String,
+ trialId: String,
+ diseases: Seq[String]) {
+
+ def toDomain: EligibilityArmWithDiseases = {
+ val eligibilityArm = EligibilityArm(
+ id = LongId(this.id),
+ name = this.name,
+ originalName = this.originalName,
+ trialId = StringId(this.trialId),
+ deleted = None // if we have an ApiEligibilityArm object, the EligibilityArm itself has not been deleted
+ )
+
+ EligibilityArmWithDiseases(
+ eligibilityArm,
+ this.diseases.map { disease =>
+ val condition = CancerType
+ .fromString(disease)
+ .getOrElse(throw new NoSuchElementException(s"unknown condition $disease"))
+ EligibilityArmDisease(eligibilityArm.id, condition)
+ }
+ )
+ }
+}
+
+object ApiEligibilityArm {
+
+ implicit val format: Format[ApiEligibilityArm] = (
+ (JsPath \ "id").format[Long] and
+ (JsPath \ "name").format[String] and
+ (JsPath \ "originalName").format[String] and
+ (JsPath \ "trialId").format[String] and
+ (JsPath \ "diseases").format[Seq[String]]
+ )(ApiEligibilityArm.apply, unlift(ApiEligibilityArm.unapply))
+
+ def fromDomain(eligibilityArmWithDiseases: EligibilityArmWithDiseases): ApiEligibilityArm = {
+ import eligibilityArmWithDiseases.{eligibilityArm, eligibilityArmDiseases}
+
+ ApiEligibilityArm(
+ id = eligibilityArm.id.id,
+ name = eligibilityArm.name,
+ originalName = eligibilityArm.originalName,
+ trialId = eligibilityArm.trialId.id,
+ diseases = eligibilityArmDiseases.map(_.disease.toString)
+ )
+ }
+}
diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/eligibilityarm/ApiEligibilityCreateArm.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/eligibilityarm/ApiEligibilityCreateArm.scala
new file mode 100644
index 0000000..087fed5
--- /dev/null
+++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/eligibilityarm/ApiEligibilityCreateArm.scala
@@ -0,0 +1,33 @@
+package xyz.driver.pdsuidomain.formats.json.eligibilityarm
+
+import play.api.libs.json.{Format, Json}
+import xyz.driver.entities.patient.CancerType
+import xyz.driver.pdsuicommon.domain.{LongId, StringId}
+import xyz.driver.pdsuidomain.entities.{EligibilityArm, EligibilityArmDisease, EligibilityArmWithDiseases}
+
+final case class ApiCreateEligibilityArm(name: String, trialId: String, diseases: Seq[String]) {
+
+ def toDomain: EligibilityArmWithDiseases = {
+ val eligibilityArm = EligibilityArm(
+ id = LongId(0),
+ name = name,
+ trialId = StringId(trialId),
+ originalName = name
+ )
+
+ EligibilityArmWithDiseases(
+ eligibilityArm,
+ diseases.map { disease =>
+ val condition = CancerType
+ .fromString(disease)
+ .getOrElse(throw new NoSuchElementException(s"unknown condition $disease"))
+ EligibilityArmDisease(eligibilityArm.id, condition)
+ }
+ )
+ }
+}
+
+object ApiCreateEligibilityArm {
+
+ implicit val format: Format[ApiCreateEligibilityArm] = Json.format[ApiCreateEligibilityArm]
+}
diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/eligibilityarm/ApiPartialEligibilityArm.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/eligibilityarm/ApiPartialEligibilityArm.scala
new file mode 100644
index 0000000..aca22ef
--- /dev/null
+++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/eligibilityarm/ApiPartialEligibilityArm.scala
@@ -0,0 +1,23 @@
+package xyz.driver.pdsuidomain.formats.json.eligibilityarm
+
+import play.api.libs.json.{Format, Json}
+import xyz.driver.entities.patient.CancerType
+import xyz.driver.pdsuidomain.entities.{EligibilityArmDisease, EligibilityArmWithDiseases}
+
+final case class ApiPartialEligibilityArm(name: String, diseases: Seq[String]) {
+
+ def applyTo(armWithDisease: EligibilityArmWithDiseases): EligibilityArmWithDiseases = {
+ val arm = armWithDisease.eligibilityArm.copy(name = name)
+ val armDiseases = diseases.map { disease =>
+ EligibilityArmDisease(
+ armWithDisease.eligibilityArm.id,
+ CancerType.fromString(disease).getOrElse(throw new NoSuchElementException(s"unknown condition $disease")))
+ }
+ EligibilityArmWithDiseases(arm, armDiseases)
+ }
+}
+
+object ApiPartialEligibilityArm {
+
+ implicit val format: Format[ApiPartialEligibilityArm] = Json.format
+}
diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/slotarm/ApiPartialSlotArm.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/slotarm/ApiPartialSlotArm.scala
new file mode 100644
index 0000000..2f8e93f
--- /dev/null
+++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/slotarm/ApiPartialSlotArm.scala
@@ -0,0 +1,14 @@
+package xyz.driver.pdsuidomain.formats.json.slotarm
+
+import xyz.driver.pdsuidomain.entities.SlotArm
+import play.api.libs.json.{Format, Json}
+
+final case class ApiPartialSlotArm(name: String) {
+
+ def applyTo(arm: SlotArm): SlotArm = arm.copy(name = name)
+}
+
+object ApiPartialSlotArm {
+
+ implicit val format: Format[ApiPartialSlotArm] = Json.format
+}
diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/slotarm/ApiSlotArm.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/slotarm/ApiSlotArm.scala
new file mode 100644
index 0000000..375c1a2
--- /dev/null
+++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/slotarm/ApiSlotArm.scala
@@ -0,0 +1,35 @@
+package xyz.driver.pdsuidomain.formats.json.slotarm
+
+import xyz.driver.pdsuicommon.domain.{LongId, StringId}
+import xyz.driver.pdsuidomain.entities.SlotArm
+import play.api.libs.functional.syntax._
+import play.api.libs.json._
+
+final case class ApiSlotArm(id: Long, name: String, originalName: String, trialId: String) {
+
+ def toDomain: SlotArm = SlotArm(
+ id = LongId(this.id),
+ name = this.name,
+ originalName = this.originalName,
+ trialId = StringId(this.trialId),
+ deleted = None // if we have an ApiSlotArm object, the SlotArm itself has not been deleted
+ )
+
+}
+
+object ApiSlotArm {
+
+ implicit val format: Format[ApiSlotArm] = (
+ (JsPath \ "id").format[Long] and
+ (JsPath \ "name").format[String] and
+ (JsPath \ "originalName").format[String] and
+ (JsPath \ "trialId").format[String]
+ )(ApiSlotArm.apply, unlift(ApiSlotArm.unapply))
+
+ def fromDomain(arm: SlotArm): ApiSlotArm = ApiSlotArm(
+ id = arm.id.id,
+ name = arm.name,
+ originalName = arm.originalName,
+ trialId = arm.trialId.id
+ )
+}
diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/slotarm/ApiSlotCreateArm.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/slotarm/ApiSlotCreateArm.scala
new file mode 100644
index 0000000..3e52c13
--- /dev/null
+++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/slotarm/ApiSlotCreateArm.scala
@@ -0,0 +1,20 @@
+package xyz.driver.pdsuidomain.formats.json.slotarm
+
+import xyz.driver.pdsuicommon.domain.{LongId, StringId}
+import xyz.driver.pdsuidomain.entities.SlotArm
+import play.api.libs.json.{Format, Json}
+
+final case class ApiCreateSlotArm(name: String, trialId: String) {
+
+ def toDomain = SlotArm(
+ id = LongId(0),
+ name = name,
+ trialId = StringId(trialId),
+ originalName = name
+ )
+}
+
+object ApiCreateSlotArm {
+
+ implicit val format: Format[ApiCreateSlotArm] = Json.format[ApiCreateSlotArm]
+}
diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/criterion.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/criterion.scala
index 89c843f..a43e92a 100644
--- a/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/criterion.scala
+++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/criterion.scala
@@ -80,7 +80,7 @@ object criterion {
val arms = fields
.get("arms")
- .map(_.convertTo[Option[List[LongId[Arm]]]].getOrElse(List.empty[LongId[Arm]]))
+ .map(_.convertTo[Option[List[LongId[EligibilityArm]]]].getOrElse(List.empty[LongId[EligibilityArm]]))
.getOrElse(orig.armIds)
val labels = fields
@@ -143,8 +143,8 @@ object criterion {
val arms = fields
.get("arms")
- .map(_.convertTo[List[LongId[Arm]]])
- .getOrElse(List.empty[LongId[Arm]])
+ .map(_.convertTo[List[LongId[EligibilityArm]]])
+ .getOrElse(List.empty[LongId[EligibilityArm]])
val labels = fields
.get("labels")
diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/intervention.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/intervention.scala
index 62cb9fa..2ced434 100644
--- a/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/intervention.scala
+++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/intervention.scala
@@ -56,7 +56,7 @@ object intervention {
val arms = fields
.get("arms")
- .map(_.convertTo[List[LongId[Arm]]].map(x => InterventionArm(armId = x, interventionId = LongId(0))))
+ .map(_.convertTo[List[LongId[SlotArm]]].map(x => InterventionArm(armId = x, interventionId = LongId(0))))
.getOrElse(List.empty[InterventionArm])
InterventionWithArms(
@@ -104,7 +104,7 @@ object intervention {
val origIntervention = orig.intervention
val arms = fields
.get("arms")
- .map(_.convertTo[List[LongId[Arm]]].map(x => InterventionArm(x, orig.intervention.id)))
+ .map(_.convertTo[List[LongId[SlotArm]]].map(x => InterventionArm(x, orig.intervention.id)))
orig.copy(
intervention = origIntervention.copy(
diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/trial.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/trial.scala
index d851c2c..b25ed1d 100644
--- a/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/trial.scala
+++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/trial.scala
@@ -6,12 +6,11 @@ import spray.json._
import xyz.driver.core.json.EnumJsonFormat
import xyz.driver.pdsuicommon.domain.{LongId, UuidId}
import xyz.driver.pdsuidomain.entities._
-import xyz.driver.formats.json.patient._
object trial {
import DefaultJsonProtocol._
- import common._
import Trial._
+ import common._
implicit val trialStatusFormat = new EnumJsonFormat[Status](
"New" -> Status.New,
@@ -36,7 +35,6 @@ object trial {
"previousStatus" -> obj.previousStatus.toJson,
"previousAssignee" -> obj.previousAssignee.toJson,
"lastActiveUser" -> obj.lastActiveUserId.toJson,
- "disease" -> obj.disease.toJson,
"phase" -> obj.phase.toJson,
"hypothesisId" -> obj.hypothesisId.toJson,
"studyDesignId" -> obj.studyDesignId.toJson,
diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/trial/ApiTrial.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/trial/ApiTrial.scala
index a2a1ca9..133a2c8 100644
--- a/src/main/scala/xyz/driver/pdsuidomain/formats/json/trial/ApiTrial.scala
+++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/trial/ApiTrial.scala
@@ -3,11 +3,10 @@ package xyz.driver.pdsuidomain.formats.json.trial
import java.time.{ZoneId, ZonedDateTime}
import java.util.UUID
-import xyz.driver.pdsuicommon.domain.{LongId, StringId, UuidId}
-import xyz.driver.pdsuidomain.entities.Trial
import play.api.libs.functional.syntax._
import play.api.libs.json._
-import xyz.driver.entities.patient.CancerType
+import xyz.driver.pdsuicommon.domain.{LongId, StringId, UuidId}
+import xyz.driver.pdsuidomain.entities.Trial
final case class ApiTrial(id: String,
externalId: UUID,
@@ -17,7 +16,6 @@ final case class ApiTrial(id: String,
previousStatus: Option[String],
previousAssignee: Option[String],
lastActiveUser: Option[String],
- disease: String,
phase: String,
hypothesisId: Option[UUID],
studyDesignId: Option[Long],
@@ -38,9 +36,6 @@ final case class ApiTrial(id: String,
previousAssignee = this.previousAssignee.map(id => StringId(id)),
lastActiveUserId = this.lastActiveUser.map(id => StringId(id)),
lastUpdate = this.lastUpdate.toLocalDateTime,
- disease = CancerType
- .fromString(this.disease)
- .getOrElse(throw new NoSuchElementException(s"Unknown disease ${this.disease}")),
phase = this.phase,
hypothesisId = this.hypothesisId.map(id => UuidId(id)),
studyDesignId = this.studyDesignId.map(id => LongId(id)),
@@ -66,7 +61,6 @@ object ApiTrial {
(JsPath \ "previousStatus").formatNullable[String] and
(JsPath \ "previousAssignee").formatNullable[String] and
(JsPath \ "lastActiveUser").formatNullable[String] and
- (JsPath \ "disease").format[String] and
(JsPath \ "phase").format[String] and
(JsPath \ "hypothesisId").formatNullable[UUID] and
(JsPath \ "studyDesignId").formatNullable[Long] and
@@ -88,7 +82,6 @@ object ApiTrial {
previousAssignee = trial.previousAssignee.map(_.id),
lastActiveUser = trial.lastActiveUserId.map(_.id),
lastUpdate = ZonedDateTime.of(trial.lastUpdate, ZoneId.of("Z")),
- disease = trial.disease.toString,
phase = trial.phase,
hypothesisId = trial.hypothesisId.map(_.id),
studyDesignId = trial.studyDesignId.map(_.id),
diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/CriterionService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/CriterionService.scala
index fd9268b..7dbf0d9 100644
--- a/src/main/scala/xyz/driver/pdsuidomain/services/CriterionService.scala
+++ b/src/main/scala/xyz/driver/pdsuidomain/services/CriterionService.scala
@@ -21,7 +21,9 @@ object CriterionService {
def userMessage: String = "Access denied"
}
- final case class RichCriterion(criterion: Criterion, armIds: Seq[LongId[Arm]], labels: Seq[CriterionLabel])
+ final case class RichCriterion(criterion: Criterion,
+ armIds: Seq[LongId[EligibilityArm]],
+ labels: Seq[CriterionLabel])
object RichCriterion {
implicit def toPhiString(x: RichCriterion): PhiString = {
import x._
diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/EligibilityArmService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/EligibilityArmService.scala
new file mode 100644
index 0000000..1e0f65e
--- /dev/null
+++ b/src/main/scala/xyz/driver/pdsuidomain/services/EligibilityArmService.scala
@@ -0,0 +1,139 @@
+package xyz.driver.pdsuidomain.services
+
+import xyz.driver.pdsuicommon.auth.AuthenticatedRequestContext
+import xyz.driver.pdsuicommon.db._
+import xyz.driver.pdsuicommon.domain.LongId
+import xyz.driver.pdsuicommon.error.DomainError
+import xyz.driver.pdsuicommon.logging._
+import xyz.driver.pdsuidomain.entities.{EligibilityArm, EligibilityArmWithDiseases, SlotArm}
+
+import scala.concurrent.Future
+
+object EligibilityArmService {
+
+ trait DefaultAccessDeniedError {
+ def userMessage: String = "Access denied"
+ }
+
+ trait DefaultNotFoundError {
+ def userMessage: String = "EligibilityArm not found"
+ }
+
+ trait SlotArmNotFoundError {
+ def userMessage: String = "SlotArm not found"
+ }
+
+ sealed trait GetByIdReply
+ object GetByIdReply {
+
+ final case class Entity(x: EligibilityArmWithDiseases) extends GetByIdReply
+
+ type Error = GetByIdReply with DomainError
+
+ case object NotFoundError extends GetByIdReply with DefaultNotFoundError with DomainError.NotFoundError
+
+ case object AuthorizationError
+ extends GetByIdReply with DomainError.AuthorizationError with DefaultAccessDeniedError
+
+ final case class CommonError(userMessage: String)(implicit requestContext: AuthenticatedRequestContext)
+ extends GetByIdReply with DomainError
+ }
+
+ sealed trait GetListReply
+ object GetListReply {
+ type Error = GetListReply with DomainError
+
+ final case class EntityList(xs: Seq[EligibilityArmWithDiseases], totalFound: Int) extends GetListReply
+
+ case object AuthorizationError
+ extends GetListReply with DomainError.AuthorizationError with DefaultAccessDeniedError
+ }
+
+ sealed trait UpdateReply
+ object UpdateReply {
+
+ final case class Updated(updated: EligibilityArmWithDiseases) extends UpdateReply
+
+ type Error = UpdateReply with DomainError
+
+ case object NotFoundError extends UpdateReply with DefaultNotFoundError with DomainError.NotFoundError
+
+ case object AuthorizationError
+ extends UpdateReply with DefaultAccessDeniedError with DomainError.AuthorizationError
+
+ final case class CommonError(userMessage: String) extends UpdateReply with DomainError
+
+ final case class AlreadyExistsError(x: EligibilityArmWithDiseases) extends UpdateReply with DomainError {
+ val userMessage = s"The arm with such name of trial already exists."
+ }
+
+ implicit def toPhiString(reply: UpdateReply): PhiString = reply match {
+ case Updated(x) => phi"Updated($x)"
+ case x: Error => DomainError.toPhiString(x)
+ }
+ }
+
+ sealed trait CreateReply
+ object CreateReply {
+ final case class Created(x: EligibilityArmWithDiseases) extends CreateReply
+
+ type Error = CreateReply with DomainError
+
+ case object AuthorizationError
+ extends CreateReply with DefaultAccessDeniedError with DomainError.AuthorizationError
+
+ case object NotFoundError extends CreateReply with SlotArmNotFoundError with DomainError.NotFoundError
+
+ final case class CommonError(userMessage: String) extends CreateReply with DomainError
+
+ final case class AlreadyExistsError(x: EligibilityArmWithDiseases) extends CreateReply with DomainError {
+ val userMessage = s"The arm with this name of trial already exists."
+ }
+
+ implicit def toPhiString(reply: CreateReply): PhiString = reply match {
+ case Created(x) => phi"Created($x)"
+ case x: Error => DomainError.toPhiString(x)
+ }
+ }
+
+ sealed trait DeleteReply
+ object DeleteReply {
+ case object Deleted extends DeleteReply
+
+ type Error = DeleteReply with DomainError
+
+ case object NotFoundError extends DeleteReply with DefaultNotFoundError with DomainError.NotFoundError
+
+ case object AuthorizationError
+ extends DeleteReply with DefaultAccessDeniedError with DomainError.AuthorizationError
+
+ final case class CommonError(userMessage: String) extends DeleteReply with DomainError
+ }
+}
+
+trait EligibilityArmService {
+
+ import EligibilityArmService._
+
+ def getAll(filter: SearchFilterExpr = SearchFilterExpr.Empty,
+ sorting: Option[Sorting] = None,
+ pagination: Option[Pagination] = None)(
+ implicit requestContext: AuthenticatedRequestContext): Future[GetListReply]
+
+ def getByEligibilityId(armId: LongId[EligibilityArm])(
+ implicit requestContext: AuthenticatedRequestContext): Future[GetByIdReply]
+
+ def getBySlotId(armId: LongId[SlotArm],
+ filter: SearchFilterExpr = SearchFilterExpr.Empty,
+ sorting: Option[Sorting] = None,
+ pagination: Option[Pagination] = None)(
+ implicit requestContext: AuthenticatedRequestContext): Future[GetListReply]
+
+ def create(slotArmId: LongId[SlotArm], draftEligibilityArm: EligibilityArmWithDiseases)(
+ implicit requestContext: AuthenticatedRequestContext): Future[CreateReply]
+
+ def update(origEligibilityArm: EligibilityArmWithDiseases, draftEligibilityArm: EligibilityArmWithDiseases)(
+ implicit requestContext: AuthenticatedRequestContext): Future[UpdateReply]
+
+ def delete(id: LongId[EligibilityArm])(implicit requestContext: AuthenticatedRequestContext): Future[DeleteReply]
+}
diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/SlotArmService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/SlotArmService.scala
new file mode 100644
index 0000000..af7084b
--- /dev/null
+++ b/src/main/scala/xyz/driver/pdsuidomain/services/SlotArmService.scala
@@ -0,0 +1,125 @@
+package xyz.driver.pdsuidomain.services
+
+import xyz.driver.pdsuicommon.auth.AuthenticatedRequestContext
+import xyz.driver.pdsuicommon.db._
+import xyz.driver.pdsuicommon.domain.LongId
+import xyz.driver.pdsuicommon.error.DomainError
+import xyz.driver.pdsuicommon.logging._
+import xyz.driver.pdsuidomain.entities.SlotArm
+
+import scala.concurrent.Future
+
+object SlotArmService {
+
+ trait DefaultAccessDeniedError {
+ def userMessage: String = "Access denied"
+ }
+
+ trait DefaultNotFoundError {
+ def userMessage: String = "SlotArm not found"
+ }
+
+ sealed trait GetByIdReply
+ object GetByIdReply {
+
+ final case class Entity(x: SlotArm) extends GetByIdReply
+
+ type Error = GetByIdReply with DomainError
+
+ case object NotFoundError extends GetByIdReply with DefaultNotFoundError with DomainError.NotFoundError
+
+ case object AuthorizationError
+ extends GetByIdReply with DomainError.AuthorizationError with DefaultAccessDeniedError
+
+ final case class CommonError(userMessage: String)(implicit requestContext: AuthenticatedRequestContext)
+ extends GetByIdReply with DomainError
+ }
+
+ sealed trait GetListReply
+ object GetListReply {
+ type Error = GetListReply with DomainError
+
+ final case class EntityList(xs: Seq[SlotArm], totalFound: Int) extends GetListReply
+
+ case object AuthorizationError
+ extends GetListReply with DomainError.AuthorizationError with DefaultAccessDeniedError
+ }
+
+ sealed trait UpdateReply
+ object UpdateReply {
+
+ final case class Updated(updated: SlotArm) extends UpdateReply
+
+ type Error = UpdateReply with DomainError
+
+ case object NotFoundError extends UpdateReply with DefaultNotFoundError with DomainError.NotFoundError
+
+ case object AuthorizationError
+ extends UpdateReply with DefaultAccessDeniedError with DomainError.AuthorizationError
+
+ final case class CommonError(userMessage: String) extends UpdateReply with DomainError
+
+ final case class AlreadyExistsError(x: SlotArm) extends UpdateReply with DomainError {
+ val userMessage = s"The arm with such name of trial already exists."
+ }
+
+ implicit def toPhiString(reply: UpdateReply): PhiString = reply match {
+ case Updated(x) => phi"Updated($x)"
+ case x: Error => DomainError.toPhiString(x)
+ }
+ }
+
+ sealed trait CreateReply
+ object CreateReply {
+ final case class Created(x: SlotArm) extends CreateReply
+
+ type Error = CreateReply with DomainError
+
+ case object AuthorizationError
+ extends CreateReply with DefaultAccessDeniedError with DomainError.AuthorizationError
+
+ final case class CommonError(userMessage: String) extends CreateReply with DomainError
+
+ final case class AlreadyExistsError(x: SlotArm) extends CreateReply with DomainError {
+ val userMessage = s"The arm with this name of trial already exists."
+ }
+
+ implicit def toPhiString(reply: CreateReply): PhiString = reply match {
+ case Created(x) => phi"Created($x)"
+ case x: Error => DomainError.toPhiString(x)
+ }
+ }
+
+ sealed trait DeleteReply
+ object DeleteReply {
+ case object Deleted extends DeleteReply
+
+ type Error = DeleteReply with DomainError
+
+ case object NotFoundError extends DeleteReply with DefaultNotFoundError with DomainError.NotFoundError
+
+ case object AuthorizationError
+ extends DeleteReply with DefaultAccessDeniedError with DomainError.AuthorizationError
+
+ final case class CommonError(userMessage: String) extends DeleteReply with DomainError
+ }
+}
+
+trait SlotArmService {
+
+ import SlotArmService._
+
+ def getAll(filter: SearchFilterExpr = SearchFilterExpr.Empty,
+ sorting: Option[Sorting] = None,
+ pagination: Option[Pagination] = None)(
+ implicit requestContext: AuthenticatedRequestContext): Future[GetListReply]
+
+ def getById(armId: LongId[SlotArm])(implicit requestContext: AuthenticatedRequestContext): Future[GetByIdReply]
+
+ def create(draftSlotArm: SlotArm)(implicit requestContext: AuthenticatedRequestContext): Future[CreateReply]
+
+ def update(origSlotArm: SlotArm, draftSlotArm: SlotArm)(
+ implicit requestContext: AuthenticatedRequestContext): Future[UpdateReply]
+
+ def delete(id: LongId[SlotArm])(implicit requestContext: AuthenticatedRequestContext): Future[DeleteReply]
+}
diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/fake/FakeTrialService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/fake/FakeTrialService.scala
index a1efdac..cb7f8bc 100644
--- a/src/main/scala/xyz/driver/pdsuidomain/services/fake/FakeTrialService.scala
+++ b/src/main/scala/xyz/driver/pdsuidomain/services/fake/FakeTrialService.scala
@@ -11,8 +11,8 @@ import xyz.driver.entities.patient.CancerType
import xyz.driver.pdsuicommon.auth.AuthenticatedRequestContext
import xyz.driver.pdsuicommon.db._
import xyz.driver.pdsuicommon.domain.{LongId, StringId, UuidId}
-import xyz.driver.pdsuidomain.entities.{Arm, Criterion, Trial}
import xyz.driver.pdsuidomain.entities.export.trial.{ExportTrialArm, ExportTrialLabelCriterion, ExportTrialWithLabels}
+import xyz.driver.pdsuidomain.entities.{Arm, Criterion, Trial}
import xyz.driver.pdsuidomain.services.TrialService
import scala.concurrent.Future
@@ -30,7 +30,6 @@ class FakeTrialService extends TrialService {
previousAssignee = None,
lastActiveUserId = None,
lastUpdate = LocalDateTime.now(),
- disease = CancerType.Breast,
phase = "",
hypothesisId = None,
studyDesignId = None,