diff options
author | Jakob Odersky <jakob@odersky.com> | 2017-08-14 22:29:45 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-08-14 22:29:45 -0700 |
commit | 322ea28ecf5ad5f65d3376f3e97e004d229d4736 (patch) | |
tree | c405d10d70f4ec1f18ffa81bc01cd8da64bddcba /src/main/scala/xyz/driver/pdsuidomain/formats/json | |
parent | be74ae7c5531af998d38b9de8052791f17b25341 (diff) | |
parent | 442579b27ccbac82cb001a5b02402a593d005977 (diff) | |
download | rest-query-322ea28ecf5ad5f65d3376f3e97e004d229d4736.tar.gz rest-query-322ea28ecf5ad5f65d3376f3e97e004d229d4736.tar.bz2 rest-query-322ea28ecf5ad5f65d3376f3e97e004d229d4736.zip |
Merge pull request #18 from drivergroup/PDSUI-2188
PDSUI-2188 Create spray json formats for domain entities
Diffstat (limited to 'src/main/scala/xyz/driver/pdsuidomain/formats/json')
26 files changed, 1806 insertions, 0 deletions
diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/arm.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/arm.scala new file mode 100644 index 0000000..39af1c3 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/arm.scala @@ -0,0 +1,44 @@ +package xyz.driver.pdsuidomain.formats.json.sprayformats + +import spray.json._ +import xyz.driver.pdsuicommon.domain.{LongId, StringId} +import xyz.driver.pdsuidomain.entities._ + +object arm { + import DefaultJsonProtocol._ + import common._ + + def applyUpdateToArm(json: JsValue, orig: Arm): Arm = json match { + case JsObject(fields) => + val name = fields + .get("name") + .map(_.convertTo[String]) + .getOrElse(deserializationError(s"Arm json object does not contain `name` field: $json")) + orig.copy(name = name) + + case _ => deserializationError(s"Expected Json Object as partial Arm, but got $json") + } + + def armFormat: RootJsonFormat[Arm] = new RootJsonFormat[Arm] { + override def write(obj: Arm): JsValue = + JsObject( + "id" -> obj.id.toJson, + "name" -> obj.name.toJson, + "originalName" -> obj.originalName.toJson, + "trialId" -> obj.trialId.toJson + ) + + override def read(json: JsValue): Arm = json.asJsObject.getFields("trialId", "name") match { + case Seq(trialId, name) => + Arm( + id = LongId(0), + name = name.convertTo[String], + trialId = trialId.convertTo[StringId[Trial]], + originalName = name.convertTo[String] + ) + + case _ => deserializationError(s"Expected Json Object as Arm, but got $json") + } + } + +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/bridgeuploadqueue.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/bridgeuploadqueue.scala new file mode 100644 index 0000000..77fb4d2 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/bridgeuploadqueue.scala @@ -0,0 +1,68 @@ +package xyz.driver.pdsuidomain.formats.json.sprayformats + +import java.time.LocalDateTime + +import spray.json._ +import xyz.driver.pdsuicommon.concurrent.BridgeUploadQueue +import xyz.driver.pdsuicommon.concurrent.BridgeUploadQueue.Item + +object bridgeuploadqueue { + import DefaultJsonProtocol._ + import common._ + + implicit val queueUploadItemFormat: RootJsonFormat[BridgeUploadQueue.Item] = new RootJsonFormat[Item] { + override def write(obj: Item) = + JsObject( + "kind" -> obj.kind.toJson, + "tag" -> obj.tag.toJson, + "created" -> obj.created.toJson, + "attempts" -> obj.attempts.toJson, + "nextAttempt" -> obj.nextAttempt.toJson, + "completed" -> obj.completed.toJson + ) + + override def read(json: JsValue): Item = json match { + case JsObject(fields) => + val kind = fields + .get("kind") + .map(_.convertTo[String]) + .getOrElse(deserializationError(s"BridgeUploadQueue.Item json object does not contain `kind` field: $json")) + + val tag = fields + .get("tag") + .map(_.convertTo[String]) + .getOrElse(deserializationError(s"BridgeUploadQueue.Item json object does not contain `tag` field: $json")) + + val created = fields + .get("created") + .map(_.convertTo[LocalDateTime]) + .getOrElse( + deserializationError(s"BridgeUploadQueue.Item json object does not contain `created` field: $json")) + + val attempts = fields + .get("attempts") + .map(_.convertTo[Int]) + .getOrElse( + deserializationError(s"BridgeUploadQueue.Item json object does not contain `attempts` field: $json")) + + val nextAttempt = fields + .get("nextAttempt") + .map(_.convertTo[LocalDateTime]) + .getOrElse( + deserializationError(s"BridgeUploadQueue.Item json object does not contain `nextAttempt` field: $json")) + + BridgeUploadQueue.Item( + kind = kind, + tag = tag, + created = created, + attempts = attempts, + nextAttempt = nextAttempt, + completed = true, + dependencyKind = None, + dependencyTag = None + ) + + case _ => deserializationError(s"Expected Json Object as BridgeUploadQueue.Item, but got $json") + } + } +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/common.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/common.scala new file mode 100644 index 0000000..dbd0a43 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/common.scala @@ -0,0 +1,74 @@ +package xyz.driver.pdsuidomain.formats.json.sprayformats + +import java.time.{LocalDate, LocalDateTime, ZoneId, ZonedDateTime} + +import spray.json._ +import xyz.driver.pdsuicommon.domain.{FuzzyValue, LongId, StringId, UuidId} + +object common { + + implicit def longIdFormat[T] = new RootJsonFormat[LongId[T]] { + override def write(id: LongId[T]): JsNumber = JsNumber(id.id) + override def read(json: JsValue): LongId[T] = json match { + case JsNumber(value) => LongId(value.toLong) + case _ => deserializationError(s"Expected number as LongId, but got $json") + } + } + + implicit def stringIdFormat[T] = new RootJsonFormat[StringId[T]] { + override def write(id: StringId[T]): JsString = JsString(id.toString) + override def read(json: JsValue): StringId[T] = json match { + case JsString(value) => StringId(value) + case _ => deserializationError(s"Expected string as StringId, but got $json") + } + } + + implicit def uuidIdFormat[T] = new RootJsonFormat[UuidId[T]] { + override def write(id: UuidId[T]): JsString = JsString(id.toString) + override def read(json: JsValue): UuidId[T] = json match { + case JsString(value) => UuidId(value) + case _ => deserializationError(s"Expected string as UuidId, but got $json") + } + } + + implicit def dateTimeFormat = new RootJsonFormat[LocalDateTime] { + override def write(date: LocalDateTime): JsString = JsString(ZonedDateTime.of(date, ZoneId.of("Z")).toString) + override def read(json: JsValue): LocalDateTime = json match { + case JsString(value) => ZonedDateTime.parse(value).toLocalDateTime + case _ => deserializationError(s"Expected date as LocalDateTime, but got $json") + } + } + + implicit def zonedDateTimeFormat = new RootJsonFormat[ZonedDateTime] { + override def write(date: ZonedDateTime): JsString = JsString(date.toString) + override def read(json: JsValue): ZonedDateTime = json match { + case JsString(value) => ZonedDateTime.parse(value) + case _ => deserializationError(s"Expected date as ZonedDateTime, but got $json") + } + } + + implicit def dateFormat = new RootJsonFormat[LocalDate] { + override def write(date: LocalDate): JsString = JsString(date.toString) + override def read(json: JsValue): LocalDate = json match { + case JsString(value) => LocalDate.parse(value) + case _ => deserializationError(s"Expected date as LocalDate, but got $json") + } + } + + implicit def fuzzyValueFormat: RootJsonFormat[FuzzyValue] = new RootJsonFormat[FuzzyValue] { + override def write(value: FuzzyValue): JsString = JsString(FuzzyValue.valueToString(value)) + override def read(json: JsValue): FuzzyValue = json match { + case JsString(value) => FuzzyValue.fromString(value) + case _ => deserializationError(s"Expected value as FuzzyValue, but got $json") + } + } + + implicit val integerFormat: RootJsonFormat[Integer] = new RootJsonFormat[Integer] { + override def write(obj: Integer): JsNumber = JsNumber(obj.intValue()) + override def read(json: JsValue): Integer = json match { + case JsNumber(value) => value.toInt + case _ => deserializationError(s"Expected number as Integer, but got $json") + } + } + +} 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 new file mode 100644 index 0000000..732bcad --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/criterion.scala @@ -0,0 +1,158 @@ +package xyz.driver.pdsuidomain.formats.json.sprayformats + +import spray.json._ +import xyz.driver.pdsuicommon.domain.{LongId, StringId} +import xyz.driver.pdsuidomain.entities._ +import xyz.driver.pdsuidomain.services.CriterionService.RichCriterion + +object criterion { + import DefaultJsonProtocol._ + import common._ + + implicit val criterionLabelWriter = new JsonWriter[CriterionLabel] { + override def write(obj: CriterionLabel) = JsObject( + "labelId" -> obj.labelId.toJson, + "categoryId" -> obj.categoryId.toJson, + "value" -> obj.value.map { + case true => "Yes" + case false => "No" + }.toJson, + "isDefining" -> obj.isDefining.toJson + ) + } + + def jsValueToCriterionLabel(json: JsValue, criterionId: LongId[Criterion]): CriterionLabel = json match { + case JsObject(fields) => + val labelId = fields + .get("labelId") + .map(_.convertTo[LongId[Label]]) + + val categoryId = fields + .get("categoryId") + .map(_.convertTo[LongId[Category]]) + + val value = fields + .get("value") + .map(_.convertTo[String] match { + case "Yes" => true + case "No" => false + case other => + deserializationError(s"Unknown `value` of CriterionLabel object: expected `yes` or `no`, but got $other") + }) + + val isDefining = fields + .get("isDefining") + .map(_.convertTo[Boolean]) + .getOrElse(deserializationError(s"CriterionLabel json object does not contain `isDefining` field: $json")) + + CriterionLabel( + id = LongId(0L), + labelId = labelId, + criterionId = criterionId, + categoryId = categoryId, + value = value, + isDefining = isDefining + ) + + case _ => deserializationError(s"Expected Json Object as CriterionLabel, but got $json") + } + + def applyUpdateToCriterion(json: JsValue, orig: RichCriterion): RichCriterion = json match { + case JsObject(fields) => + val text = fields + .get("text") + .map(_.convertTo[String]) + + val isCompound = fields + .get("isCompound") + .exists(_.convertTo[Boolean]) + + val meta = fields + .get("meta") + .map(_.convertTo[Option[String]].getOrElse("{}")) + .getOrElse(orig.criterion.meta) + + val arms = fields + .get("arms") + .map(_.convertTo[Option[List[LongId[Arm]]]].getOrElse(List.empty[LongId[Arm]])) + .getOrElse(orig.armIds) + + val labels = fields + .get("labels") + .map(_.convertTo[Option[List[JsValue]]].getOrElse(List.empty[JsValue])) + .map(_.map(l => jsValueToCriterionLabel(l, orig.criterion.id))) + .getOrElse(orig.labels) + + orig.copy( + criterion = orig.criterion.copy( + meta = meta, + text = text, + isCompound = isCompound + ), + armIds = arms, + labels = labels + ) + + case _ => deserializationError(s"Expected Json Object as partial Criterion, but got $json") + } + + val richCriterionFormat = new RootJsonFormat[RichCriterion] { + override def write(obj: RichCriterion): JsValue = + JsObject( + "id" -> obj.criterion.id.toJson, + "meta" -> Option(obj.criterion.meta).toJson, + "arms" -> obj.armIds.toJson, + "text" -> obj.criterion.text.toJson, + "isCompound" -> obj.criterion.isCompound.toJson, + "labels" -> obj.labels.map(_.toJson).toJson, + "trialId" -> obj.criterion.trialId.toJson + ) + + override def read(json: JsValue): RichCriterion = json match { + case JsObject(fields) => + val trialId = fields + .get("trialId") + .map(_.convertTo[StringId[Trial]]) + .getOrElse(deserializationError(s"Criterion json object does not contain `trialId` field: $json")) + + val text = fields + .get("text") + .map(_.convertTo[String]) + + val isCompound = fields + .get("isCompound") + .exists(_.convertTo[Boolean]) + + val meta = fields + .get("meta") + .map(_.convertTo[String]) + .getOrElse("") + + val arms = fields + .get("arms") + .map(_.convertTo[List[LongId[Arm]]]) + .getOrElse(List.empty[LongId[Arm]]) + + val labels = fields + .get("labels") + .map(_.convertTo[List[JsValue]]) + .map(_.map(l => jsValueToCriterionLabel(l, LongId(0)))) + .getOrElse(List.empty[CriterionLabel]) + + RichCriterion( + criterion = Criterion( + id = LongId(0), + trialId = trialId, + text = text, + isCompound = isCompound, + meta = meta + ), + armIds = arms, + labels = labels + ) + + case _ => deserializationError(s"Expected Json Object as Criterion, but got $json") + } + } + +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/document.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/document.scala new file mode 100644 index 0000000..baf1b4e --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/document.scala @@ -0,0 +1,164 @@ +package xyz.driver.pdsuidomain.formats.json.sprayformats + +import java.time.{LocalDate, LocalDateTime} + +import spray.json._ +import xyz.driver.core.json.EnumJsonFormat +import xyz.driver.pdsuicommon.domain.{LongId, TextJson} +import xyz.driver.pdsuidomain.entities._ + +object document { + import DefaultJsonProtocol._ + import Document._ + import common._ + + implicit val documentStatusFormat = new EnumJsonFormat[Status]( + "New" -> Status.New, + "Organized" -> Status.Organized, + "Extracted" -> Status.Extracted, + "Done" -> Status.Done, + "Flagged" -> Status.Flagged, + "Archived" -> Status.Archived + ) + + implicit val requiredTypeFormat = new EnumJsonFormat[RequiredType]( + "OPN" -> RequiredType.OPN, + "PN" -> RequiredType.PN + ) + + implicit val documentMetaFormat: RootJsonFormat[Meta] = jsonFormat3(Meta.apply) + + implicit val fullDocumentMetaFormat = new RootJsonFormat[TextJson[Meta]] { + override def write(obj: TextJson[Meta]): JsValue = obj.content.toJson + override def read(json: JsValue) = TextJson(documentMetaFormat.read(json)) + } + + def applyUpdateToDocument(json: JsValue, orig: Document): Document = json match { + case JsObject(fields) => + val physician = fields + .get("physician") + .map(_.convertTo[String]) + + val typeId = fields + .get("typeId") + .map(_.convertTo[Option[LongId[DocumentType]]]) + .getOrElse(orig.typeId) + + val provider = fields + .get("provider") + .map(_.convertTo[Option[String]]) + .getOrElse(orig.providerName) + + val providerTypeId = fields + .get("providerTypeId") + .map(_.convertTo[Option[LongId[ProviderType]]]) + .getOrElse(orig.providerTypeId) + + val meta = fields + .get("meta") + .map(_.convertTo[Option[TextJson[Meta]]]) + .getOrElse(orig.meta) + + val startDate = fields + .get("startDate") + .map(_.convertTo[Option[LocalDate]]) + .getOrElse(orig.startDate) + + val endDate = fields + .get("endDate") + .map(_.convertTo[Option[LocalDate]]) + .getOrElse(orig.endDate) + + orig.copy( + physician = physician.orElse(orig.physician), + typeId = typeId, + providerName = provider, + providerTypeId = providerTypeId, + meta = meta, + startDate = startDate, + endDate = endDate + ) + + case _ => deserializationError(s"Expected Json Object as partial Document, but got $json") + } + + implicit val documentFormat: RootJsonFormat[Document] = new RootJsonFormat[Document] { + override def write(document: Document): JsValue = + JsObject( + "id" -> document.id.id.toJson, + "recordId" -> document.recordId.toJson, + "physician" -> document.physician.toJson, + "typeId" -> document.typeId.toJson, + "provider" -> document.providerName.toJson, + "providerTypeId" -> document.providerTypeId.toJson, + "requiredType" -> document.requiredType.toJson, + "startDate" -> document.startDate.toJson, + "endDate" -> document.endDate.toJson, + "status" -> document.status.toJson, + "previousStatus" -> document.previousStatus.toJson, + "assignee" -> document.assignee.toJson, + "previousAssignee" -> document.previousAssignee.toJson, + "meta" -> document.meta.toJson, + "lastActiveUser" -> document.lastActiveUserId.toJson, + "lastUpdate" -> document.lastUpdate.toJson + ) + + override def read(json: JsValue): Document = json match { + case JsObject(fields) => + val recordId = fields + .get("recordId") + .map(_.convertTo[LongId[MedicalRecord]]) + .getOrElse(deserializationError(s"Document create json object does not contain `recordId` field: $json")) + + val physician = fields + .get("physician") + .map(_.convertTo[String]) + + val typeId = fields + .get("typeId") + .map(_.convertTo[LongId[DocumentType]]) + + val provider = fields + .get("provider") + .map(_.convertTo[String]) + + val providerTypeId = fields + .get("providerTypeId") + .map(_.convertTo[LongId[ProviderType]]) + + val meta = fields + .get("meta") + .map(_.convertTo[TextJson[Meta]]) + + val startDate = fields + .get("startDate") + .map(_.convertTo[LocalDate]) + + val endDate = fields + .get("endDate") + .map(_.convertTo[LocalDate]) + + Document( + id = LongId(0), + recordId = recordId, + status = Document.Status.New, + physician = physician, + typeId = typeId, + startDate = startDate, + endDate = endDate, + providerName = provider, + providerTypeId = providerTypeId, + requiredType = None, + meta = meta, + previousStatus = None, + assignee = None, + previousAssignee = None, + lastActiveUserId = None, + lastUpdate = LocalDateTime.MIN + ) + + case _ => deserializationError(s"Expected Json Object as Document, but got $json") + } + } + +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/documenthistory.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/documenthistory.scala new file mode 100644 index 0000000..419c252 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/documenthistory.scala @@ -0,0 +1,29 @@ +package xyz.driver.pdsuidomain.formats.json.sprayformats + +import spray.json._ +import xyz.driver.core.json.EnumJsonFormat +import xyz.driver.pdsuidomain.entities._ + +object documenthistory { + import DefaultJsonProtocol._ + import common._ + import DocumentHistory._ + + implicit val documentStateFormat = new EnumJsonFormat[State]( + "Extract" -> State.Extract, + "Review" -> State.Review, + "Flag" -> State.Flag + ) + + implicit val documentActionFormat = new EnumJsonFormat[Action]( + "Start" -> Action.Start, + "Submit" -> Action.Submit, + "Unassign" -> Action.Unassign, + "Resolve" -> Action.Resolve, + "Flag" -> Action.Flag, + "Archive" -> Action.Archive + ) + + implicit val documentHistoryFormat: RootJsonFormat[DocumentHistory] = jsonFormat6(DocumentHistory.apply) + +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/documentissue.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/documentissue.scala new file mode 100644 index 0000000..28b2a5e --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/documentissue.scala @@ -0,0 +1,77 @@ +package xyz.driver.pdsuidomain.formats.json.sprayformats + +import java.time.LocalDateTime + +import spray.json._ +import xyz.driver.pdsuicommon.domain.{LongId, StringId, User} +import xyz.driver.pdsuidomain.entities._ + +object documentissue { + import DefaultJsonProtocol._ + import common._ + + def applyUpdateToDocumentIssue(json: JsValue, orig: DocumentIssue): DocumentIssue = json match { + case JsObject(fields) => + val text = fields + .get("text") + .map(_.convertTo[String]) + .getOrElse(deserializationError(s"DocumentIssue json object does not contain `text` field: $json")) + + val archiveRequired = fields + .get("archiveRequired") + .map(_.convertTo[Boolean]) + .getOrElse(deserializationError(s"DocumentIssue json object does not contain `archiveRequired` field: $json")) + + val startPage = fields.get("startPage").map(_.convertTo[Double]) + val endPage = fields.get("endPage").map(_.convertTo[Double]) + + orig.copy( + text = text, + archiveRequired = archiveRequired, + startPage = startPage, + endPage = endPage + ) + + case _ => deserializationError(s"Expected Json Object as partial DocumentIssue, but got $json") + + } + + def jsValueToDocumentIssue(json: JsValue, documentId: LongId[Document], userId: StringId[User]): DocumentIssue = + json match { + case JsObject(fields) => + val text = fields + .get("text") + .map(_.convertTo[String]) + .getOrElse(deserializationError(s"DocumentIssue json object does not contain `text` field: $json")) + + val startPage = fields.get("startPage").map(_.convertTo[Double]) + val endPage = fields.get("endPage").map(_.convertTo[Double]) + DocumentIssue( + id = LongId(0), + userId = userId, + documentId = documentId, + lastUpdate = LocalDateTime.MIN, + isDraft = true, + text = text, + archiveRequired = false, + startPage = startPage, + endPage = endPage + ) + + case _ => deserializationError(s"Expected Json Object as DocumentIssue, but got $json") + } + + implicit val documentIssueWriter = new JsonWriter[DocumentIssue] { + override def write(obj: DocumentIssue) = JsObject( + "id" -> obj.id.toJson, + "startPage" -> obj.startPage.toJson, + "endPage" -> obj.endPage.toJson, + "text" -> obj.text.toJson, + "lastUpdate" -> obj.lastUpdate.toJson, + "userId" -> obj.userId.toJson, + "isDraft" -> obj.isDraft.toJson, + "archiveRequired" -> obj.archiveRequired.toJson + ) + } + +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/export.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/export.scala new file mode 100644 index 0000000..20b6ed0 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/export.scala @@ -0,0 +1,78 @@ +package xyz.driver.pdsuidomain.formats.json.sprayformats + +import spray.json._ +import xyz.driver.pdsuidomain.entities.export.patient._ +import xyz.driver.pdsuidomain.entities.export.trial.{ExportTrialArm, ExportTrialLabelCriterion, ExportTrialWithLabels} + +object export { + import DefaultJsonProtocol._ + import common._ + import record._ + + implicit val patientLabelEvidenceDocumentFormat: RootJsonFormat[ExportPatientLabelEvidenceDocument] = jsonFormat5( + ExportPatientLabelEvidenceDocument.apply) + + implicit val patientLabelEvidenceWriter: JsonWriter[ExportPatientLabelEvidence] = + new JsonWriter[ExportPatientLabelEvidence] { + override def write(obj: ExportPatientLabelEvidence): JsValue = + JsObject( + "evidenceId" -> obj.id.toJson, + "labelValue" -> obj.value.toJson, + "evidenceText" -> obj.evidenceText.toJson, + "document" -> obj.document.toJson + ) + } + + implicit val patientLabelWriter: JsonWriter[ExportPatientLabel] = new JsonWriter[ExportPatientLabel] { + override def write(obj: ExportPatientLabel): JsValue = + JsObject( + "labelId" -> obj.id.toJson, + "evidence" -> obj.evidences.map(_.toJson).toJson + ) + } + + implicit val patientWithLabelsWriter: JsonWriter[ExportPatientWithLabels] = new JsonWriter[ExportPatientWithLabels] { + override def write(obj: ExportPatientWithLabels): JsValue = + JsObject( + "patientId" -> obj.patientId.toJson, + "labelVersion" -> obj.labelVersion.toJson, + "labels" -> obj.labels.map(_.toJson).toJson + ) + } + + implicit val trialArmFormat: RootJsonFormat[ExportTrialArm] = jsonFormat2(ExportTrialArm.apply) + + implicit val trialLabelCriterionWriter: JsonWriter[ExportTrialLabelCriterion] = + new JsonWriter[ExportTrialLabelCriterion] { + override def write(obj: ExportTrialLabelCriterion): JsValue = + JsObject( + "value" -> obj.value + .map { + case true => "Yes" + case false => "No" + } + .getOrElse("Unknown") + .toJson, + "labelId" -> obj.labelId.toJson, + "criterionId" -> obj.criterionId.toJson, + "criterionText" -> obj.criteria.toJson, + "armIds" -> obj.armIds.toJson, + "isCompound" -> obj.isCompound.toJson, + "isDefining" -> obj.isDefining.toJson + ) + } + + implicit val trialWithLabelsWriter: JsonWriter[ExportTrialWithLabels] = new JsonWriter[ExportTrialWithLabels] { + override def write(obj: ExportTrialWithLabels) = + JsObject( + "nctId" -> obj.nctId.toJson, + "trialId" -> obj.trialId.toJson, + "disease" -> obj.condition.toJson, + "lastReviewed" -> obj.lastReviewed.toJson, + "labelVersion" -> obj.labelVersion.toJson, + "arms" -> obj.arms.toJson, + "criteria" -> obj.criteria.map(_.toJson).toJson + ) + } + +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/extracteddata.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/extracteddata.scala new file mode 100644 index 0000000..42473bc --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/extracteddata.scala @@ -0,0 +1,148 @@ +package xyz.driver.pdsuidomain.formats.json.sprayformats + +import spray.json._ +import xyz.driver.pdsuicommon.domain.{FuzzyValue, LongId, TextJson} +import xyz.driver.pdsuidomain.entities._ +import xyz.driver.pdsuidomain.services.ExtractedDataService.RichExtractedData + +object extracteddata { + import DefaultJsonProtocol._ + import common._ + import ExtractedData._ + + implicit val metaKeywordFormat: RootJsonFormat[Meta.Keyword] = jsonFormat4(Meta.Keyword) + implicit val metaTextLayerPositionFormat: RootJsonFormat[Meta.TextLayerPosition] = jsonFormat3( + Meta.TextLayerPosition) + implicit val metaEvidenceFormat: RootJsonFormat[Meta.Evidence] = jsonFormat3(Meta.Evidence) + + implicit val extractedDataMetaFormat: RootJsonFormat[Meta] = jsonFormat2(Meta.apply) + implicit val fullExtractedDataMetaFormat = new RootJsonFormat[TextJson[Meta]] { + override def write(obj: TextJson[Meta]): JsValue = obj.content.toJson + override def read(json: JsValue): TextJson[Meta] = TextJson(extractedDataMetaFormat.read(json)) + } + + implicit val extractedDataLabelWriter: JsonWriter[ExtractedDataLabel] = new JsonWriter[ExtractedDataLabel] { + override def write(label: ExtractedDataLabel): JsObject = { + JsObject( + "id" -> label.labelId.toJson, + "categoryId" -> label.categoryId.toJson, + "value" -> label.value.toJson + ) + } + } + + def applyLabelsForExtractedData(json: JsValue, dataId: LongId[ExtractedData]): ExtractedDataLabel = json match { + case JsObject(fields) => + val labelId = fields + .get("id") + .map(_.convertTo[LongId[Label]]) + + val categoryId = fields + .get("categoryId") + .map(_.convertTo[LongId[Category]]) + + val value = fields + .get("value") + .map(_.convertTo[FuzzyValue]) + + ExtractedDataLabel( + id = LongId(0), + dataId = dataId, + labelId = labelId, + categoryId = categoryId, + value = value + ) + + case _ => deserializationError(s"Expected Json Object as ExtractedDataLabel, but got $json") + } + + def applyUpdateToExtractedData(json: JsValue, orig: RichExtractedData): RichExtractedData = json match { + case JsObject(fields) => + val keywordId = fields + .get("keywordId") + .map(_.convertTo[Option[LongId[Keyword]]]) + .getOrElse(orig.extractedData.keywordId) + + val evidence = fields + .get("evidence") + .map(_.convertTo[Option[String]]) + .getOrElse(orig.extractedData.evidenceText) + + val meta = fields + .get("meta") + .map(_.convertTo[Option[TextJson[Meta]]]) + .getOrElse(orig.extractedData.meta) + + val labels = fields + .get("labels") + .map( + _.convertTo[Option[List[JsValue]]] + .getOrElse(List.empty[JsValue]) + .map(l => applyLabelsForExtractedData(l, orig.extractedData.id))) + .getOrElse(orig.labels) + + val extractedData = orig.extractedData.copy( + keywordId = keywordId, + evidenceText = evidence, + meta = meta + ) + + orig.copy( + extractedData = extractedData, + labels = labels + ) + + case _ => deserializationError(s"Expected Json Object as partial ExtractedData, but got $json") + } + + implicit val extractedDataFormat: RootJsonFormat[RichExtractedData] = new RootJsonFormat[RichExtractedData] { + override def write(richData: RichExtractedData): JsValue = + JsObject( + "id" -> richData.extractedData.id.id.toJson, + "documentId" -> richData.extractedData.documentId.toJson, + "keywordId" -> richData.extractedData.keywordId.toJson, + "evidence" -> richData.extractedData.evidenceText.toJson, + "meta" -> richData.extractedData.meta.toJson, + "labels" -> richData.labels.map(_.toJson).toJson + ) + + override def read(json: JsValue): RichExtractedData = json match { + case JsObject(fields) => + val documentId = fields + .get("documentId") + .map(_.convertTo[LongId[Document]]) + .getOrElse( + deserializationError(s"ExtractedData create json object does not contain `documentId` field: $json")) + + val keywordId = fields + .get("keywordId") + .map(_.convertTo[LongId[Keyword]]) + + val evidence = fields + .get("evidence") + .map(_.convertTo[String]) + + val meta = fields + .get("meta") + .map(_.convertTo[TextJson[Meta]]) + + val labels = fields + .get("labels") + .map(_.convertTo[List[JsValue]]) + .getOrElse(List.empty[JsValue]) + .map(l => applyLabelsForExtractedData(l, LongId(0))) + + val extractedData = ExtractedData( + documentId = documentId, + keywordId = keywordId, + evidenceText = evidence, + meta = meta + ) + + RichExtractedData(extractedData, labels) + + case _ => deserializationError(s"Expected Json Object as ExtractedData, but got $json") + } + } + +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/hypothesis.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/hypothesis.scala new file mode 100644 index 0000000..c05ff23 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/hypothesis.scala @@ -0,0 +1,12 @@ +package xyz.driver.pdsuidomain.formats.json.sprayformats + +import spray.json._ +import xyz.driver.pdsuidomain.entities._ + +object hypothesis { + import DefaultJsonProtocol._ + import common._ + + implicit val hypothesisFormat: RootJsonFormat[Hypothesis] = jsonFormat4(Hypothesis.apply) + +} 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 new file mode 100644 index 0000000..a8ce950 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/intervention.scala @@ -0,0 +1,60 @@ +package xyz.driver.pdsuidomain.formats.json.sprayformats + +import spray.json._ +import xyz.driver.pdsuicommon.domain.LongId +import xyz.driver.pdsuidomain.entities._ + +object intervention { + import DefaultJsonProtocol._ + import common._ + + implicit val interventionWriter: JsonWriter[InterventionWithArms] = new JsonWriter[InterventionWithArms] { + override def write(obj: InterventionWithArms) = + JsObject( + "id" -> obj.intervention.id.toJson, + "name" -> obj.intervention.name.toJson, + "typeId" -> obj.intervention.typeId.toJson, + "description" -> obj.intervention.description.toJson, + "isActive" -> obj.intervention.isActive.toJson, + "arms" -> obj.arms.map(_.armId).toJson, + "trialId" -> obj.intervention.trialId.toJson, + "originalName" -> obj.intervention.originalName.toJson, + "originalDescription" -> obj.intervention.originalDescription.toJson, + "originalType" -> obj.intervention.originalType.toJson + ) + } + + def applyUpdateToInterventionWithArms(json: JsValue, orig: InterventionWithArms): InterventionWithArms = json match { + case JsObject(fields) => + val typeId = fields + .get("typeId") + .map(_.convertTo[LongId[InterventionType]]) + + val description = fields + .get("description") + .map(_.convertTo[String]) + + val isActive = fields + .get("isActive") + .map(_.convertTo[Boolean]) + + val origIntervention = orig.intervention + val arms = fields + .get("arms") + .map(_.convertTo[List[LongId[Arm]]].map(x => InterventionArm(x, orig.intervention.id))) + + orig.copy( + intervention = origIntervention.copy( + typeId = typeId.orElse(origIntervention.typeId), + description = description.getOrElse(origIntervention.description), + isActive = isActive.getOrElse(origIntervention.isActive) + ), + arms = arms.getOrElse(orig.arms) + ) + + case _ => deserializationError(s"Expected Json Object as partial Intervention, but got $json") + } + + implicit val interventionTypeFormat: RootJsonFormat[InterventionType] = jsonFormat2(InterventionType.apply) + +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/patient.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/patient.scala new file mode 100644 index 0000000..724c391 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/patient.scala @@ -0,0 +1,46 @@ +package xyz.driver.pdsuidomain.formats.json.sprayformats + +import spray.json._ +import xyz.driver.core.json.EnumJsonFormat +import xyz.driver.pdsuidomain.entities._ + +object patient { + import DefaultJsonProtocol._ + import common._ + import Patient._ + + implicit val patientStatusFormat = new EnumJsonFormat[Status]( + "New" -> Status.New, + "Verified" -> Status.Verified, + "Reviewed" -> Status.Reviewed, + "Curated" -> Status.Curated, + "Done" -> Status.Done, + "Flagged" -> Status.Flagged + ) + + implicit val patientOrderIdFormat = 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) + case _ => deserializationError(s"Expected string as PatientOrderId, but got $json") + } + } + + implicit val patientWriter: JsonWriter[Patient] = new JsonWriter[Patient] { + override def write(patient: Patient): JsValue = + JsObject( + "id" -> patient.id.toJson, + "status" -> patient.status.toJson, + "name" -> patient.name.toJson, + "dob" -> patient.dob.toJson, + "assignee" -> patient.assignee.toJson, + "previousStatus" -> patient.previousStatus.toJson, + "previousAssignee" -> patient.previousAssignee.toJson, + "lastActiveUser" -> patient.lastActiveUserId.toJson, + "lastUpdate" -> patient.lastUpdate.toJson, + "condition" -> patient.condition.toJson, + "orderId" -> patient.orderId.toJson + ) + } + +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/patientcriterion.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/patientcriterion.scala new file mode 100644 index 0000000..53e927d --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/patientcriterion.scala @@ -0,0 +1,68 @@ +package xyz.driver.pdsuidomain.formats.json.sprayformats + +import spray.json._ +import xyz.driver.pdsuicommon.domain.{FuzzyValue, LongId} +import xyz.driver.pdsuidomain.entities._ +import xyz.driver.pdsuidomain.services.PatientCriterionService.DraftPatientCriterion + +object patientcriterion { + import DefaultJsonProtocol._ + import common._ + + def applyUpdateToPatientCriterion(json: JsValue, orig: PatientCriterion): PatientCriterion = json match { + case JsObject(fields) => + val eligibilityStatus = if (fields.contains("eligibilityStatus")) { + fields + .get("eligibilityStatus") + .map(_.convertTo[FuzzyValue]) + } else orig.eligibilityStatus + + val verifiedEligibilityStatus = if (fields.contains("verifiedEligibilityStatus")) { + fields + .get("verifiedEligibilityStatus") + .map(_.convertTo[FuzzyValue]) + } else orig.verifiedEligibilityStatus + + orig.copy( + eligibilityStatus = eligibilityStatus, + verifiedEligibilityStatus = verifiedEligibilityStatus + ) + + case _ => deserializationError(s"Expected Json Object as partial PatientCriterion, but got $json") + } + + implicit val draftPatientCriterionFormat: RootJsonFormat[DraftPatientCriterion] = jsonFormat3( + DraftPatientCriterion.apply) + implicit val draftPatientCriterionListReader = new JsonReader[List[DraftPatientCriterion]] { + override def read(json: JsValue) = json.convertTo[List[JsValue]].map(_.convertTo[DraftPatientCriterion]) + } + + implicit val patientCriterionWriter: JsonWriter[(PatientCriterion, LongId[Label], List[PatientCriterionArm])] = + new JsonWriter[(PatientCriterion, LongId[Label], List[PatientCriterionArm])] { + override def write(obj: (PatientCriterion, LongId[Label], List[PatientCriterionArm])): JsValue = { + val criterion = obj._1 + val labelId = obj._2 + val arms = obj._3 + JsObject( + "id" -> criterion.id.toJson, + "labelId" -> labelId.toJson, + "nctId" -> criterion.nctId.toJson, + "criterionId" -> criterion.criterionId.toJson, + "criterionText" -> criterion.criterionText.toJson, + "criterionValue" -> criterion.criterionValue.map { + case true => "Yes" + case false => "No" + }.toJson, + "criterionIsDefining" -> criterion.criterionIsDefining.toJson, + "criterionIsCompound" -> criterion.criterionValue.isEmpty.toJson, + "arms" -> arms.map(_.armName).toJson, + "eligibilityStatus" -> criterion.eligibilityStatus.toJson, + "verifiedEligibilityStatus" -> criterion.verifiedEligibilityStatus.toJson, + "isVerified" -> criterion.isVerified.toJson, + "isVisible" -> criterion.isVisible.toJson, + "lastUpdate" -> criterion.lastUpdate.toJson + ) + } + } + +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/patientdefiningcriteria.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/patientdefiningcriteria.scala new file mode 100644 index 0000000..b97570a --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/patientdefiningcriteria.scala @@ -0,0 +1,18 @@ +package xyz.driver.pdsuidomain.formats.json.sprayformats + +import spray.json._ +import xyz.driver.pdsuidomain.entities.PatientLabel + +object patientdefiningcriteria { + import DefaultJsonProtocol._ + import common._ + + implicit val patientLabelDefiningCriteriaWriter: JsonWriter[PatientLabel] = new JsonWriter[PatientLabel] { + override def write(obj: PatientLabel) = + JsObject( + "id" -> obj.id.toJson, + "value" -> obj.verifiedPrimaryValue.toJson + ) + } + +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/patienteligibletrial.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/patienteligibletrial.scala new file mode 100644 index 0000000..894e453 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/patienteligibletrial.scala @@ -0,0 +1,39 @@ +package xyz.driver.pdsuidomain.formats.json.sprayformats + +import spray.json._ +import xyz.driver.pdsuidomain.entities._ +import xyz.driver.pdsuidomain.services.PatientEligibleTrialService.RichPatientEligibleTrial + +object patienteligibletrial { + import DefaultJsonProtocol._ + import common._ + + def applyUpdateToTrialArmGroup(json: JsValue, orig: PatientTrialArmGroupView): PatientTrialArmGroupView = + json match { + case JsObject(fields) => + val isVerified = fields + .get("isVerified") + .map(_.convertTo[Boolean]) + .getOrElse(orig.isVerified) + + orig.copy(isVerified = isVerified) + + case _ => deserializationError(s"Expected Json Object as partial PatientTrialArmGroupView, but got $json") + } + + implicit val patientEligibleTrialWriter: JsonWriter[RichPatientEligibleTrial] = + new JsonWriter[RichPatientEligibleTrial] { + override def write(obj: RichPatientEligibleTrial) = + JsObject( + "id" -> obj.group.id.toJson, + "patientId" -> obj.group.patientId.toJson, + "trialId" -> obj.group.trialId.toJson, + "trialTitle" -> obj.trial.title.toJson, + "arms" -> obj.arms.map(_.armName).toJson, + "hypothesisId" -> obj.trial.hypothesisId.toJson, + "verifiedEligibilityStatus" -> obj.group.verifiedEligibilityStatus.toJson, + "isVerified" -> obj.group.isVerified.toJson + ) + } + +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/patienthistory.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/patienthistory.scala new file mode 100644 index 0000000..da7a664 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/patienthistory.scala @@ -0,0 +1,30 @@ +package xyz.driver.pdsuidomain.formats.json.sprayformats + +import spray.json._ +import xyz.driver.core.json.EnumJsonFormat +import xyz.driver.pdsuidomain.entities._ + +object patienthistory { + import DefaultJsonProtocol._ + import common._ + import PatientHistory._ + + implicit val patientStateFormat = new EnumJsonFormat[State]( + "Verify" -> State.Verify, + "Curate" -> State.Curate, + "Review" -> State.Review, + "Flag" -> State.Flag + ) + + implicit val patientActionFormat = new EnumJsonFormat[Action]( + "Start" -> Action.Start, + "Submit" -> Action.Submit, + "Unassign" -> Action.Unassign, + "Resolve" -> Action.Resolve, + "Flag" -> Action.Flag, + "Archive" -> Action.Archive + ) + + implicit val patientHistoryFormat: RootJsonFormat[PatientHistory] = jsonFormat6(PatientHistory.apply) + +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/patienthypothesis.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/patienthypothesis.scala new file mode 100644 index 0000000..4f2783c --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/patienthypothesis.scala @@ -0,0 +1,37 @@ +package xyz.driver.pdsuidomain.formats.json.sprayformats + +import spray.json._ +import xyz.driver.pdsuidomain.entities._ + +object patienthypothesis { + import DefaultJsonProtocol._ + import common._ + + def applyUpdateToPatientHypothesis(json: JsValue, orig: PatientHypothesis): PatientHypothesis = json match { + case JsObject(fields) => + val rationale = if (fields.contains("rationale")) { + fields.get("rationale").map(_.convertTo[String]) + } else orig.rationale + + orig.copy(rationale = rationale) + + case _ => deserializationError(s"Expected Json Object as partial PatientHypothesis, but got $json") + } + + implicit val patientHypothesisWriter: JsonWriter[(PatientHypothesis, Boolean)] = + new JsonWriter[(PatientHypothesis, Boolean)] { + override def write(obj: (PatientHypothesis, Boolean)): JsValue = { + val patientHypothesis = obj._1 + val isRationaleRequired = obj._2 + JsObject( + "id" -> patientHypothesis.id.toJson, + "patientId" -> patientHypothesis.patientId.toJson, + "hypothesisId" -> patientHypothesis.hypothesisId.toJson, + "matchedTrials" -> patientHypothesis.matchedTrials.toJson, + "rationale" -> patientHypothesis.rationale.toJson, + "isRationaleRequired" -> isRationaleRequired.toJson + ) + } + } + +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/patientissue.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/patientissue.scala new file mode 100644 index 0000000..7d35bd1 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/patientissue.scala @@ -0,0 +1,54 @@ +package xyz.driver.pdsuidomain.formats.json.sprayformats + +import java.time.LocalDateTime + +import spray.json._ +import xyz.driver.pdsuicommon.domain.{LongId, StringId, User, UuidId} +import xyz.driver.pdsuidomain.entities._ + +object patientissue { + import DefaultJsonProtocol._ + import common._ + + def applyUpdateToPatientIssue(json: JsValue, orig: PatientIssue): PatientIssue = { + json.asJsObject.getFields("text", "archiveRequired") match { + case Seq(text, archiveRequired) => + orig.copy( + text = text.convertTo[String], + archiveRequired = archiveRequired.convertTo[Boolean] + ) + + case _ => deserializationError(s"Expected Json Object as partial PatientIssue, but got $json") + } + } + + def jsValueToPatientIssue(json: JsValue, patientId: UuidId[Patient], userId: StringId[User]): PatientIssue = { + json.asJsObject.getFields("text") match { + case Seq(text) => + PatientIssue( + id = LongId(0), + userId = userId, + patientId = patientId, + lastUpdate = LocalDateTime.MIN, + isDraft = true, + text = text.convertTo[String], + archiveRequired = false + ) + + case _ => deserializationError(s"Expected Json Object as PatientIssue, but got $json") + } + + } + + implicit val patientIssueWriter = new JsonWriter[PatientIssue] { + override def write(obj: PatientIssue) = JsObject( + "id" -> obj.id.toJson, + "text" -> obj.text.toJson, + "lastUpdate" -> obj.lastUpdate.toJson, + "userId" -> obj.userId.toJson, + "isDraft" -> obj.isDraft.toJson, + "archiveRequired" -> obj.archiveRequired.toJson + ) + } + +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/patientlabel.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/patientlabel.scala new file mode 100644 index 0000000..3b52833 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/patientlabel.scala @@ -0,0 +1,64 @@ +package xyz.driver.pdsuidomain.formats.json.sprayformats + +import spray.json._ +import xyz.driver.pdsuicommon.domain.FuzzyValue +import xyz.driver.pdsuidomain.entities._ + +object patientlabel { + import DefaultJsonProtocol._ + import common._ + + def applyUpdateToPatientLabel(json: JsValue, orig: PatientLabel): PatientLabel = json match { + case JsObject(fields) => + val primaryValue = fields + .get("primaryValue") + .map(_.convertTo[Option[FuzzyValue]]) + .getOrElse(orig.primaryValue) + + val verifiedPrimaryValue = fields + .get("verifiedPrimaryValue") + .map(_.convertTo[Option[FuzzyValue]]) + .getOrElse(orig.verifiedPrimaryValue) + + orig.copy( + primaryValue = primaryValue, + verifiedPrimaryValue = verifiedPrimaryValue + ) + + case _ => deserializationError(s"Expected Json Object as PatientLabel, but got $json") + } + + implicit val patientLabelWriter: JsonWriter[(PatientLabel, Boolean)] = new JsonWriter[(PatientLabel, Boolean)] { + override def write(obj: (PatientLabel, Boolean)): JsValue = { + val patientLabel = obj._1 + val isVerified = obj._2 + JsObject( + "id" -> patientLabel.id.toJson, + "labelId" -> patientLabel.labelId.toJson, + "primaryValue" -> patientLabel.primaryValue.toJson, + "verifiedPrimaryValue" -> patientLabel.verifiedPrimaryValue.toJson, + "score" -> patientLabel.score.toJson, + "isImplicitMatch" -> patientLabel.isImplicitMatch.toJson, + "isVisible" -> patientLabel.isVisible.toJson, + "isVerified" -> isVerified.toJson + ) + } + } + + implicit val patientLabelEvidenceWriter: JsonWriter[PatientLabelEvidenceView] = + new JsonWriter[PatientLabelEvidenceView] { + override def write(evidence: PatientLabelEvidenceView): JsValue = + JsObject( + "id" -> evidence.id.toJson, + "value" -> evidence.value.toJson, + "evidenceText" -> evidence.evidenceText.toJson, + "documentId" -> evidence.documentId.toJson, + "evidenceId" -> evidence.evidenceId.toJson, + "reportId" -> evidence.reportId.toJson, + "documentType" -> evidence.documentType.toJson, + "date" -> evidence.date.toJson, + "providerType" -> evidence.providerType.toJson + ) + } + +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/record.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/record.scala new file mode 100644 index 0000000..e378dbd --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/record.scala @@ -0,0 +1,237 @@ +package xyz.driver.pdsuidomain.formats.json.sprayformats + +import java.time.LocalDateTime +import java.util.UUID + +import spray.json._ +import xyz.driver.core.json.{EnumJsonFormat, GadtJsonFormat} +import xyz.driver.pdsuicommon.domain.{LongId, TextJson, UuidId} +import xyz.driver.pdsuidomain.entities.MedicalRecord.Meta._ +import xyz.driver.pdsuidomain.entities._ + +object record { + import DefaultJsonProtocol._ + import MedicalRecord._ + import common._ + + implicit val recordStatusFormat = new EnumJsonFormat[Status]( + "Unprocessed" -> Status.Unprocessed, + "PreOrganized" -> Status.PreOrganized, + "New" -> Status.New, + "Cleaned" -> Status.Cleaned, + "PreOrganized" -> Status.PreOrganized, + "PreOrganizing" -> Status.PreOrganizing, + "Reviewed" -> Status.Reviewed, + "Organized" -> Status.Organized, + "Done" -> Status.Done, + "Flagged" -> Status.Flagged, + "Archived" -> Status.Archived + ) + + implicit val requestIdFormat = new RootJsonFormat[RecordRequestId] { + override def write(requestId: RecordRequestId): JsString = JsString(requestId.toString) + override def read(json: JsValue): RecordRequestId = json match { + case JsString(value) => RecordRequestId(UUID.fromString(value)) + case _ => deserializationError(s"Expected string as RecordRequestId, but got $json") + } + } + + implicit val caseIdFormat = new RootJsonFormat[CaseId] { + override def write(caseId: CaseId): JsString = JsString(caseId.toString) + override def read(json: JsValue): CaseId = json match { + case JsString(value) => CaseId(value) + case _ => deserializationError(s"Expected string as CaseId, but got $json") + } + } + + implicit val duplicateMetaFormat: RootJsonFormat[Duplicate] = new RootJsonFormat[Duplicate] { + override def write(obj: Duplicate) = + JsObject( + "type" -> "duplicate".toJson, + "predicted" -> obj.predicted.toJson, + "startPage" -> obj.startPage.toJson, + "endPage" -> obj.endPage.toJson, + "startOriginalPage" -> obj.startOriginalPage.toJson, + "endOriginalPage" -> obj.endOriginalPage.toJson + ) + + override def read(json: JsValue): Duplicate = json match { + case JsObject(fields) => + val predicted = fields + .get("predicted") + .map(_.convertTo[Boolean]) + + val startPage = fields + .get("startPage") + .map(_.convertTo[Double]) + .getOrElse(deserializationError(s"Duplicate meta json object does not contain `startPage` field: $json")) + + val endPage = fields + .get("endPage") + .map(_.convertTo[Double]) + .getOrElse(deserializationError(s"Duplicate meta json object does not contain `endPage` field: $json")) + + val startOriginalPage = fields + .get("startOriginalPage") + .map(_.convertTo[Double]) + .getOrElse( + deserializationError(s"Duplicate meta json object does not contain `startOriginalPage` field: $json")) + + val endOriginalPage = fields + .get("endOriginalPage") + .map(_.convertTo[Double]) + + Duplicate( + predicted = predicted, + startPage = startPage, + endPage = endPage, + startOriginalPage = startOriginalPage, + endOriginalPage = endOriginalPage + ) + + case _ => deserializationError(s"Expected JsObject as Duplicate meta of medical record, but got $json") + } + } + + implicit val reorderMetaFormat: RootJsonFormat[Reorder] = new RootJsonFormat[Reorder] { + override def write(obj: Reorder) = + JsObject( + "type" -> "reorder".toJson, + "predicted" -> obj.predicted.toJson, + "items" -> obj.items.toJson + ) + + override def read(json: JsValue): Reorder = json match { + case JsObject(fields) => + val predicted = fields + .get("predicted") + .map(_.convertTo[Boolean]) + + val items = fields + .get("items") + .map(_.convertTo[Seq[Int]]) + .getOrElse(deserializationError(s"Reorder meta json object does not contain `items` field: $json")) + + Reorder( + predicted = predicted, + items = items + ) + + case _ => deserializationError(s"Expected JsObject as Reorder meta of medical record, but got $json") + } + } + + implicit val rotateMetaFormat: RootJsonFormat[Rotation] = new RootJsonFormat[Rotation] { + override def write(obj: Rotation) = + JsObject( + "type" -> "rotation".toJson, + "predicted" -> obj.predicted.toJson, + "items" -> obj.items.toJson + ) + + override def read(json: JsValue): Rotation = json match { + case JsObject(fields) => + val predicted = fields + .get("predicted") + .map(_.convertTo[Boolean]) + + val items = fields + .get("items") + .map(_.convertTo[Map[String, Int]]) + .getOrElse(deserializationError(s"Rotation meta json object does not contain `items` field: $json")) + + Rotation( + predicted = predicted, + items = items + ) + + case _ => deserializationError(s"Expected JsObject as Rotation meta of medical record, but got $json") + } + } + + implicit val recordMetaTypeFormat: GadtJsonFormat[MedicalRecord.Meta] = { + GadtJsonFormat.create[Meta]("type")({ case m => m.metaType }) { + case "duplicate" => duplicateMetaFormat + case "reorder" => reorderMetaFormat + case "rotation" => rotateMetaFormat + } + } + + implicit val recordMetaFormat = new RootJsonFormat[TextJson[List[Meta]]] { + override def write(obj: TextJson[List[Meta]]): JsArray = JsArray(obj.content.map(_.toJson).toVector) + override def read(json: JsValue): TextJson[List[Meta]] = json match { + case JsArray(values) => TextJson[List[Meta]](values.map(_.convertTo[Meta]).toList) + case _ => deserializationError(s"Expected array as Meta, but got $json") + } + } + + implicit val recordFormat: RootJsonFormat[MedicalRecord] = + new RootJsonFormat[MedicalRecord] { + override def write(record: MedicalRecord): JsValue = + JsObject( + "id" -> record.id.id.toJson, + "patientId" -> record.patientId.toJson, + "caseId" -> record.caseId.toJson, + "disease" -> record.disease.toJson, + "physician" -> record.physician.toJson, + "status" -> record.status.toJson, + "previousStatus" -> record.previousStatus.toJson, + "assignee" -> record.assignee.toJson, + "previousAssignee" -> record.previousAssignee.toJson, + "requestId" -> record.requestId.toJson, + "meta" -> record.meta.getOrElse(TextJson[List[Meta]](List.empty)).toJson, + "lastActiveUser" -> record.lastActiveUserId.toJson, + "lastUpdate" -> record.lastUpdate.toJson + ) + + override def read(json: JsValue): MedicalRecord = json match { + case JsObject(fields) => + val disease = fields + .get("disease") + .map(_.convertTo[String]) + .getOrElse(deserializationError(s"MedicalRecord json object does not contain `disease` field: $json")) + + val patientId = fields + .get("patientId") + .map(_.convertTo[UuidId[Patient]]) + .getOrElse(deserializationError(s"MedicalRecord json object does not contain `patientId` field: $json")) + + val requestId = fields + .get("requestId") + .map(_.convertTo[RecordRequestId]) + .getOrElse(deserializationError(s"MedicalRecord json object does not contain `requestId` field: $json")) + + MedicalRecord( + id = LongId(0), + status = MedicalRecord.Status.New, + previousStatus = None, + assignee = None, + previousAssignee = None, + lastActiveUserId = None, + patientId = patientId, + requestId = requestId, + disease = disease, + caseId = None, + physician = None, + meta = None, + predictedMeta = None, + predictedDocuments = None, + lastUpdate = LocalDateTime.now() + ) + + case _ => deserializationError(s"Expected Json Object as MedicalRecord, but got $json") + } + } + + def applyUpdateToMedicalRecord(json: JsValue, orig: MedicalRecord): MedicalRecord = json match { + case JsObject(fields) => + val meta = fields + .get("meta") + .map(_.convertTo[Option[TextJson[List[Meta]]]]) + .getOrElse(orig.meta) + orig.copy(meta = meta) + + case _ => deserializationError(s"Expected Json Object as partial MedicalRecord, but got $json") + } + +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/recordhistory.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/recordhistory.scala new file mode 100644 index 0000000..bd14d43 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/recordhistory.scala @@ -0,0 +1,30 @@ +package xyz.driver.pdsuidomain.formats.json.sprayformats + +import spray.json._ +import xyz.driver.core.json.EnumJsonFormat +import xyz.driver.pdsuidomain.entities._ + +object recordhistory { + import DefaultJsonProtocol._ + import common._ + import MedicalRecordHistory._ + + implicit val recordStateFormat = new EnumJsonFormat[State]( + "Clean" -> State.Clean, + "Organize" -> State.Organize, + "Review" -> State.Review, + "Flag" -> State.Flag + ) + + implicit val recordActionFormat = new EnumJsonFormat[Action]( + "Start" -> Action.Start, + "Submit" -> Action.Submit, + "Unassign" -> Action.Unassign, + "Resolve" -> Action.Resolve, + "Flag" -> Action.Flag, + "Archive" -> Action.Archive + ) + + implicit val recordHistoryFormat: RootJsonFormat[MedicalRecordHistory] = jsonFormat6(MedicalRecordHistory.apply) + +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/recordissue.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/recordissue.scala new file mode 100644 index 0000000..4ae04d0 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/recordissue.scala @@ -0,0 +1,79 @@ +package xyz.driver.pdsuidomain.formats.json.sprayformats + +import java.time.LocalDateTime + +import spray.json._ +import xyz.driver.pdsuicommon.domain.{LongId, StringId, User} +import xyz.driver.pdsuidomain.entities._ + +object recordissue { + import DefaultJsonProtocol._ + import common._ + + def applyUpdateToRecordIssue(json: JsValue, orig: MedicalRecordIssue): MedicalRecordIssue = json match { + case JsObject(fields) => + val text = fields + .get("text") + .map(_.convertTo[String]) + .getOrElse(deserializationError(s"MedicalRecordIssue json object does not contain `text` field: $json")) + + val archiveRequired = fields + .get("archiveRequired") + .map(_.convertTo[Boolean]) + .getOrElse( + deserializationError(s"MedicalRecordIssue json object does not contain `archiveRequired` field: $json")) + + val startPage = fields.get("startPage").map(_.convertTo[Double]) + val endPage = fields.get("endPage").map(_.convertTo[Double]) + + orig.copy( + text = text, + archiveRequired = archiveRequired, + startPage = startPage, + endPage = endPage + ) + + case _ => deserializationError(s"Expected Json Object as partial MedicalRecordIssue, but got $json") + + } + + def jsValueToRecordIssue(json: JsValue, + recordId: LongId[MedicalRecord], + userId: StringId[User]): MedicalRecordIssue = json match { + case JsObject(fields) => + val text = fields + .get("text") + .map(_.convertTo[String]) + .getOrElse(deserializationError(s"MedicalRecordIssue json object does not contain `text` field: $json")) + + val startPage = fields.get("startPage").map(_.convertTo[Double]) + val endPage = fields.get("endPage").map(_.convertTo[Double]) + MedicalRecordIssue( + id = LongId(0), + userId = userId, + recordId = recordId, + lastUpdate = LocalDateTime.MIN, + isDraft = true, + text = text, + archiveRequired = false, + startPage = startPage, + endPage = endPage + ) + + case _ => deserializationError(s"Expected Json Object as MedicalRecordIssue, but got $json") + } + + implicit val recordIssueWriter = new JsonWriter[MedicalRecordIssue] { + override def write(obj: MedicalRecordIssue) = JsObject( + "id" -> obj.id.toJson, + "startPage" -> obj.startPage.toJson, + "endPage" -> obj.endPage.toJson, + "text" -> obj.text.toJson, + "lastUpdate" -> obj.lastUpdate.toJson, + "userId" -> obj.userId.toJson, + "isDraft" -> obj.isDraft.toJson, + "archiveRequired" -> obj.archiveRequired.toJson + ) + } + +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/studydesign.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/studydesign.scala new file mode 100644 index 0000000..e801666 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/studydesign.scala @@ -0,0 +1,12 @@ +package xyz.driver.pdsuidomain.formats.json.sprayformats + +import spray.json._ +import xyz.driver.pdsuidomain.entities._ + +object studydesign { + import DefaultJsonProtocol._ + import common._ + + implicit val studyDesignFormat: RootJsonFormat[StudyDesign] = jsonFormat2(StudyDesign.apply) + +} 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 new file mode 100644 index 0000000..c1751bf --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/trial.scala @@ -0,0 +1,90 @@ +package xyz.driver.pdsuidomain.formats.json.sprayformats + +import java.time.{ZoneId, ZonedDateTime} + +import spray.json._ +import xyz.driver.core.json.EnumJsonFormat +import xyz.driver.pdsuicommon.domain.{LongId, UuidId} +import xyz.driver.pdsuidomain.entities._ + +object trial { + import DefaultJsonProtocol._ + import common._ + import Trial._ + + implicit val trialStatusFormat = new EnumJsonFormat[Status]( + "New" -> Status.New, + "ReviewSummary" -> Status.ReviewSummary, + "Summarized" -> Status.Summarized, + "PendingUpdate" -> Status.PendingUpdate, + "Update" -> Status.Update, + "ReviewCriteria" -> Status.ReviewCriteria, + "Done" -> Status.Done, + "Flagged" -> Status.Flagged, + "Archived" -> Status.Archived + ) + + implicit val conditionFormat = new EnumJsonFormat[Condition]( + "Breast" -> Condition.Breast, + "Lung" -> Condition.Lung, + "Prostate" -> Condition.Prostate + ) + + implicit val trialWriter: JsonWriter[Trial] = new JsonWriter[Trial] { + override def write(obj: Trial) = + JsObject( + "id" -> obj.id.toJson, + "externalid" -> obj.externalId.toJson, + "lastUpdate" -> ZonedDateTime.of(obj.lastUpdate, ZoneId.of("Z")).toJson, + "status" -> obj.status.toJson, + "assignee" -> obj.assignee.toJson, + "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, + "originalStudyDesignId" -> obj.originalStudyDesign.toJson, + "isPartner" -> obj.isPartner.toJson, + "overview" -> obj.overview.toJson, + "overviewTemplate" -> obj.overviewTemplate.toJson, + "isUpdated" -> obj.isUpdated.toJson, + "title" -> obj.title.toJson, + "originalTitle" -> obj.originalTitle.toJson + ) + } + + def applyUpdateToTrial(json: JsValue, orig: Trial): Trial = json match { + case JsObject(fields) => + val hypothesisId = fields + .get("hypothesisId") + .map(_.convertTo[Option[UuidId[Hypothesis]]]) + .getOrElse(orig.hypothesisId) + + val studyDesignId = fields + .get("studyDesignId") + .map(_.convertTo[Option[LongId[StudyDesign]]]) + .getOrElse(orig.studyDesignId) + + val overview = fields + .get("overview") + .map(_.convertTo[Option[String]]) + .getOrElse(orig.overview) + + val title = fields + .get("title") + .map(_.convertTo[Option[String]].getOrElse("")) + .getOrElse(orig.title) + + orig.copy( + hypothesisId = hypothesisId, + studyDesignId = studyDesignId, + overview = overview, + title = title + ) + + case _ => deserializationError(s"Expected Json Object as Trial, but got $json") + } + +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/trialhistory.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/trialhistory.scala new file mode 100644 index 0000000..844c5f5 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/trialhistory.scala @@ -0,0 +1,30 @@ +package xyz.driver.pdsuidomain.formats.json.sprayformats + +import spray.json._ +import xyz.driver.core.json.EnumJsonFormat +import xyz.driver.pdsuidomain.entities._ + +object trialhistory { + import DefaultJsonProtocol._ + import common._ + import TrialHistory._ + + implicit val trialStateFormat = new EnumJsonFormat[State]( + "Summarize" -> State.Summarize, + "Criteriarize" -> State.Criteriarize, + "Review" -> State.Review, + "Flag" -> State.Flag + ) + + implicit val trialActionFormat = new EnumJsonFormat[Action]( + "Start" -> Action.Start, + "Submit" -> Action.Submit, + "Unassign" -> Action.Unassign, + "Resolve" -> Action.Resolve, + "Flag" -> Action.Flag, + "Archive" -> Action.Archive + ) + + implicit val trialHistoryFormat: RootJsonFormat[TrialHistory] = jsonFormat6(TrialHistory.apply) + +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/trialissue.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/trialissue.scala new file mode 100644 index 0000000..572f44d --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/sprayformats/trialissue.scala @@ -0,0 +1,60 @@ +package xyz.driver.pdsuidomain.formats.json.sprayformats + +import java.time.LocalDateTime + +import spray.json._ +import xyz.driver.pdsuicommon.domain.{LongId, StringId, User} +import xyz.driver.pdsuidomain.entities._ + +object trialissue { + import DefaultJsonProtocol._ + import common._ + + def applyUpdateToTrialIssue(json: JsValue, orig: TrialIssue): TrialIssue = { + json.asJsObject.getFields("text", "evidence", "archiveRequired", "meta") match { + case Seq(text, evidence, archiveRequired, meta) => + orig.copy( + text = text.convertTo[String], + evidence = evidence.convertTo[String], + archiveRequired = archiveRequired.convertTo[Boolean], + meta = meta.convertTo[String] + ) + + case _ => deserializationError(s"Expected Json Object as partial TrialIssue, but got $json") + } + } + + def jsValueToTrialIssue(json: JsValue, trialId: StringId[Trial], userId: StringId[User]): TrialIssue = { + json.asJsObject.getFields("text", "evidence", "meta") match { + case Seq(text, evidence, meta) => + TrialIssue( + id = LongId(0), + userId = userId, + trialId = trialId, + lastUpdate = LocalDateTime.MIN, + isDraft = true, + text = text.convertTo[String], + evidence = evidence.convertTo[String], + archiveRequired = false, + meta = meta.convertTo[String] + ) + + case _ => deserializationError(s"Expected Json Object as TrialIssue, but got $json") + } + + } + + implicit val trialIssueWriter = new JsonWriter[TrialIssue] { + override def write(obj: TrialIssue) = JsObject( + "id" -> obj.id.toJson, + "text" -> obj.text.toJson, + "lastUpdate" -> obj.lastUpdate.toJson, + "userId" -> obj.userId.toJson, + "isDraft" -> obj.isDraft.toJson, + "evidence" -> obj.evidence.toJson, + "archiveRequired" -> obj.archiveRequired.toJson, + "meta" -> obj.meta.toJson + ) + } + +} |