diff options
Diffstat (limited to 'src/main/scala/xyz/driver/pdsuidomain/formats/json/document')
5 files changed, 249 insertions, 0 deletions
diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/document/ApiDocument.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/document/ApiDocument.scala new file mode 100644 index 0000000..be9c65b --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/document/ApiDocument.scala @@ -0,0 +1,71 @@ +package xyz.driver.pdsuidomain.formats.json.document + +import java.time.{LocalDate, ZoneId, ZonedDateTime} + +import xyz.driver.pdsuidomain.entities._ +import play.api.data.validation.ValidationError +import play.api.libs.functional.syntax._ +import play.api.libs.json._ +import xyz.driver.pdsuicommon.json.JsonSerializer + +final case class ApiDocument(id: Long, + recordId: Long, + physician: Option[String], + lastUpdate: Option[ZonedDateTime], + typeId: Option[Long], + startDate: Option[LocalDate], + endDate: Option[LocalDate], + provider: Option[String], + providerTypeId: Option[Long], + status: Option[String], + previousStatus: Option[String], + assignee: Option[Long], + previousAssignee: Option[Long], + meta: Option[String]) + +object ApiDocument { + + private val statusFormat = Format( + Reads.StringReads.filter(ValidationError("unknown status")) { + case x if DocumentUtils.statusFromString.isDefinedAt(x) => true + case _ => false + }, + Writes.StringWrites + ) + + implicit val format: Format[ApiDocument] = ( + (JsPath \ "id").format[Long] and + (JsPath \ "recordId").format[Long] and + (JsPath \ "physician").formatNullable[String] and + (JsPath \ "lastUpdate").formatNullable[ZonedDateTime] and + (JsPath \ "typeId").formatNullable[Long] and + (JsPath \ "startDate").formatNullable[LocalDate] and + (JsPath \ "endDate").formatNullable[LocalDate] and + (JsPath \ "provider").formatNullable[String] and + (JsPath \ "providerTypeId").formatNullable[Long] and + (JsPath \ "status").formatNullable(statusFormat) and + (JsPath \ "previousStatus").formatNullable(statusFormat) and + (JsPath \ "assignee").formatNullable[Long] and + (JsPath \ "previousAssignee").formatNullable[Long] and + (JsPath \ "meta").formatNullable(Format(Reads { x => JsSuccess(Json.stringify(x)) }, Writes[String](Json.parse))) + ) (ApiDocument.apply, unlift(ApiDocument.unapply)) + + def fromDomain(document: Document): ApiDocument = { + ApiDocument( + id = document.id.id, + recordId = document.recordId.id, + physician = document.physician, + lastUpdate = Option(document.lastUpdate).map(ZonedDateTime.of(_, ZoneId.of("Z"))), + typeId = document.typeId.map(_.id), + startDate = document.startDate, + endDate = document.endDate, + provider = document.providerName, + providerTypeId = document.providerTypeId.map(_.id), + status = Option(DocumentUtils.statusToString(document.status)), + previousStatus = document.previousStatus.map(DocumentUtils.statusToString), + assignee = document.assignee.map(_.id), + previousAssignee = document.previousAssignee.map(_.id), + meta = document.meta.map(meta => JsonSerializer.serialize(meta.content)) + ) + } +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/document/ApiDocumentType.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/document/ApiDocumentType.scala new file mode 100644 index 0000000..e00da20 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/document/ApiDocumentType.scala @@ -0,0 +1,20 @@ +package xyz.driver.pdsuidomain.formats.json.document + +import play.api.libs.functional.syntax._ +import play.api.libs.json.{Format, JsPath} +import xyz.driver.pdsuidomain.entities.DocumentType + +final case class ApiDocumentType(id: Long, name: String) + +object ApiDocumentType { + + implicit val format: Format[ApiDocumentType] = ( + (JsPath \ "id").format[Long] and + (JsPath \ "name").format[String] + ) (ApiDocumentType.apply, unlift(ApiDocumentType.unapply)) + + def fromDomain(documentType: DocumentType) = ApiDocumentType( + id = documentType.id.id, + name = documentType.name + ) +} 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 new file mode 100644 index 0000000..7682bb5 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/document/ApiPartialDocument.scala @@ -0,0 +1,114 @@ +package xyz.driver.pdsuidomain.formats.json.document + +import java.time.{LocalDate, LocalDateTime} + +import xyz.driver.pdsuicommon.domain.{LongId, TextJson} +import xyz.driver.pdsuidomain.entities.Document.Meta +import xyz.driver.pdsuidomain.entities._ +import org.davidbild.tristate.Tristate +import org.davidbild.tristate.contrib.play.ToJsPathOpsFromJsPath +import play.api.data.validation._ +import play.api.libs.functional.syntax._ +import play.api.libs.json._ +import xyz.driver.pdsuicommon.json.{JsonSerializer, JsonValidationException} +import xyz.driver.pdsuicommon.validation.{AdditionalConstraints, JsonValidationErrors} + +import scala.collection.breakOut +import scala.util.Try + +final case class ApiPartialDocument(recordId: Option[Long], + physician: Option[String], + typeId: Tristate[Long], + startDate: Tristate[LocalDate], + endDate: Tristate[LocalDate], + provider: Tristate[String], + providerTypeId: Tristate[Long], + status: Option[String], + assignee: Tristate[Long], + meta: Tristate[String]) { + + import xyz.driver.pdsuicommon.domain.User + + def applyTo(orig: Document): Document = Document( + id = orig.id, + status = status.map(DocumentUtils.statusFromString).getOrElse(orig.status), + previousStatus = orig.previousStatus, + assignee = assignee.map(LongId[User]).cata(Some(_), None, orig.assignee), + previousAssignee = orig.previousAssignee, + recordId = recordId.map(LongId[MedicalRecord]).getOrElse(orig.recordId), + physician = physician.orElse(orig.physician), + typeId = typeId.map(LongId[DocumentType]).cata(Some(_), None, orig.typeId), + providerName = provider.cata(Some(_), None, orig.providerName), + providerTypeId = providerTypeId.map(LongId[ProviderType]).cata(Some(_), None, orig.providerTypeId), + meta = meta.cata(x => Some(TextJson(JsonSerializer.deserialize[Meta](x))), None, orig.meta), + startDate = startDate.cata(Some(_), None, orig.startDate), + endDate = endDate.cata(Some(_), None, orig.endDate), + lastUpdate = LocalDateTime.MIN // Should update internally in a business logic module + ) + + def toDomain: Try[Document] = Try { + val validation = Map(JsPath \ "recordId" -> AdditionalConstraints.optionNonEmptyConstraint(recordId)) + + val validationErrors: JsonValidationErrors = validation.collect({ + case (fieldName, e: Invalid) => (fieldName, e.errors) + })(breakOut) + + if (validationErrors.isEmpty) { + Document( + id = LongId(0), + recordId = recordId.map(LongId[MedicalRecord]).get, + status = Document.Status.New, + physician = physician, + typeId = typeId.map(LongId[DocumentType]).toOption, + startDate = startDate.toOption, + endDate = endDate.toOption, + providerName = provider.toOption, + providerTypeId = providerTypeId.map(LongId[ProviderType]).toOption, + meta = meta.map(x => TextJson(JsonSerializer.deserialize[Meta](x))).toOption, + previousStatus = None, + assignee = None, + previousAssignee = None, + lastUpdate = LocalDateTime.MIN + ) + } else { + throw new JsonValidationException(validationErrors) + } + } +} + +object ApiPartialDocument { + + private val reads: Reads[ApiPartialDocument] = ( + (JsPath \ "recordId").readNullable[Long] and + (JsPath \ "physician").readNullable[String] and + (JsPath \ "typeId").readTristate[Long] and + (JsPath \ "startDate").readTristate[LocalDate] and + (JsPath \ "endDate").readTristate[LocalDate] and + (JsPath \ "provider").readTristate[String] and + (JsPath \ "providerTypeId").readTristate[Long] and + (JsPath \ "status").readNullable[String](Reads.of[String].filter(ValidationError("unknown status"))({ + case x if DocumentUtils.statusFromString.isDefinedAt(x) => true + case _ => false + })) and + (JsPath \ "assignee").readTristate[Long] and + (JsPath \ "meta").readTristate(Reads { x => JsSuccess(Json.stringify(x)) }).map { + case Tristate.Present("{}") => Tristate.Absent + case x => x + } + ) (ApiPartialDocument.apply _) + + private val writes: Writes[ApiPartialDocument] = ( + (JsPath \ "recordId").writeNullable[Long] and + (JsPath \ "physician").writeNullable[String] and + (JsPath \ "typeId").writeTristate[Long] and + (JsPath \ "startDate").writeTristate[LocalDate] and + (JsPath \ "endDate").writeTristate[LocalDate] and + (JsPath \ "provider").writeTristate[String] and + (JsPath \ "providerTypeId").writeTristate[Long] and + (JsPath \ "status").writeNullable[String] and + (JsPath \ "assignee").writeTristate[Long] and + (JsPath \ "meta").writeTristate(Writes[String](Json.parse)) + ) (unlift(ApiPartialDocument.unapply)) + + implicit val format: Format[ApiPartialDocument] = Format(reads, writes) +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/document/ApiProviderType.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/document/ApiProviderType.scala new file mode 100644 index 0000000..7b370ba --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/document/ApiProviderType.scala @@ -0,0 +1,20 @@ +package xyz.driver.pdsuidomain.formats.json.document + +import play.api.libs.functional.syntax._ +import play.api.libs.json.{Format, JsPath} +import xyz.driver.pdsuidomain.entities.ProviderType + +final case class ApiProviderType(id: Long, name: String) + +object ApiProviderType { + + implicit val format: Format[ApiProviderType] = ( + (JsPath \ "id").format[Long] and + (JsPath \ "name").format[String] + ) (ApiProviderType.apply, unlift(ApiProviderType.unapply)) + + def fromDomain(providerType: ProviderType) = ApiProviderType( + id = providerType.id.id, + name = providerType.name + ) +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/document/DocumentUtils.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/document/DocumentUtils.scala new file mode 100644 index 0000000..87e449f --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/document/DocumentUtils.scala @@ -0,0 +1,24 @@ +package xyz.driver.pdsuidomain.formats.json.document + +import xyz.driver.pdsuidomain.entities.Document.Status + +object DocumentUtils { + + val statusFromString: PartialFunction[String, Status] = { + case "New" => Status.New + case "Organized" => Status.Organized + case "Extracted" => Status.Extracted + case "Done" => Status.Done + case "Flagged" => Status.Flagged + case "Archived" => Status.Archived + } + + def statusToString(x: Status): String = x match { + case Status.New => "New" + case Status.Organized => "Organized" + case Status.Extracted => "Extracted" + case Status.Done => "Done" + case Status.Flagged => "Flagged" + case Status.Archived => "Archived" + } +} |