diff options
author | vlad <vlad@driver.xyz> | 2017-06-13 16:12:20 -0700 |
---|---|---|
committer | vlad <vlad@driver.xyz> | 2017-06-13 16:12:20 -0700 |
commit | cd1b635b2ae90d9ac2d8b1779183a1fbd8c5fd5c (patch) | |
tree | 062e8dad1a1513e26b0fd08b1742d6ff2ee874f7 /src/main/scala/xyz/driver/pdsuidomain/entities | |
parent | 0000a65ab4479a2a40e2d6468036438e9705b4aa (diff) | |
download | rest-query-cd1b635b2ae90d9ac2d8b1779183a1fbd8c5fd5c.tar.gz rest-query-cd1b635b2ae90d9ac2d8b1779183a1fbd8c5fd5c.tar.bz2 rest-query-cd1b635b2ae90d9ac2d8b1779183a1fbd8c5fd5c.zip |
Adding domain entitiesv0.1.0
Diffstat (limited to 'src/main/scala/xyz/driver/pdsuidomain/entities')
30 files changed, 1397 insertions, 0 deletions
diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/Arm.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/Arm.scala new file mode 100644 index 0000000..17a913d --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/Arm.scala @@ -0,0 +1,21 @@ +package xyz.driver.pdsuidomain.entities + +import java.time.LocalDateTime + +import xyz.driver.pdsuicommon.domain.{LongId, StringId} +import xyz.driver.pdsuicommon.logging._ + +case class Arm(id: LongId[Arm], + name: String, + originalName: String, + trialId: StringId[Trial], + deleted: Option[LocalDateTime] = None) + +object Arm { + + implicit def toPhiString(x: Arm): PhiString = { + import x._ + phi"Arm(id=$id, name=${Unsafe(x.name)}, trialId=${Unsafe(x.trialId)})" + } +} + diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/CaseId.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/CaseId.scala new file mode 100644 index 0000000..751545e --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/CaseId.scala @@ -0,0 +1,10 @@ +package xyz.driver.pdsuidomain.entities + +import java.util.UUID + +final case class CaseId(id: String) + +object CaseId { + + def apply() = new CaseId(UUID.randomUUID().toString) +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/Criterion.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/Criterion.scala new file mode 100644 index 0000000..0dfb33f --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/Criterion.scala @@ -0,0 +1,55 @@ +package xyz.driver.pdsuidomain.entities + +import xyz.driver.pdsuicommon.domain.{LongId, StringId} +import xyz.driver.pdsuicommon.logging._ +import xyz.driver.pdsuidomain.entities.Criterion.Meta.Evidence + +final case class Criterion(id: LongId[Criterion], + trialId: StringId[Trial], + text: Option[String], + isCompound: Boolean, + meta: String) { + + def isValid(): Boolean = text.nonEmpty && Option(meta).isDefined +} + +object Criterion { + + final case class Meta(evidence: Evidence) + + object Meta { + final case class Evidence(pageRatio: Double, start: TextLayerPosition, end: TextLayerPosition) + final case class TextLayerPosition(page: Integer, index: Integer, offset: Integer) + } + + implicit def toPhiString(x: Criterion): PhiString = { + import x._ + phi"Criterion(id=$id, trialId=$trialId, isCompound=$isCompound)" + } +} + +final case class CriterionArm(criterionId: LongId[Criterion], armId: LongId[Arm]) + +object CriterionArm { + + implicit def toPhiString(x: CriterionArm): PhiString = { + import x._ + phi"CriterionArm(criterionId=$criterionId, armId=$armId)" + } +} + +final case class CriterionLabel(id: LongId[CriterionLabel], + labelId: Option[LongId[Label]], + criterionId: LongId[Criterion], + categoryId: Option[LongId[Category]], + value: Option[Boolean], + isDefining: Boolean) + +object CriterionLabel { + + implicit def toPhiString(x: CriterionLabel): PhiString = { + import x._ + phi"CriterionLabel(id=$id, labelId=$labelId, criterionId=$criterionId, " + + phi"categoryId=$categoryId, value=$value, isDefining=$isDefining)" + } +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/DirectReport.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/DirectReport.scala new file mode 100644 index 0000000..c9d1d88 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/DirectReport.scala @@ -0,0 +1,42 @@ +package xyz.driver.pdsuidomain.entities + +import java.time.LocalDateTime + +import xyz.driver.pdsuicommon.domain.UuidId +import xyz.driver.pdsuicommon.logging._ +import xyz.driver.pdsuicommon.utils.Utils +import xyz.driver.pdsuidomain.entities.DirectReport.ReportType + +object DirectReport { + + sealed trait ReportType extends Product with Serializable { + def oneOf(xs: ReportType*): Boolean = xs.contains(this) + + def oneOf(xs: Set[ReportType]): Boolean = xs.contains(this) + } + + object ReportType { + case object IHC extends ReportType + case object DNA extends ReportType + case object CFDNA extends ReportType + + val All = Set(IHC, DNA, CFDNA) + implicit def toPhiString(x: ReportType): PhiString = Unsafe(Utils.getClassSimpleName(x.getClass)) + } + + implicit def toPhiString(x: DirectReport): PhiString = { + import x._ + phi"DirectReport(id=$id, patientId=$patientId, reportType=$reportType, date=${Unsafe(date)}, " + + phi"documentType=${Unsafe(documentType)}, providerType=${Unsafe(providerType)}, " + + phi"providerName=${Unsafe(providerName)})" + } + +} + +case class DirectReport(id: UuidId[DirectReport], + patientId: UuidId[Patient], + reportType: ReportType, + date: LocalDateTime, + documentType: String, + providerType: String, + providerName: String) diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/Document.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/Document.scala new file mode 100644 index 0000000..8c2616a --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/Document.scala @@ -0,0 +1,153 @@ +package xyz.driver.pdsuidomain.entities + +import java.time.LocalDateTime + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import com.fasterxml.jackson.core.{JsonGenerator, JsonParser} +import com.fasterxml.jackson.databind._ +import com.fasterxml.jackson.databind.annotation.{JsonDeserialize, JsonSerialize} +import xyz.driver.pdsuicommon.domain.{LongId, TextJson, User} +import xyz.driver.pdsuicommon.logging._ +import xyz.driver.pdsuicommon.utils.Utils +import xyz.driver.pdsuicommon.validation.Validators +import xyz.driver.pdsuicommon.validation.Validators.Validator +import xyz.driver.pdsuidomain.entities.Document.Meta +import xyz.driver.pdsuicommon.compat.Implicits._ + + +final case class ProviderType(id: LongId[ProviderType], name: String) + +object ProviderType { + implicit def toPhiString(x: ProviderType): PhiString = { + import x._ + phi"ProviderType(id=$id, category=${Unsafe(name)})" + } +} + +final case class DocumentType(id: LongId[DocumentType], name: String) + +object DocumentType { + implicit def toPhiString(x: DocumentType): PhiString = { + import x._ + phi"DocumentType(id=$id, name=${Unsafe(name)})" + } +} + +object Document { + + case class Meta(predicted: Option[Boolean], startPage: Double, endPage: Double) { + /** + * Return a regular meta: this meta is considered as not predicted + */ + def confirmed: Meta = copy(predicted = predicted.map(_ => false)) + } + + class DocumentStatusSerializer extends JsonSerializer[Status] { + def serialize(value: Status, gen: JsonGenerator, serializers: SerializerProvider): Unit = { + gen.writeString(value.toString.toUpperCase) + } + } + + class DocumentStatusDeserializer extends JsonDeserializer[Status] { + def deserialize(parser: JsonParser, context: DeserializationContext): Status = { + val value = parser.getValueAsString + Option(value).fold[Document.Status](Status.New /* Default document status */) { v => + Status.All.find(_.toString.toUpperCase == v) match { + case None => throw new RuntimeJsonMappingException(s"$v is not valid Document.Status") + case Some(status) => status + } + } + } + } + + // Product with Serializable fixes issue: + // Set(New, Organized) has type Set[Status with Product with Serializable] + @JsonDeserialize(using = classOf[DocumentStatusDeserializer]) + @JsonSerialize(using = classOf[DocumentStatusSerializer]) + sealed trait Status extends Product with Serializable { + + def oneOf(xs: Status*): Boolean = xs.contains(this) + + def oneOf(xs: Set[Status]): Boolean = xs.contains(this) + + } + object Status { + case object New extends Status + case object Organized extends Status + case object Extracted extends Status + case object Done extends Status + case object Flagged extends Status + case object Archived extends Status + + val All = Set[Status](New, Organized, Extracted, Done, Flagged, Archived) + val AllPrevious = Set[Status](Organized, Extracted) + + implicit def toPhiString(x: Status): PhiString = Unsafe(Utils.getClassSimpleName(x.getClass)) + } + + implicit def toPhiString(x: Document): PhiString = { + import x._ + phi"Document(id=$id, status=$status, assignee=$assignee, previousAssignee=$previousAssignee, recordId=$recordId)" + } + + val validator: Validator[Document, Document] = { input => + for { + typeId <- Validators.nonEmpty("typeId")(input.typeId) + + providerTypeId <- Validators.nonEmpty("providerTypeId")(input.providerTypeId) + + meta <- Validators.nonEmpty("meta")(input.meta) + + startDate <- Validators.nonEmpty("startDate")(input.startDate) + + isOrderRight <- input.endDate match { + case Some(endDate) if startDate.isAfter(endDate) => + Validators.fail("The start date should be less, than the end one") + + case _ => Validators.success(true) + } + + areDatesInThePast <- { + val dates = List(input.startDate, input.endDate).flatten + val now = LocalDateTime.now() + val hasInvalid = dates.exists(_.isAfter(now)) + + if (hasInvalid) Validators.fail("Dates should be in the past") + else Validators.success(true) + } + } yield input + } + +} + +@JsonIgnoreProperties(value = Array("valid")) +case class Document(id: LongId[Document] = LongId(0L), + status: Document.Status, + previousStatus: Option[Document.Status], + assignee: Option[LongId[User]], + previousAssignee: Option[LongId[User]], + recordId: LongId[MedicalRecord], + physician: Option[String], + typeId: Option[LongId[DocumentType]], // not null + providerName: Option[String], // not null + providerTypeId: Option[LongId[ProviderType]], // not null + meta: Option[TextJson[Meta]], // not null + startDate: Option[LocalDateTime], // not null + endDate: Option[LocalDateTime], + lastUpdate: LocalDateTime) { + + import Document.Status._ + + if (previousStatus.nonEmpty) { + assert(AllPrevious.contains(previousStatus.get), + s"Previous status has invalid value: ${previousStatus.get}") + } + + // TODO: with the current business logic code this constraint sometimes harmful + // require(status match { + // case Document.Status.New if assignee.isDefined => false + // case Document.Status.Done if assignee.isDefined => false + // case _ => true + // }, "Assignee can't be defined in New or Done statuses") + +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/ExtractedData.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/ExtractedData.scala new file mode 100644 index 0000000..9972142 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/ExtractedData.scala @@ -0,0 +1,48 @@ +package xyz.driver.pdsuidomain.entities + +import xyz.driver.pdsuicommon.domain.{FuzzyValue, LongId, TextJson} +import xyz.driver.pdsuicommon.logging._ +import xyz.driver.pdsuidomain.entities.ExtractedData.Meta + +final case class ExtractedData(id: LongId[ExtractedData] = LongId(0L), + documentId: LongId[Document], + keywordId: Option[LongId[Keyword]], + evidenceText: Option[String], + meta: Option[TextJson[Meta]]) { + + def isValid: Boolean = evidenceText.getOrElse("") != "" && meta.nonEmpty + +} + +object ExtractedData { + + final case class Meta(keyword: Meta.Keyword, evidence: Meta.Evidence) + + object Meta { + + final case class Evidence(pageRatio: Double, start: TextLayerPosition, end: TextLayerPosition) + + final case class TextLayerPosition(page: Integer, index: Integer, offset: Integer) + + final case class Keyword(page: Integer, pageRatio: Option[Double], index: Integer, sortIndex: String) + } + + implicit def toPhiString(x: ExtractedData): PhiString = { + import x._ + phi"ExtractedData(id=$id, documentId=$documentId, keywordId=$keywordId)" + } +} + +object ExtractedDataLabel { + + implicit def toPhiString(x: ExtractedDataLabel): PhiString = { + import x._ + phi"ExtractedDataLabel(id=$id, dataId=$dataId, labelId=$labelId, categoryId=$categoryId, value=$value)" + } +} + +final case class ExtractedDataLabel(id: LongId[ExtractedDataLabel], + dataId: LongId[ExtractedData], + labelId: Option[LongId[Label]], + categoryId: Option[LongId[Category]], + value: Option[FuzzyValue]) diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/Hypothesis.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/Hypothesis.scala new file mode 100644 index 0000000..eb2b95e --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/Hypothesis.scala @@ -0,0 +1,16 @@ +package xyz.driver.pdsuidomain.entities + +import xyz.driver.pdsuicommon.domain.UuidId +import xyz.driver.pdsuicommon.logging._ + +final case class Hypothesis(id: UuidId[Hypothesis], + name: String, + treatmentType: String, + description: String) + +object Hypothesis { + implicit def toPhiString(x: Hypothesis): PhiString = { + import x._ + phi"Hypothesis(id=$id, name=${Unsafe(name)})" + } +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/Intervention.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/Intervention.scala new file mode 100644 index 0000000..6ff1a67 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/Intervention.scala @@ -0,0 +1,48 @@ +package xyz.driver.pdsuidomain.entities + +import xyz.driver.pdsuicommon.domain.{LongId, StringId} +import xyz.driver.pdsuicommon.logging._ + + +final case class InterventionType(id: LongId[InterventionType], name: String) + +object InterventionType { + implicit def toPhiString(x: InterventionType): PhiString = { + import x._ + phi"InterventionType(id=$id, name=${Unsafe(name)})" + } +} + +final case class InterventionArm(armId: LongId[Arm], interventionId: LongId[Intervention]) + +object InterventionArm { + implicit def toPhiString(x: InterventionArm): PhiString = { + import x._ + phi"InterventionArm(armId=$armId, interventionId=$interventionId)" + } +} + +final case class Intervention(id: LongId[Intervention], + trialId: StringId[Trial], + name: String, + originalName: String, + typeId: Option[LongId[InterventionType]], + originalType: Option[String], + description: String, + isActive: Boolean) + +object Intervention { + implicit def toPhiString(x: Intervention): PhiString = { + import x._ + phi"Intervention(id=$id, trialId=$trialId, name=${Unsafe(name)}, typeId=$typeId, isActive=$isActive)" + } +} + +final case class InterventionWithArms(intervention: Intervention, arms: List[InterventionArm]) + +object InterventionWithArms { + implicit def toPhiString(x: InterventionWithArms): PhiString = { + import x._ + phi"InterventionWithArms(intervention=$intervention, arms=$arms)" + } +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/Keyword.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/Keyword.scala new file mode 100644 index 0000000..3683efc --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/Keyword.scala @@ -0,0 +1,31 @@ +package xyz.driver.pdsuidomain.entities + +import xyz.driver.pdsuicommon.domain.LongId +import xyz.driver.pdsuicommon.logging._ + +final case class Keyword(id: LongId[Keyword], keyword: String) + +object Keyword { + implicit def toPhiString(x: Keyword): PhiString = { + import x._ + phi"Keyword(id=$id, keyword=${Unsafe(keyword)})" + } +} + +final case class KeywordWithLabels(keyword: Keyword, labels: List[Label]) + +object KeywordWithLabels { + implicit def toPhiString(x: KeywordWithLabels): PhiString = { + import x._ + phi"KeywordWithLabels(keyword=$keyword, $labels)" + } +} + +final case class KeywordLabel(keywordId: LongId[Keyword], labelId: LongId[Label]) + +object KeywordLabel { + implicit def toPhiString(x: KeywordLabel): PhiString = { + import x._ + phi"KeywordLabel($keywordId, $labelId)" + } +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/Label.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/Label.scala new file mode 100644 index 0000000..7f9c2aa --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/Label.scala @@ -0,0 +1,34 @@ +package xyz.driver.pdsuidomain.entities + +import xyz.driver.pdsuicommon.domain.LongId +import xyz.driver.pdsuicommon.logging._ + +final case class Category(id: LongId[Category], name: String) + +object Category { + implicit def toPhiString(x: Category): PhiString = { + import x._ + phi"Category(id=$id, name=${Unsafe(name)})" + } +} + +final case class Label(id: LongId[Label], + categoryId: LongId[Category], + name: String, + description: String) + +object Label { + implicit def toPhiString(x: Label): PhiString = { + import x._ + phi"Label($id, categoryId=${Unsafe(categoryId)}, name=${Unsafe(name)}, description=${Unsafe(description)})" + } +} + +final case class CategoryWithLabels(category: Category, labels: List[Label]) + +object CategoryWithLabels { + implicit def toPhiString(x: CategoryWithLabels): PhiString = { + import x._ + phi"CategoryWithLabels(category=$category, labels=$labels)" + } +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/MedicalRecord.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/MedicalRecord.scala new file mode 100644 index 0000000..977f4cc --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/MedicalRecord.scala @@ -0,0 +1,164 @@ +package xyz.driver.pdsuidomain.entities + +import java.nio.channels.ReadableByteChannel +import java.time.LocalDateTime + +import com.fasterxml.jackson.annotation.JsonSubTypes.Type +import com.fasterxml.jackson.annotation.{JsonProperty, JsonSubTypes, JsonTypeInfo} +import xyz.driver.pdsuicommon.domain.{LongId, TextJson, User, UuidId} +import xyz.driver.pdsuicommon.logging._ +import xyz.driver.pdsuicommon.utils.Utils +import xyz.driver.pdsuidomain.entities.MedicalRecord.Meta +import xyz.driver.pdsuidomain.entities.MedicalRecord.Meta.{Duplicate, Reorder, Rotation} + +object MedicalRecord { + + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") + @JsonSubTypes(Array( + new Type(value = classOf[Duplicate], name = "duplicate"), + new Type(value = classOf[Reorder], name = "reorder"), + new Type(value = classOf[Rotation], name = "rotation") + )) + trait Meta { + @JsonProperty("type") def metaType: String + def predicted: Option[Boolean] + + /** + * Return a regular meta: this meta is considered as not predicted + */ + def confirmed: Meta + } + + object Meta { + + case class Duplicate(predicted: Option[Boolean], + startPage: Double, + endPage: Double, + startOriginalPage: Double, + endOriginalPage: Option[Double] + ) extends Meta { + override val metaType = "duplicate" + override def confirmed: Duplicate = copy(predicted = predicted.map(_ => false)) + } + + object Duplicate { + implicit def toPhiString(x: Duplicate): PhiString = { + import x._ + phi"Duplicate(predicted=${x.predicted}, startPage=${Unsafe(startPage)}, endPage=${Unsafe(endPage)}, " + + phi"startOriginalPage=${Unsafe(startOriginalPage)}, endOriginalPage=${Unsafe(endOriginalPage)}" + } + } + + + case class Reorder(predicted: Option[Boolean], + items: Seq[Int] + ) extends Meta { + override val metaType = "reorder" + override def confirmed: Reorder = copy(predicted = predicted.map(_ => false)) + } + + object Reorder { + implicit def toPhiString(x: Reorder): PhiString = { + import x._ + phi"Reorder(predicted=${x.predicted}, items=${Unsafe(items.toString)})" + } + } + + + case class Rotation(predicted: Option[Boolean], + items: Map[String, Int] + ) extends Meta { + override val metaType = "rotation" + override def confirmed: Rotation = copy(predicted = predicted.map(_ => false)) + } + + object Rotation { + implicit def toPhiString(x: Rotation): PhiString = { + import x._ + phi"Rotation(predicted=${x.predicted}, items=${Unsafe(items.toString)})" + } + } + + + implicit def toPhiString(input: Meta): PhiString = input match { + case x: Duplicate => Duplicate.toPhiString(x) + case x: Reorder => Reorder.toPhiString(x) + case x: Rotation => Rotation.toPhiString(x) + } + + } + + // Product with Serializable fixes issue: + // Set(New, Cleaned) has type Set[Status with Product with Serializable] + sealed trait Status extends Product with Serializable { + + def oneOf(xs: Status*): Boolean = xs.contains(this) + + def oneOf(xs: Set[Status]): Boolean = xs.contains(this) + + } + object Status { + case object Unprocessed extends Status + case object PreCleaning extends Status + case object New extends Status + case object Cleaned extends Status + case object PreOrganized extends Status + case object PreOrganizing extends Status + case object Reviewed extends Status + case object Organized extends Status + case object Done extends Status + case object Flagged extends Status + case object Archived extends Status + + val All = Set[Status]( + Unprocessed, PreCleaning, New, Cleaned, PreOrganized, PreOrganizing, Reviewed, Organized, Done, Flagged, Archived + ) + + val AllPrevious = Set[Status](New, Cleaned, Reviewed, Organized) + + implicit def toPhiString(x: Status): PhiString = Unsafe(Utils.getClassSimpleName(x.getClass)) + } + + sealed trait PdfSource + + object PdfSource { + case object Empty extends PdfSource + /** @param createResource Constructor of the resource which is represents the file */ + case class Channel(createResource: () => ReadableByteChannel) extends PdfSource + } + + implicit def toPhiString(x: MedicalRecord): PhiString = { + import x._ + phi"MedicalRecord(id=$id, status=$status, assignee=$assignee, previousAssignee=$previousAssignee)" + } +} + +case class MedicalRecord(id: LongId[MedicalRecord], + status: MedicalRecord.Status, + previousStatus: Option[MedicalRecord.Status], + assignee: Option[LongId[User]], + previousAssignee: Option[LongId[User]], + patientId: UuidId[Patient], + requestId: RecordRequestId, + disease: String, + caseId: Option[CaseId], + physician: Option[String], + sourceName: String, + meta: Option[TextJson[List[Meta]]], + predictedMeta: Option[TextJson[List[Meta]]], + predictedDocuments: Option[TextJson[List[Document]]], + lastUpdate: LocalDateTime) { + + import MedicalRecord.Status._ + + if (previousStatus.nonEmpty) { + assert(AllPrevious.contains(previousStatus.get), + s"Previous status has invalid value: ${previousStatus.get}") + } + + // TODO: with the current business logic code this constraint sometimes harmful + // require(status match { + // case MedicalRecord.Status.Done if assignee.isDefined => false + // case _ => true + // }, "Assignee can't be defined in Done status") +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/Message.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/Message.scala new file mode 100644 index 0000000..2a73922 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/Message.scala @@ -0,0 +1,34 @@ +package xyz.driver.pdsuidomain.entities + +import java.time.LocalDateTime + +import xyz.driver.pdsuicommon.domain.{LongId, StringId, User, UuidId} +import xyz.driver.pdsuicommon.logging._ + +final case class Message(id: LongId[Message], + text: String, + lastUpdate: LocalDateTime, + userId: LongId[User], + isDraft: Boolean, + recordId: Option[LongId[MedicalRecord]], + documentId: Option[LongId[Document]], + patientId: Option[UuidId[Patient]], + trialId: Option[StringId[Trial]], + startPage: Option[Double], + endPage: Option[Double], + evidence: Option[String], + archiveRequired: Option[Boolean], + meta: Option[String]) + +object Message { + implicit def toPhiString(x: Message): PhiString = { + import x._ + + val entityId = recordId + .orElse(documentId) + .orElse(patientId) + .map(_.toString) + + phi"Message(id=$id, userId=$userId, isDraft=$isDraft, entityId=${Unsafe(entityId)}" + } +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/Patient.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/Patient.scala new file mode 100644 index 0000000..7767db0 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/Patient.scala @@ -0,0 +1,63 @@ +package xyz.driver.pdsuidomain.entities + +import java.time.{LocalDate, LocalDateTime} + +import xyz.driver.pdsuicommon.domain.{LongId, User, UuidId} +import xyz.driver.pdsuicommon.logging._ +import xyz.driver.pdsuicommon.utils.Utils + +object Patient { + + // Product with Serizalizable fixes issue: + // Set(New, Verified) has type Set[Status with Product with Serializable] + sealed trait Status extends Product with Serializable { + def oneOf(xs: Status*): Boolean = xs.contains(this) + + def oneOf(xs: Set[Status]): Boolean = xs.contains(this) + } + + object Status { + case object New extends Status + case object Verified extends Status + case object Reviewed extends Status + case object Curated extends Status + case object Flagged extends Status + case object Done extends Status + + val AllPrevious = Set[Status]( + New, Verified, Reviewed, Curated + ) + + val All = Set[Status]( + New, Verified, Reviewed, Curated, Flagged, Done + ) + + implicit def toPhiString(x: Status): PhiString = Unsafe(Utils.getClassSimpleName(x.getClass)) + } + + implicit def toPhiString(x: Patient): PhiString = { + import x._ + phi"Patient(id=$id, status=$status, previousStatus=$previousStatus, " + + phi"assignee=$assignee, previousAssignee=$previousAssignee)" + } +} + +case class Patient(id: UuidId[Patient], + status: Patient.Status, + name: String, + dob: LocalDate, + assignee: Option[LongId[User]], + previousStatus: Option[Patient.Status], + previousAssignee: Option[LongId[User]], + isUpdateRequired: Boolean, + condition: String, + orderId: PatientOrderId, + lastUpdate: LocalDateTime) { + + import Patient.Status._ + + if (previousStatus.nonEmpty) { + assert(AllPrevious.contains(previousStatus.get), + s"Previous status has invalid value: ${previousStatus.get}") + } +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/PatientEligibleTrial.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/PatientEligibleTrial.scala new file mode 100644 index 0000000..8d5af0d --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/PatientEligibleTrial.scala @@ -0,0 +1,155 @@ +package xyz.driver.pdsuidomain.entities + +import java.time.LocalDateTime + +import xyz.driver.pdsuicommon.domain.{FuzzyValue, LongId, StringId, UuidId} +import xyz.driver.pdsuicommon.logging._ + +object PatientCriterion { + implicit def toPhiString(x: PatientCriterion): PhiString = { + import x._ + phi"PatientCriterion(id=$id, patientLabelId=$patientLabelId, trialId=${Unsafe(trialId)}, nctId=${Unsafe(nctId)}, " + + phi"criterionId=$criterionId, criterionValue=${Unsafe(criterionValue)}, " + + phi"isImplicitMatch=$criterionIsDefining), criterionIsDefining=${Unsafe(criterionIsDefining)}, " + + phi"eligibilityStatus=${Unsafe(eligibilityStatus)}, verifiedEligibilityStatus=${Unsafe(verifiedEligibilityStatus)}, " + + phi"isVerified=${Unsafe(isVerified)}, lastUpdate=${Unsafe(lastUpdate)}" + } + + /** + * @see https://driverinc.atlassian.net/wiki/display/MTCH/EV+Business+Process + */ + def getEligibilityStatus(criterionValue: Option[Boolean], + primaryValue: Option[FuzzyValue]): Option[FuzzyValue] = { + primaryValue match { + case None => None + case Some(FuzzyValue.Maybe) => Some(FuzzyValue.Maybe) + case Some(_) if criterionValue.isEmpty => Some(FuzzyValue.Maybe) + case Some(status) => Some(FuzzyValue.fromBoolean( + FuzzyValue.fromBoolean(criterionValue.getOrElse( + throw new IllegalArgumentException("Criterion should not be empty")) + ) == status + )) + } + } + +} + +/** + * @param eligibilityStatus - a value, that selects an eligibility verifier (EV) + * @param verifiedEligibilityStatus - a copy of eligibilityStatus, when a patient goes to routes curator (RC) + * @param isVerified - is EV selected the eligibilityStatus? + */ +final case class PatientCriterion(id: LongId[PatientCriterion], + patientLabelId: LongId[PatientLabel], + trialId: Long, + nctId: StringId[Trial], + criterionId: LongId[Criterion], + criterionText: String, + criterionValue: Option[Boolean], + criterionIsDefining: Boolean, + eligibilityStatus: Option[FuzzyValue], + verifiedEligibilityStatus: Option[FuzzyValue], + isVerified: Boolean, + isVisible: Boolean, + lastUpdate: LocalDateTime) { + def isIneligible: Boolean = eligibilityStatus.contains(FuzzyValue.No) && isVerified +} + +object PatientTrialArm { + + implicit def toPhiString(x: PatientTrialArm): PhiString = { + import x._ + phi"PatientTrialArm(armId=$armId, trialArmGroupId=$trialArmGroupId)" + } +} + +final case class PatientTrialArm(armId: LongId[Arm], trialArmGroupId: LongId[PatientTrialArmGroup]) + +object PatientEligibleTrial { + implicit def toPhiString(x: PatientEligibleTrial): PhiString = { + import x._ + phi"PatientEligibleTrial(id=$id, patientId=$patientId, trialId=$trialId, hypothesisId=$hypothesisId)" + } +} + +final case class PatientEligibleTrial(id: UuidId[PatientEligibleTrial], + patientId: UuidId[Patient], + trialId: StringId[Trial], + hypothesisId: UuidId[Hypothesis]) + +object PatientTrialArmGroup { + + implicit def toPhiString(x: PatientTrialArmGroup): PhiString = { + import x._ + phi"PatientTrialArmGroup(id=$id, eligibleTrialId=$eligibleTrialId, " + + phi"eligibilityStatus=${Unsafe(eligibilityStatus)}, isVerified=$isVerified)" + } + + /** + * @see https://driverinc.atlassian.net/wiki/display/DMPD/EV+Business+Process + */ + def getEligibilityStatusForEvToRc(criterionList: List[(Option[Boolean], Option[FuzzyValue])]): Option[FuzzyValue] = { + def isEligible = { + // Eligible, if for all (verified for EV) label-criteria eligibilityStatus=NULL or YES and at least one has status=YES + // If method executes from PatientService (when EV submit patient) need to check value PatientCriterion.isVerified + val verifiedList = criterionList.filter { case (isVerifiedOpt, _) => isVerifiedOpt.isEmpty || isVerifiedOpt.get } + verifiedList.forall { case (_, eligibilityStatus) => + eligibilityStatus.isEmpty || eligibilityStatus.contains(FuzzyValue.Yes) + } && verifiedList.exists { case (_, eligibilityStatus) => eligibilityStatus.contains(FuzzyValue.Yes) } + } + + if (criterionList.exists { case (isVerified, eligibilityStatus) => + eligibilityStatus.contains(FuzzyValue.No) && (isVerified.isEmpty || isVerified.get) + }) { Some(FuzzyValue.No) + } else if (criterionList.forall { case (_, eligibilityStatus) => eligibilityStatus.isEmpty }) { + None + } else if (isEligible) { + Some(FuzzyValue.Yes) + } else { + Some(FuzzyValue.Maybe) + } + } + + /** + * @see https://driverinc.atlassian.net/wiki/display/DMPD/EV+Business+Process + */ + def getEligibilityStatusForRc(criterionList: TraversableOnce[PatientCriterion]): Option[FuzzyValue] = { + def isEligible: Boolean = criterionList.forall(_.verifiedEligibilityStatus.contains(FuzzyValue.Yes)) + def isIneligible: Boolean = criterionList.exists(_.verifiedEligibilityStatus.contains(FuzzyValue.No)) + def isUnknown: Boolean = criterionList.forall(_.verifiedEligibilityStatus.isEmpty) + + if (isEligible) Some(FuzzyValue.Yes) + else if (isIneligible) Some(FuzzyValue.No) + else if (isUnknown) None + else Some(FuzzyValue.Maybe) + } +} + +final case class PatientTrialArmGroup(id: LongId[PatientTrialArmGroup], + eligibleTrialId: UuidId[PatientEligibleTrial], + eligibilityStatus: Option[FuzzyValue], + isVerified: Boolean) + +object PatientTrialArmGroupView { + + implicit def toPhiString(x: PatientTrialArmGroupView): PhiString = { + import x._ + phi"PatientTrialArmGroupView(id=$id, patientId=$patientId, trialId=$trialId, hypothesisId=$hypothesisId, " + + phi"eligibilityStatus=${Unsafe(eligibilityStatus)}, isVerified=$isVerified)" + } +} + +final case class PatientTrialArmGroupView(id: LongId[PatientTrialArmGroup], + patientId: UuidId[Patient], + trialId: StringId[Trial], + hypothesisId: UuidId[Hypothesis], + eligibilityStatus: Option[FuzzyValue], + isVerified: Boolean) { + + def applyTo(trialArmGroup: PatientTrialArmGroup) = { + trialArmGroup.copy( + eligibilityStatus = this.eligibilityStatus, + isVerified = this.isVerified + ) + } +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/PatientHypothesis.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/PatientHypothesis.scala new file mode 100644 index 0000000..146b1c3 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/PatientHypothesis.scala @@ -0,0 +1,18 @@ +package xyz.driver.pdsuidomain.entities + +import xyz.driver.pdsuicommon.domain.UuidId +import xyz.driver.pdsuicommon.logging._ + +object PatientHypothesis { + implicit def toPhiString(x: PatientHypothesis): PhiString = { + import x._ + phi"PatientHypothesis(id=$id, patientId=$patientId, hypothesisId=$hypothesisId, " + + phi"rationale=${Unsafe(rationale)}, matchedTrials=${Unsafe(matchedTrials)})" + } +} + +case class PatientHypothesis(id: UuidId[PatientHypothesis], + patientId: UuidId[Patient], + hypothesisId: UuidId[Hypothesis], + rationale: Option[String], + matchedTrials: Long) diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/PatientLabel.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/PatientLabel.scala new file mode 100644 index 0000000..633a347 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/PatientLabel.scala @@ -0,0 +1,38 @@ +package xyz.driver.pdsuidomain.entities + +import xyz.driver.pdsuicommon.domain.{FuzzyValue, LongId, UuidId} +import xyz.driver.pdsuicommon.logging._ + +object PatientLabel { + implicit def toPhiString(x: PatientLabel): PhiString = { + import x._ + phi"PatientLabel(id=$id, patientId=$patientId, labelId=$labelId, " + + phi"score=${Unsafe(score)}, primaryValue=${Unsafe(primaryValue)}, " + + phi"verifiedPrimaryValue=${Unsafe(verifiedPrimaryValue)})" + } +} + +final case class PatientLabel(id: LongId[PatientLabel], + patientId: UuidId[Patient], + labelId: LongId[Label], + score: Int, + primaryValue: Option[FuzzyValue], + verifiedPrimaryValue: Option[FuzzyValue], + isImplicitMatch: Boolean, + isVisible: Boolean) + +object PatientLabelEvidence { + implicit def toPhiString(x: PatientLabelEvidence): PhiString = { + import x._ + phi"PatientLabelEvidence(id=$id, patientLabelId=$patientLabelId, value=${Unsafe(value)}, " + + phi"documentId=$documentId, evidenceId=$evidenceId, reportId=$reportId)" + } +} + +final case class PatientLabelEvidence(id: LongId[PatientLabelEvidence], + patientLabelId: LongId[PatientLabel], + value: FuzzyValue, + evidenceText: String, + reportId: Option[UuidId[DirectReport]], + documentId: Option[LongId[Document]], + evidenceId: Option[LongId[ExtractedData]]) diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/PatientOrderId.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/PatientOrderId.scala new file mode 100644 index 0000000..50a97ce --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/PatientOrderId.scala @@ -0,0 +1,14 @@ +package xyz.driver.pdsuidomain.entities + +import java.util.UUID + +final case class PatientOrderId(id: UUID) { + override def toString: String = id.toString +} + +object PatientOrderId { + + def apply() = new PatientOrderId(UUID.randomUUID()) + + def apply(x: String) = new PatientOrderId(UUID.fromString(x)) +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/RawPatientDocument.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/RawPatientDocument.scala new file mode 100644 index 0000000..88e1a45 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/RawPatientDocument.scala @@ -0,0 +1,28 @@ +package xyz.driver.pdsuidomain.entities + +import java.time.LocalDateTime + +import xyz.driver.pdsuicommon.domain.{LongId, UuidId} +import xyz.driver.pdsuicommon.logging._ + +case class RawPatientDocument(disease: String, + patientId: UuidId[Patient], + requestId: RecordRequestId, + recordId: LongId[MedicalRecord], + recordStatus: MedicalRecord.Status, + documentId: LongId[Document], + documentType: String, + documentProviderType: String, + documentStartDate: LocalDateTime, + documentStatus: Document.Status) + +object RawPatientDocument { + + implicit def toPhiString(x: RawPatientDocument): PhiString = { + import x._ + phi"RawPatientDocument(disease=${Unsafe(disease)}, patientId=$patientId, requestId=$requestId, " + + phi"recordId=$recordId, recordStatus=$recordStatus, documentId=$documentId, " + + phi"documentType=${Unsafe(documentType)}, providerType=${Unsafe(documentProviderType)}, " + + phi"startDate=$documentStartDate, documentStatus=$documentStatus)" + } +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/RawPatientLabel.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/RawPatientLabel.scala new file mode 100644 index 0000000..e0cf06b --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/RawPatientLabel.scala @@ -0,0 +1,33 @@ +package xyz.driver.pdsuidomain.entities + +import java.time.LocalDateTime + +import xyz.driver.pdsuicommon.domain.{FuzzyValue, LongId, UuidId} +import xyz.driver.pdsuicommon.logging._ + +case class RawPatientLabel(patientId: UuidId[Patient], + labelId: LongId[Label], + label: String, + value: FuzzyValue, + evidenceId: LongId[ExtractedData], + evidenceText: String, + disease: String, + documentId: LongId[Document], + requestId: RecordRequestId, + documentType: String, + providerType: String, + startDate: LocalDateTime, + endDate: Option[LocalDateTime]) + +object RawPatientLabel { + + implicit def toPhiString(x: RawPatientLabel): PhiString = { + import x._ + phi"RawPatientLabel(patientId=$patientId, labelId=$labelId, label=${Unsafe(label)}, value=$value," + + phi"evidenceId=${Unsafe(evidenceId)}, " + + phi"evidenceText=${Unsafe(evidenceText)}, documentId=$documentId, requestId=${Unsafe(requestId)}, " + + phi"documentType=${Unsafe(documentType)}, providerType=${Unsafe(providerType)}, " + + phi"startDate=$startDate, endDate=$endDate)" + } + +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/RawTrialLabel.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/RawTrialLabel.scala new file mode 100644 index 0000000..e3b323f --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/RawTrialLabel.scala @@ -0,0 +1,32 @@ +package xyz.driver.pdsuidomain.entities + +import java.time.LocalDateTime + +import xyz.driver.pdsuicommon.domain.{LongId, StringId, UuidId} +import xyz.driver.pdsuicommon.logging._ + +case class RawTrialLabel(nctId: StringId[Trial], + trialId: UuidId[Trial], + condition: String, + lastReviewed: LocalDateTime, + armName: String, + armId: LongId[Arm], + labelId: LongId[Label], + label: String, + value: Option[Boolean], + criterionId: LongId[Criterion], + criteria: String, + criterionArmId: LongId[Arm], + isCompound: Boolean, + isDefining: Boolean) + +object RawTrialLabel { + + implicit def toPhiString(x: RawTrialLabel): PhiString = { + import x._ + phi"RawTrialLabel(nctId=$nctId, trialId=$trialId, condition=${Unsafe(condition)}, lastReviewed=$lastReviewed, " + + phi"armId=$armId, armName=${Unsafe(armName)}, labelId=$labelId, label=${Unsafe(label)}, value=$value, " + + phi"criterionId=$criterionId, criteria=${Unsafe(criteria)}, criterionArmId=$criterionArmId, " + + phi"isCompound=$isCompound, isDefining=$isDefining)" + } +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/RecordRequestId.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/RecordRequestId.scala new file mode 100644 index 0000000..5df87c2 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/RecordRequestId.scala @@ -0,0 +1,15 @@ +package xyz.driver.pdsuidomain.entities + +import java.util.UUID +import xyz.driver.pdsuicommon.logging._ + +final case class RecordRequestId(id: UUID) { + override def toString: String = id.toString +} + +object RecordRequestId { + + def apply() = new RecordRequestId(UUID.randomUUID()) + + implicit def toPhiString(x: RecordRequestId): PhiString = phi"${x.id}" +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/Trial.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/Trial.scala new file mode 100644 index 0000000..2477c22 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/Trial.scala @@ -0,0 +1,102 @@ +package xyz.driver.pdsuidomain.entities + +import java.nio.file.Path +import java.time.LocalDateTime + +import xyz.driver.pdsuicommon.domain.{LongId, StringId, User, UuidId} +import xyz.driver.pdsuicommon.logging._ +import xyz.driver.pdsuicommon.utils.Utils +import xyz.driver.pdsuidomain.entities.Trial.{Condition, Status} + + +final case class StudyDesign(id: LongId[StudyDesign], name: String) + +object StudyDesign { + implicit def toPhiString(x: StudyDesign): PhiString = { + import x._ + phi"StudyDesign(id=$id, category=${Unsafe(name)})" + } +} + +object Trial { + + sealed trait Status { + def oneOf(xs: Status*): Boolean = xs.contains(this) + def oneOf(xs: Set[Status]): Boolean = xs.contains(this) + } + + object Status { + case object New extends Status + case object ReviewSummary extends Status + case object Summarized extends Status + case object PendingUpdate extends Status + case object Update extends Status + case object ReviewCriteria extends Status + case object Done extends Status + case object Flagged extends Status + case object Archived extends Status + + val All = Set[Status]( + New, ReviewSummary, Summarized, PendingUpdate, Update, ReviewCriteria, Done, Flagged, Archived + ) + + val AllPrevious = Set[Status](New, ReviewSummary, Summarized, ReviewCriteria) + + implicit def toPhiString(x: Status): PhiString = Unsafe(Utils.getClassSimpleName(x.getClass)) + } + + case class PdfSource(path: Path) extends AnyVal + + implicit def toPhiString(x: Trial): PhiString = { + import x._ + phi"Trial(id=$id, externalId=$externalId, status=$status, previousStatus=$previousStatus, " + + phi"assignee=$assignee, previousAssignee=$previousAssignee, isSummaryReviewed=$isSummaryReviewed, " + + phi"isCriteriaReviewed=$isCriteriaReviewed)" + } + + case class Locations(locations: List[String]) + + sealed trait Condition + + object Condition { + + case object Breast extends Condition + case object Lung extends Condition + case object Prostate extends Condition + + val All = Set(Breast, Lung, Prostate) + } +} + +final case class Trial(id: StringId[Trial], + externalId: UuidId[Trial], + status: Status, + assignee: Option[LongId[User]], + previousStatus: Option[Status], + previousAssignee: Option[LongId[User]], + lastUpdate: LocalDateTime, + condition: Condition, + phase: String, + hypothesisId: Option[UuidId[Hypothesis]], + studyDesignId: Option[LongId[StudyDesign]], + originalStudyDesign: Option[String], + isPartner: Boolean, + overview: Option[String], + overviewTemplate: String, + isUpdated: Boolean, + title: String, + originalTitle: String, + isSummaryReviewed: Boolean, + isCriteriaReviewed: Boolean, + eligibilityCriteriaChecksum: String, + briefSummaryChecksum: String, + detailedDescriptionChecksum: String, + armDescriptionChecksum: String) { + + import Trial.Status._ + + if (previousStatus.nonEmpty) { + assert(AllPrevious.contains(previousStatus.get), + s"Previous status has invalid value: ${previousStatus.get}") + } +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/export/patient/ExportPatientLabel.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/export/patient/ExportPatientLabel.scala new file mode 100644 index 0000000..7d5de79 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/export/patient/ExportPatientLabel.scala @@ -0,0 +1,30 @@ +package xyz.driver.pdsuidomain.entities.export.patient + +import xyz.driver.pdsuicommon.domain.LongId +import xyz.driver.pdsuicommon.logging._ +import xyz.driver.pdsuidomain.entities.{Label, RawPatientLabel} + +case class ExportPatientLabel(id: LongId[Label], + name: String, + evidences: List[ExportPatientLabelEvidence]) + +object ExportPatientLabel extends PhiLogging { + + implicit def toPhiString(x: ExportPatientLabel): PhiString = { + import x._ + phi"ExportPatientLabel(id=$id, evidences=$evidences)" + } + + def fromRaw(labelId: LongId[Label], rawPatientLabels: List[RawPatientLabel]): ExportPatientLabel = { + val firstLabel = rawPatientLabels.headOption + if (firstLabel.isEmpty) { + logger.warn(phi"rawPatientLabels is empty, labelId: $labelId") + } + + ExportPatientLabel( + id = labelId, + name = firstLabel.map(_.label).getOrElse(""), + evidences = rawPatientLabels.map(ExportPatientLabelEvidence.fromRaw) + ) + } +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/export/patient/ExportPatientLabelEvidence.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/export/patient/ExportPatientLabelEvidence.scala new file mode 100644 index 0000000..ff0fb6c --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/export/patient/ExportPatientLabelEvidence.scala @@ -0,0 +1,32 @@ +package xyz.driver.pdsuidomain.entities.export.patient + +import xyz.driver.pdsuicommon.domain.{FuzzyValue, LongId} +import xyz.driver.pdsuicommon.logging._ +import xyz.driver.pdsuidomain.entities.{ExtractedData, RawPatientLabel} + +case class ExportPatientLabelEvidence(id: LongId[ExtractedData], + value: FuzzyValue, + evidenceText: String, + document: ExportPatientLabelEvidenceDocument) + +object ExportPatientLabelEvidence { + + implicit def toPhiString(x: ExportPatientLabelEvidence): PhiString = { + import x._ + phi"ExportPatientLabelEvidence(id=${Unsafe(id)}, value=$value, " + + phi"evidenceText=${Unsafe(evidenceText)}, document=$document)" + } + + def fromRaw(x: RawPatientLabel) = ExportPatientLabelEvidence( + id = x.evidenceId, + value = x.value, + evidenceText = x.evidenceText, + document = ExportPatientLabelEvidenceDocument( + x.documentId, + x.requestId, + x.documentType, + x.providerType, + x.startDate + ) + ) +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/export/patient/ExportPatientLabelEvidenceDocument.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/export/patient/ExportPatientLabelEvidenceDocument.scala new file mode 100644 index 0000000..d696569 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/export/patient/ExportPatientLabelEvidenceDocument.scala @@ -0,0 +1,30 @@ +package xyz.driver.pdsuidomain.entities.export.patient + +import java.time.LocalDateTime + +import xyz.driver.pdsuicommon.domain.LongId +import xyz.driver.pdsuicommon.logging._ +import xyz.driver.pdsuidomain.entities.{Document, RawPatientLabel, RecordRequestId} + +case class ExportPatientLabelEvidenceDocument(documentId: LongId[Document], + requestId: RecordRequestId, + documentType: String, + providerType: String, + date: LocalDateTime) + +object ExportPatientLabelEvidenceDocument extends PhiLogging { + + implicit def toPhiString(x: ExportPatientLabelEvidenceDocument): PhiString = { + import x._ + phi"ExportPatientLabelEvidenceDocument(documentId=$documentId, requestId=$requestId, " + + phi"documentType=${Unsafe(documentType)}, providerType=${Unsafe(providerType)}, date=$date)" + } + + def fromRaw(x: RawPatientLabel) = ExportPatientLabelEvidenceDocument( + documentId = x.documentId, + requestId = x.requestId, + documentType = x.documentType, + providerType = x.providerType, + date = x.startDate + ) +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/export/patient/ExportPatientWithLabels.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/export/patient/ExportPatientWithLabels.scala new file mode 100644 index 0000000..418f20b --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/export/patient/ExportPatientWithLabels.scala @@ -0,0 +1,26 @@ +package xyz.driver.pdsuidomain.entities.export.patient + +import xyz.driver.pdsuicommon.domain.UuidId +import xyz.driver.pdsuicommon.logging._ +import xyz.driver.pdsuidomain.entities.{Patient, RawPatientLabel} + +import scala.collection.breakOut + +case class ExportPatientWithLabels(patientId: UuidId[Patient], labelVersion: Long, labels: List[ExportPatientLabel]) + +object ExportPatientWithLabels { + + implicit def toPhiString(x: ExportPatientWithLabels): PhiString = { + import x._ + phi"ExportPatientWithLabels(patientId=$patientId, version=${Unsafe(labelVersion)}, labels=$labels)" + } + + def fromRaw(patientId: UuidId[Patient], + rawPatientRefs: List[RawPatientLabel]) = ExportPatientWithLabels( + patientId = patientId, + labelVersion = 1L, // TODO It is needed to replace this mock label version. + labels = rawPatientRefs + .groupBy(_.labelId) + .map(Function.tupled(ExportPatientLabel.fromRaw))(breakOut) + ) +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/export/trial/ExportTrial.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/export/trial/ExportTrial.scala new file mode 100644 index 0000000..a3a0e90 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/export/trial/ExportTrial.scala @@ -0,0 +1,27 @@ +package xyz.driver.pdsuidomain.entities.export.trial + +import java.time.LocalDateTime + +import xyz.driver.pdsuicommon.domain.{StringId, UuidId} +import xyz.driver.pdsuicommon.logging._ +import xyz.driver.pdsuidomain.entities.Trial + +case class ExportTrial(nctId: StringId[Trial], + trialId: UuidId[Trial], + condition: Trial.Condition, + lastReviewed: LocalDateTime) + +object ExportTrial { + + implicit def toPhiString(x: ExportTrial): PhiString = { + import x._ + phi"ExportTrial(nctId=$nctId, trialId=$trialId, condition=${Unsafe(condition)}, lastReviewed=$lastReviewed)" + } + + def fromDomain(x: Trial) = ExportTrial( + nctId = x.id, + trialId = x.externalId, + condition = x.condition, + lastReviewed = x.lastUpdate + ) +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/export/trial/ExportTrialArm.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/export/trial/ExportTrialArm.scala new file mode 100644 index 0000000..abdade4 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/export/trial/ExportTrialArm.scala @@ -0,0 +1,15 @@ +package xyz.driver.pdsuidomain.entities.export.trial + +import xyz.driver.pdsuicommon.domain.LongId +import xyz.driver.pdsuicommon.logging._ +import xyz.driver.pdsuidomain.entities.Arm + +case class ExportTrialArm(armId: LongId[Arm], armName: String) + +object ExportTrialArm { + + implicit def toPhiString(x: ExportTrialArm): PhiString = { + import x._ + phi"ExportTrialArm(armId=$armId, armName=${Unsafe(armName)})" + } +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/export/trial/ExportTrialLabelCriterion.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/export/trial/ExportTrialLabelCriterion.scala new file mode 100644 index 0000000..1dccaa5 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/export/trial/ExportTrialLabelCriterion.scala @@ -0,0 +1,32 @@ +package xyz.driver.pdsuidomain.entities.export.trial + +import xyz.driver.pdsuicommon.domain.LongId +import xyz.driver.pdsuicommon.logging._ +import xyz.driver.pdsuidomain.entities.{Arm, Criterion, Label, RawTrialLabel} + +case class ExportTrialLabelCriterion(criterionId: LongId[Criterion], + value: Option[Boolean], + labelId: LongId[Label], + armIds: Set[LongId[Arm]], + criteria: String, + isCompound: Boolean, + isDefining: Boolean) + +object ExportTrialLabelCriterion { + + implicit def toPhiString(x: ExportTrialLabelCriterion): PhiString = { + import x._ + phi"TrialLabelCriterion(criterionId=$criterionId, value=$value, labelId=$labelId, " + + phi"criteria=${Unsafe(criteria)}, isCompound=$isCompound, isDefining=$isDefining)" + } + + def fromRaw(x: RawTrialLabel, armIds: Set[LongId[Arm]]) = ExportTrialLabelCriterion( + criterionId = x.criterionId, + value = x.value, + labelId = x.labelId, + armIds = armIds, + criteria = x.criteria, + isCompound = x.isCompound, + isDefining = x.isDefining + ) +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/export/trial/ExportTrialWithLabels.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/export/trial/ExportTrialWithLabels.scala new file mode 100644 index 0000000..251f6fb --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/export/trial/ExportTrialWithLabels.scala @@ -0,0 +1,51 @@ +package xyz.driver.pdsuidomain.entities.export.trial + +import java.time.LocalDateTime + +import xyz.driver.pdsuicommon.domain.{StringId, UuidId} +import xyz.driver.pdsuicommon.logging._ +import xyz.driver.pdsuidomain.entities.{RawTrialLabel, Trial} + +import scala.collection.breakOut + +case class ExportTrialWithLabels(nctId: StringId[Trial], + trialId: UuidId[Trial], + condition: String, + lastReviewed: LocalDateTime, + labelVersion: Long, + arms: List[ExportTrialArm], + criteria: List[ExportTrialLabelCriterion]) + +object ExportTrialWithLabels { + + implicit def toPhiString(x: ExportTrialWithLabels): PhiString = { + import x._ + phi"TrialWithLabels(nctId=$nctId, trialId=$trialId, condition=${Unsafe(condition)}, " + + phi"lastReviewed=$lastReviewed, labelVersion=${Unsafe(labelVersion)}, arms=$arms, criteria=$criteria)" + } + + def fromRaw(rawData: List[RawTrialLabel]): ExportTrialWithLabels = { + val trials: Set[StringId[Trial]] = rawData.map(_.nctId)(breakOut) + + assert(trials.size == 1, "There are more than one trials in the rawData") + val trial = rawData.head + + ExportTrialWithLabels( + nctId = trial.nctId, + trialId = trial.trialId, + condition = trial.condition, + lastReviewed = trial.lastReviewed, + labelVersion = 1, // TODO It is needed to replace this mock label version. + arms = rawData.groupBy(_.armId).map { case (armId, rawTrials) => + ExportTrialArm(armId, rawTrials.head.armName) + }(breakOut), + criteria = rawData.groupBy { x => + (x.criterionId, x.labelId) + }.map { + case (_, rawTrialLabels) => + val armIds = rawTrialLabels.map(_.criterionArmId).toSet + ExportTrialLabelCriterion.fromRaw(rawTrialLabels.head, armIds) + }(breakOut) + ) + } +} |