aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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.scala15
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/entities/SlotArms.scala20
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/entities/Trial.scala3
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/fakes/entities/trialcuration.scala5
-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.scala48
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/formats/json/eligibilityarm/ApiEligibilityCreateArm.scala28
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/formats/json/eligibilityarm/ApiPartialEligibilityArm.scala22
-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.scala1
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/formats/json/trial/ApiTrial.scala8
-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.scala1
-rw-r--r--src/test/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/CriterionFormatSuite.scala4
-rw-r--r--src/test/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/PatientEligibleTrialFormatSuite.scala1
-rw-r--r--src/test/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/TrialFormatSuite.scala3
27 files changed, 529 insertions, 33 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..329c15c
--- /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.pdsuicommon.domain.{LongId, StringId}
+import xyz.driver.pdsuicommon.logging._
+import xyz.driver.pdsuidomain.entities.Trial.Condition
+
+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: Condition)
+
+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..f9aed6c
--- /dev/null
+++ b/src/main/scala/xyz/driver/pdsuidomain/entities/SlotArmEligibilityArm.scala
@@ -0,0 +1,15 @@
+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 db4def2..bd86057 100644
--- a/src/main/scala/xyz/driver/pdsuidomain/entities/Trial.scala
+++ b/src/main/scala/xyz/driver/pdsuidomain/entities/Trial.scala
@@ -5,7 +5,7 @@ import java.time.LocalDateTime
import xyz.driver.pdsuicommon.domain.{LongId, StringId, User, UuidId}
import xyz.driver.pdsuicommon.logging._
import xyz.driver.pdsuicommon.utils.Utils
-import xyz.driver.pdsuidomain.entities.Trial.{Condition, Status}
+import xyz.driver.pdsuidomain.entities.Trial.Status
final case class StudyDesign(id: LongId[StudyDesign], name: String)
@@ -86,7 +86,6 @@ final case class Trial(id: StringId[Trial],
previousAssignee: Option[StringId[User]],
lastActiveUserId: Option[StringId[User]],
lastUpdate: LocalDateTime,
- condition: Condition,
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 9a5022c..86ea765 100644
--- a/src/main/scala/xyz/driver/pdsuidomain/fakes/entities/trialcuration.scala
+++ b/src/main/scala/xyz/driver/pdsuidomain/fakes/entities/trialcuration.scala
@@ -19,7 +19,6 @@ object trialcuration {
previousAssignee = Option(nextStringId[User]),
lastActiveUserId = Option(nextStringId[User]),
lastUpdate = nextLocalDateTime,
- condition = nextCondition,
phase = generators.nextString(),
hypothesisId = Option(nextUuidId[Hypothesis]),
studyDesignId = Option(nextLongId[StudyDesign]),
@@ -62,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)
@@ -85,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..4d6d0a2
--- /dev/null
+++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/eligibilityarm/ApiEligibilityArm.scala
@@ -0,0 +1,48 @@
+package xyz.driver.pdsuidomain.formats.json.eligibilityarm
+
+import play.api.libs.functional.syntax._
+import play.api.libs.json._
+import xyz.driver.pdsuicommon.domain.{LongId, StringId}
+import xyz.driver.pdsuidomain.entities.{EligibilityArm, EligibilityArmDisease, EligibilityArmWithDiseases, Trial}
+
+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 = Trial.Condition.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..6cf71a2
--- /dev/null
+++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/eligibilityarm/ApiEligibilityCreateArm.scala
@@ -0,0 +1,28 @@
+package xyz.driver.pdsuidomain.formats.json.eligibilityarm
+
+import xyz.driver.pdsuicommon.domain.{LongId, StringId}
+import xyz.driver.pdsuidomain.entities.{EligibilityArm, EligibilityArmDisease, EligibilityArmWithDiseases, Trial}
+import play.api.libs.json.{Format, Json}
+
+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 = Trial.Condition.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..3d9445f
--- /dev/null
+++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/eligibilityarm/ApiPartialEligibilityArm.scala
@@ -0,0 +1,22 @@
+package xyz.driver.pdsuidomain.formats.json.eligibilityarm
+
+import play.api.libs.json.{Format, Json}
+import xyz.driver.pdsuidomain.entities.{EligibilityArmDisease, EligibilityArmWithDiseases, Trial}
+
+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,
+ Trial.Condition.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 8000a27..aeec90f 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
@@ -41,7 +41,6 @@ object trial {
"previousStatus" -> obj.previousStatus.toJson,
"previousAssignee" -> obj.previousAssignee.toJson,
"lastActiveUser" -> obj.lastActiveUserId.toJson,
- "condition" -> obj.condition.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 1b4ac64..fbf506b 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
@@ -16,7 +16,6 @@ final case class ApiTrial(id: String,
previousStatus: Option[String],
previousAssignee: Option[String],
lastActiveUser: Option[String],
- condition: String,
phase: String,
hypothesisId: Option[UUID],
studyDesignId: Option[Long],
@@ -37,11 +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,
- condition = Trial.Condition
- .fromString(this.condition)
- .getOrElse(
- throw new NoSuchElementException(s"unknown condition ${this.condition}")
- ),
phase = this.phase,
hypothesisId = this.hypothesisId.map(id => UuidId(id)),
studyDesignId = this.studyDesignId.map(id => LongId(id)),
@@ -67,7 +61,6 @@ object ApiTrial {
(JsPath \ "previousStatus").formatNullable[String] and
(JsPath \ "previousAssignee").formatNullable[String] and
(JsPath \ "lastActiveUser").formatNullable[String] and
- (JsPath \ "condition").format[String] and
(JsPath \ "phase").format[String] and
(JsPath \ "hypothesisId").formatNullable[UUID] and
(JsPath \ "studyDesignId").formatNullable[Long] and
@@ -89,7 +82,6 @@ object ApiTrial {
previousAssignee = trial.previousAssignee.map(_.id),
lastActiveUser = trial.lastActiveUserId.map(_.id),
lastUpdate = ZonedDateTime.of(trial.lastUpdate, ZoneId.of("Z")),
- condition = trial.condition.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..e250e8f
--- /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 cd62d48..59f300b 100644
--- a/src/main/scala/xyz/driver/pdsuidomain/services/fake/FakeTrialService.scala
+++ b/src/main/scala/xyz/driver/pdsuidomain/services/fake/FakeTrialService.scala
@@ -29,7 +29,6 @@ class FakeTrialService extends TrialService {
previousAssignee = None,
lastActiveUserId = None,
lastUpdate = LocalDateTime.now(),
- condition = Trial.Condition.Breast,
phase = "",
hypothesisId = None,
studyDesignId = None,
diff --git a/src/test/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/CriterionFormatSuite.scala b/src/test/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/CriterionFormatSuite.scala
index 708aed9..51665d2 100644
--- a/src/test/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/CriterionFormatSuite.scala
+++ b/src/test/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/CriterionFormatSuite.scala
@@ -3,7 +3,7 @@ package xyz.driver.pdsuidomain.formats.json.sprayformats
import spray.json._
import org.scalatest.{FlatSpec, Matchers}
import xyz.driver.pdsuicommon.domain.{LongId, StringId}
-import xyz.driver.pdsuidomain.entities.{Arm, Criterion, CriterionLabel}
+import xyz.driver.pdsuidomain.entities.{EligibilityArm, Criterion, CriterionLabel}
import xyz.driver.pdsuidomain.services.CriterionService.RichCriterion
class CriterionFormatSuite extends FlatSpec with Matchers {
@@ -36,7 +36,7 @@ class CriterionFormatSuite extends FlatSpec with Matchers {
isDefining = true
)
)
- val arms = List(LongId[Arm](20), LongId[Arm](21), LongId[Arm](21))
+ val arms = List(LongId[EligibilityArm](20), LongId[EligibilityArm](21), LongId[EligibilityArm](21))
val richCriterion = RichCriterion(
criterion = criterion,
armIds = arms,
diff --git a/src/test/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/PatientEligibleTrialFormatSuite.scala b/src/test/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/PatientEligibleTrialFormatSuite.scala
index ad54946..8b00883 100644
--- a/src/test/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/PatientEligibleTrialFormatSuite.scala
+++ b/src/test/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/PatientEligibleTrialFormatSuite.scala
@@ -21,7 +21,6 @@ class PatientEligibleTrialFormatSuite extends FlatSpec with Matchers {
previousAssignee = None,
lastActiveUserId = None,
lastUpdate = LocalDateTime.parse("2017-08-10T18:16:19"),
- condition = Trial.Condition.Breast,
phase = "",
hypothesisId = Some(UuidId("e76e2fc4-a29c-44fb-a81b-8856d06bb1d4")),
studyDesignId = Some(LongId(321)),
diff --git a/src/test/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/TrialFormatSuite.scala b/src/test/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/TrialFormatSuite.scala
index 59cf779..3533c07 100644
--- a/src/test/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/TrialFormatSuite.scala
+++ b/src/test/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/TrialFormatSuite.scala
@@ -20,7 +20,6 @@ class TrialFormatSuite extends FlatSpec with Matchers {
previousAssignee = None,
lastActiveUserId = None,
lastUpdate = LocalDateTime.parse("2017-08-10T18:16:19"),
- condition = Trial.Condition.Breast,
phase = "",
hypothesisId = Some(UuidId("3b80b2e2-5372-4cf5-a342-6e4ebe10fafd")),
studyDesignId = Some(LongId(321)),
@@ -39,7 +38,7 @@ class TrialFormatSuite extends FlatSpec with Matchers {
"isUpdated":false,"overviewTemplate":"","phase":"","originalStudyDesignId":null,
"hypothesisId":"3b80b2e2-5372-4cf5-a342-6e4ebe10fafd","originalTitle":"orig trial title",
"studyDesignId":321,"lastActiveUser":null,"externalid":"40892a07-c638-49d2-9795-1edfefbbcc7c",
- "id":"NCT000001","condition":"Breast","status":"New","overview":null,"previousAssignee":null,"title":"trial title"}""".parseJson)
+ "id":"NCT000001","status":"New","overview":null,"previousAssignee":null,"title":"trial title"}""".parseJson)
val updateTrialJson = """{"hypothesisId":null,"overview":"new overview"}""".parseJson
val expectedUpdatedTrial = orig.copy(hypothesisId = None, overview = Some("new overview"))