From 78f467dd0b3b9f35bb49ae973b26fb37c458b138 Mon Sep 17 00:00:00 2001 From: vlad Date: Mon, 30 Oct 2017 14:21:55 -0700 Subject: Fixing Patients and Trials parsing, some refined to try --- .../pdsuidomain/entities/PatientHypothesis.scala | 4 +- .../driver/pdsuidomain/entities/PatientIssue.scala | 4 +- .../fakes/entities/treatmentmatching.scala | 9 +- .../formats/json/document/ApiPartialDocument.scala | 1 - .../driver/pdsuidomain/formats/json/patient.scala | 80 ++++++++++++++++- .../json/patient/trial/ApiPatientCriterion.scala | 1 - .../formats/json/patienthypothesis.scala | 1 + .../pdsuidomain/formats/json/patientissue.scala | 9 +- .../driver/pdsuidomain/formats/json/trial.scala | 100 ++++++++++++++++++++- .../json/PatientHypothesisFormatSuite.scala | 6 +- .../formats/json/PatientIssueFormatSuite.scala | 6 +- 11 files changed, 201 insertions(+), 20 deletions(-) delete mode 100644 src/main/scala/xyz/driver/pdsuidomain/formats/json/document/ApiPartialDocument.scala delete mode 100644 src/main/scala/xyz/driver/pdsuidomain/formats/json/patient/trial/ApiPatientCriterion.scala diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/PatientHypothesis.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/PatientHypothesis.scala index 23bb546..7943fb9 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/entities/PatientHypothesis.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/PatientHypothesis.scala @@ -1,5 +1,7 @@ package xyz.driver.pdsuidomain.entities +import eu.timepit.refined.api.Refined +import eu.timepit.refined.numeric.NonNegative import xyz.driver.pdsuicommon.domain.UuidId import xyz.driver.pdsuicommon.logging._ @@ -15,4 +17,4 @@ final case class PatientHypothesis(id: UuidId[PatientHypothesis], patientId: UuidId[Patient], hypothesisId: UuidId[Hypothesis], rationale: Option[String], - matchedTrials: Long) + matchedTrials: Long Refined NonNegative) diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/PatientIssue.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/PatientIssue.scala index f616754..7f17419 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/entities/PatientIssue.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/PatientIssue.scala @@ -2,6 +2,8 @@ package xyz.driver.pdsuidomain.entities import java.time.LocalDateTime +import eu.timepit.refined.api.Refined +import eu.timepit.refined.collection.NonEmpty import xyz.driver.pdsuicommon.domain._ import xyz.driver.pdsuicommon.logging._ @@ -10,7 +12,7 @@ final case class PatientIssue(id: LongId[PatientIssue], patientId: UuidId[Patient], lastUpdate: LocalDateTime, isDraft: Boolean, - text: String, + text: String Refined NonEmpty, archiveRequired: Boolean) object PatientIssue { diff --git a/src/main/scala/xyz/driver/pdsuidomain/fakes/entities/treatmentmatching.scala b/src/main/scala/xyz/driver/pdsuidomain/fakes/entities/treatmentmatching.scala index 94c41ff..2780526 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/fakes/entities/treatmentmatching.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/fakes/entities/treatmentmatching.scala @@ -1,5 +1,6 @@ package xyz.driver.pdsuidomain.fakes.entities +import eu.timepit.refined.numeric.NonNegative import xyz.driver.entities.labels.Label import xyz.driver.fakes import xyz.driver.pdsuicommon.domain.{LongId, StringId, User} @@ -9,6 +10,7 @@ import xyz.driver.pdsuidomain.services.PatientCriterionService.{DraftPatientCrit import xyz.driver.pdsuidomain.services.PatientEligibleTrialService.RichPatientEligibleTrial import xyz.driver.pdsuidomain.services.PatientHypothesisService.RichPatientHypothesis import xyz.driver.pdsuidomain.services.PatientLabelService.RichPatientLabel +import eu.timepit.refined.{refineV, refineMV} object treatmentmatching { import common._ @@ -163,7 +165,10 @@ object treatmentmatching { patientId = nextUuidId[Patient], hypothesisId = nextUuidId[Hypothesis], rationale = Option(generators.nextString()), - matchedTrials = generators.nextInt(Int.MaxValue).toLong + matchedTrials = refineV[NonNegative](generators.nextInt(Int.MaxValue).toLong) match { + case Left(_) => refineMV[NonNegative](0) + case Right(nonNegative) => nonNegative + } ) def nextRichPatientHypothesis(): RichPatientHypothesis = RichPatientHypothesis( @@ -177,7 +182,7 @@ object treatmentmatching { patientId = nextUuidId[Patient], lastUpdate = nextLocalDateTime, isDraft = generators.nextBoolean(), - text = generators.nextString(), + text = generators.nextNonEmptyString(), archiveRequired = generators.nextBoolean() ) diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/document/ApiPartialDocument.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/document/ApiPartialDocument.scala deleted file mode 100644 index 8b13789..0000000 --- a/src/main/scala/xyz/driver/pdsuidomain/formats/json/document/ApiPartialDocument.scala +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/patient.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/patient.scala index 75eb016..0830723 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/formats/json/patient.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/patient.scala @@ -1,9 +1,14 @@ package xyz.driver.pdsuidomain.formats.json +import java.time.{LocalDate, LocalDateTime} + import spray.json._ import xyz.driver.core.json.EnumJsonFormat +import xyz.driver.entities.common.FullName +import xyz.driver.entities.patient.CancerType import xyz.driver.formats.json.common._ import xyz.driver.formats.json.patient._ +import xyz.driver.pdsuicommon.domain.{StringId, User, UuidId} import xyz.driver.pdsuidomain.entities._ object patient { @@ -11,7 +16,7 @@ object patient { import Patient._ import common._ - implicit val patientStatusFormat = new EnumJsonFormat[Status]( + implicit val patientStatusFormat: RootJsonFormat[Status] = new EnumJsonFormat[Status]( "New" -> Status.New, "Verified" -> Status.Verified, "Reviewed" -> Status.Reviewed, @@ -20,7 +25,7 @@ object patient { "Flagged" -> Status.Flagged ) - implicit val patientOrderIdFormat = new RootJsonFormat[PatientOrderId] { + implicit val patientOrderIdFormat: RootJsonFormat[PatientOrderId] = new RootJsonFormat[PatientOrderId] { override def write(orderId: PatientOrderId): JsString = JsString(orderId.toString) override def read(json: JsValue): PatientOrderId = json match { case JsString(value) => PatientOrderId(value) @@ -44,7 +49,76 @@ object patient { "orderId" -> patient.orderId.toJson ) - override def read(json: JsValue): Patient = jsonReader[Patient].read(json) + override def read(json: JsValue): Patient = { + json match { + case JsObject(fields) => + val id = fields + .get("id") + .map(_.convertTo[UuidId[Patient]]) + .getOrElse(deserializationError(s"Patient create json object does not contain `id` field: $json")) + + val status = fields + .get("status") + .map(_.convertTo[Patient.Status]) + .getOrElse(deserializationError(s"Patient create json object does not contain `status` field: $json")) + + val name = fields + .get("name") + .map(_.convertTo[FullName[Patient]]) + .getOrElse(deserializationError(s"Patient create json object does not contain `name` field: $json")) + + val dob = fields + .get("dob") + .map(_.convertTo[LocalDate]) + .getOrElse(deserializationError(s"Patient create json object does not contain `dob` field: $json")) + + val assignee = fields + .get("assignee") + .flatMap(_.convertTo[Option[StringId[User]]]) + + val previousStatus = fields + .get("previousStatus") + .flatMap(_.convertTo[Option[Patient.Status]]) + + val previousAssignee = fields + .get("previousAssignee") + .flatMap(_.convertTo[Option[StringId[User]]]) + + val lastActiveUser = fields + .get("lastActiveUser") + .flatMap(_.convertTo[Option[StringId[User]]]) + + val disease = fields + .get("disease") + .map(_.convertTo[CancerType]) + .getOrElse(deserializationError(s"Patient create json object does not contain `disease` field: $json")) + + val orderId = fields + .get("orderId") + .map(_.convertTo[PatientOrderId]) + .getOrElse(deserializationError(s"Patient create json object does not contain `orderId` field: $json")) + + val lastUpdate = fields + .get("lastUpdate") + .map(_.convertTo[LocalDateTime]) + .getOrElse(deserializationError(s"Patient create json object does not contain `lastUpdate` field: $json")) + + Patient(id, + status, + name, + dob, + assignee, + previousStatus, + previousAssignee, + lastActiveUser, + isUpdateRequired = false, + disease, + orderId, + lastUpdate) + + case _ => deserializationError(s"Expected Json Object as Trial, but got $json") + } + } } } diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/patient/trial/ApiPatientCriterion.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/patient/trial/ApiPatientCriterion.scala deleted file mode 100644 index 8b13789..0000000 --- a/src/main/scala/xyz/driver/pdsuidomain/formats/json/patient/trial/ApiPatientCriterion.scala +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/patienthypothesis.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/patienthypothesis.scala index a36bac2..a459158 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/formats/json/patienthypothesis.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/patienthypothesis.scala @@ -7,6 +7,7 @@ import xyz.driver.pdsuidomain.services.PatientHypothesisService.RichPatientHypot object patienthypothesis { import DefaultJsonProtocol._ import common._ + import xyz.driver.core.json._ def applyUpdateToPatientHypothesis(json: JsValue, orig: PatientHypothesis): PatientHypothesis = json match { case JsObject(fields) => diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/patientissue.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/patientissue.scala index 5a2955f..bf1c0fd 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/formats/json/patientissue.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/patientissue.scala @@ -2,6 +2,8 @@ package xyz.driver.pdsuidomain.formats.json import java.time.LocalDateTime +import eu.timepit.refined.api.Refined +import eu.timepit.refined.collection.NonEmpty import spray.json._ import xyz.driver.pdsuicommon.domain.{LongId, StringId, User, UuidId} import xyz.driver.pdsuidomain.entities._ @@ -9,12 +11,13 @@ import xyz.driver.pdsuidomain.entities._ object patientissue { import DefaultJsonProtocol._ import common._ + import xyz.driver.core.json._ def applyUpdateToPatientIssue(json: JsValue, orig: PatientIssue): PatientIssue = { json.asJsObject.getFields("text", "archiveRequired") match { case Seq(text, archiveRequired) => orig.copy( - text = text.convertTo[String], + text = text.convertTo[String Refined NonEmpty], archiveRequired = archiveRequired.convertTo[Boolean] ) @@ -31,7 +34,7 @@ object patientissue { patientId = patientId, lastUpdate = LocalDateTime.MIN, isDraft = true, - text = text.convertTo[String], + text = text.convertTo[String Refined NonEmpty], archiveRequired = false ) @@ -40,7 +43,7 @@ object patientissue { } - implicit val patientIssueWriter = new RootJsonWriter[PatientIssue] { + implicit val patientIssueWriter: RootJsonWriter[PatientIssue] = new RootJsonWriter[PatientIssue] { override def write(obj: PatientIssue) = JsObject( "id" -> obj.id.toJson, "text" -> obj.text.toJson, diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/trial.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/trial.scala index 6cc2560..dc9c5e0 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/formats/json/trial.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/trial.scala @@ -1,10 +1,10 @@ package xyz.driver.pdsuidomain.formats.json -import java.time.{ZoneId, ZonedDateTime} +import java.time.{LocalDateTime, ZoneId, ZonedDateTime} import spray.json._ import xyz.driver.core.json.EnumJsonFormat -import xyz.driver.pdsuicommon.domain.{LongId, UuidId} +import xyz.driver.pdsuicommon.domain.{LongId, StringId, User, UuidId} import xyz.driver.pdsuidomain.entities._ object trial { @@ -12,7 +12,7 @@ object trial { import Trial._ import common._ - implicit val trialStatusFormat = new EnumJsonFormat[Status]( + implicit val trialStatusFormat: RootJsonFormat[Status] = new EnumJsonFormat[Status]( "New" -> Status.New, "ReviewSummary" -> Status.ReviewSummary, "Summarized" -> Status.Summarized, @@ -47,7 +47,99 @@ object trial { "originalTitle" -> obj.originalTitle.toJson ) - override def read(json: JsValue): Trial = jsonReader[Trial].read(json) + override def read(json: JsValue): Trial = { + json match { + case JsObject(fields) => + val id = fields + .get("id") + .map(_.convertTo[StringId[Trial]]) + .getOrElse(deserializationError(s"Trial create json object does not contain `id` field: $json")) + val externalid = fields + .get("externalid") + .map(_.convertTo[UuidId[Trial]]) + .getOrElse(deserializationError(s"Trial create json object does not contain `externalid` field: $json")) + val status = fields + .get("status") + .map(_.convertTo[Trial.Status]) + .getOrElse(deserializationError(s"Trial create json object does not contain `status` field: $json")) + val assignee = fields + .get("assignee") + .flatMap(_.convertTo[Option[StringId[User]]]) + val previousStatus = fields + .get("previousStatus") + .flatMap(_.convertTo[Option[Trial.Status]]) + val previousAssignee = fields + .get("previousAssignee") + .flatMap(_.convertTo[Option[StringId[User]]]) + val lastActiveUser = fields + .get("lastActiveUser") + .flatMap(_.convertTo[Option[StringId[User]]]) + val lastUpdate = fields + .get("lastUpdate") + .map(_.convertTo[LocalDateTime]) + .getOrElse(deserializationError(s"Trial create json object does not contain `lastUpdate` field: $json")) + val phase = fields + .get("phase") + .map(_.convertTo[String]) + .getOrElse(deserializationError(s"Trial create json object does not contain `phase` field: $json")) + val hypothesisId = fields + .get("hypothesisId") + .flatMap(_.convertTo[Option[UuidId[Hypothesis]]]) + val studyDesignId = fields + .get("studyDesignId") + .flatMap(_.convertTo[Option[LongId[StudyDesign]]]) + val originalStudyDesignId = fields + .get("originalStudyDesignId") + .flatMap(_.convertTo[Option[String]]) + val isPartner = fields + .get("isPartner") + .map(_.convertTo[Boolean]) + .getOrElse(deserializationError(s"Trial create json object does not contain `isPartner` field: $json")) + val overview = fields + .get("overview") + .flatMap(_.convertTo[Option[String]]) + val overviewTemplate = fields + .get("overviewTemplate") + .map(_.convertTo[String]) + .getOrElse( + deserializationError(s"Trial create json object does not contain `overviewTemplate` field: $json")) + val isUpdated = fields + .get("isUpdated") + .map(_.convertTo[Boolean]) + .getOrElse(deserializationError(s"Trial create json object does not contain `isUpdated` field: $json")) + val title = fields + .get("title") + .map(_.convertTo[String]) + .getOrElse(deserializationError(s"Trial create json object does not contain `title` field: $json")) + val originalTitle = fields + .get("originalTitle") + .map(_.convertTo[String]) + .getOrElse(deserializationError(s"Trial create json object does not contain `originalTitle` field: $json")) + + Trial( + id, + externalid, + status, + assignee, + previousStatus, + previousAssignee, + lastActiveUser, + lastUpdate, + phase, + hypothesisId, + studyDesignId, + originalStudyDesignId, + isPartner, + overview, + overviewTemplate, + isUpdated, + title, + originalTitle + ) + + case _ => deserializationError(s"Expected Json Object as Trial, but got $json") + } + } } def applyUpdateToTrial(json: JsValue, orig: Trial): Trial = json match { diff --git a/src/test/scala/xyz/driver/pdsuidomain/formats/json/PatientHypothesisFormatSuite.scala b/src/test/scala/xyz/driver/pdsuidomain/formats/json/PatientHypothesisFormatSuite.scala index 270a04d..7ff1109 100644 --- a/src/test/scala/xyz/driver/pdsuidomain/formats/json/PatientHypothesisFormatSuite.scala +++ b/src/test/scala/xyz/driver/pdsuidomain/formats/json/PatientHypothesisFormatSuite.scala @@ -1,10 +1,12 @@ package xyz.driver.pdsuidomain.formats.json +import eu.timepit.refined.numeric.NonNegative import spray.json._ import org.scalatest.{FlatSpec, Matchers} import xyz.driver.pdsuicommon.domain.UuidId import xyz.driver.pdsuidomain.entities.PatientHypothesis import xyz.driver.pdsuidomain.services.PatientHypothesisService.RichPatientHypothesis +import eu.timepit.refined.refineMV class PatientHypothesisFormatSuite extends FlatSpec with Matchers { import xyz.driver.pdsuidomain.formats.json.patienthypothesis._ @@ -15,7 +17,7 @@ class PatientHypothesisFormatSuite extends FlatSpec with Matchers { patientId = UuidId("748b5884-3528-4cb9-904b-7a8151d6e343"), hypothesisId = UuidId("e76e2fc4-a29c-44fb-a81b-8856d06bb1d4"), rationale = None, - matchedTrials = 1 + matchedTrials = refineMV[NonNegative](1) ) val writtenJson = richPatientHypothesisWriter.write(RichPatientHypothesis(orig, isRequired = true)) @@ -35,7 +37,7 @@ class PatientHypothesisFormatSuite extends FlatSpec with Matchers { patientId = UuidId("748b5884-3528-4cb9-904b-7a8151d6e343"), hypothesisId = UuidId("e76e2fc4-a29c-44fb-a81b-8856d06bb1d4"), rationale = None, - matchedTrials = 1 + matchedTrials = refineMV[NonNegative](1) ) val writtenJson = patientHypothesisWriter.write(orig) diff --git a/src/test/scala/xyz/driver/pdsuidomain/formats/json/PatientIssueFormatSuite.scala b/src/test/scala/xyz/driver/pdsuidomain/formats/json/PatientIssueFormatSuite.scala index 0d56918..d904150 100644 --- a/src/test/scala/xyz/driver/pdsuidomain/formats/json/PatientIssueFormatSuite.scala +++ b/src/test/scala/xyz/driver/pdsuidomain/formats/json/PatientIssueFormatSuite.scala @@ -2,10 +2,12 @@ package xyz.driver.pdsuidomain.formats.json import java.time.LocalDateTime +import eu.timepit.refined.collection.NonEmpty import spray.json._ import org.scalatest.{FlatSpec, Matchers} import xyz.driver.pdsuicommon.domain.{LongId, StringId, UuidId} import xyz.driver.pdsuidomain.entities.PatientIssue +import eu.timepit.refined.refineMV class PatientIssueFormatSuite extends FlatSpec with Matchers { import xyz.driver.pdsuidomain.formats.json.patientissue._ @@ -17,7 +19,7 @@ class PatientIssueFormatSuite extends FlatSpec with Matchers { userId = StringId("userId-001"), lastUpdate = LocalDateTime.parse("2017-08-10T18:00:00"), isDraft = false, - text = "message text", + text = refineMV[NonEmpty]("message text"), archiveRequired = false ) val writtenJson = patientIssueWriter.write(patientIssue) @@ -35,7 +37,7 @@ class PatientIssueFormatSuite extends FlatSpec with Matchers { val updatePatientIssueJson = """{"text":"new issue text","evidence":"issue evidence","archiveRequired":true}""".parseJson val expectedUpdatedPatientIssue = patientIssue.copy( - text = "new issue text", + text = refineMV[NonEmpty]("new issue text"), archiveRequired = true ) val parsedUpdatePatientIssue = applyUpdateToPatientIssue(updatePatientIssueJson, patientIssue) -- cgit v1.2.3