From ec9fbcd4e622228e8b761b50d77ade2e114e2bf5 Mon Sep 17 00:00:00 2001 From: Aditya Khajuria Date: Tue, 17 Sep 2024 15:54:28 +0530 Subject: [PATCH] Review comments: Renamed api, removed classes not related to current supported activities --- .../fhir/workflow/activity/ActivityFlow.kt | 260 +++++++----------- .../fhir/workflow/activity/phase/Phase.kt | 14 +- .../activity/phase/event/PerformPhase.kt | 59 ++-- .../phase/request/BaseRequestPhase.kt | 28 +- .../activity/phase/request/OrderPhase.kt | 34 ++- .../activity/phase/request/PlanPhase.kt | 33 ++- .../activity/phase/request/ProposalPhase.kt | 12 +- .../activity/resource/event/CPGCaseEvent.kt | 37 --- .../resource/event/CPGCommunicationEvent.kt | 9 +- .../activity/resource/event/CPGCondition.kt | 44 --- .../resource/event/CPGEventForOrderService.kt | 68 ----- .../resource/event/CPGEventResource.kt | 51 ++-- .../resource/event/CPGImmunizationEvent.kt | 54 ---- .../event/CPGMedicationDispenseEvent.kt | 102 +++++++ .../resource/event/CPGOrderMedicationEvent.kt | 142 +--------- .../resource/event/EventStatusCodeMapper.kt | 64 +++++ .../request/CPGCommunicationRequest.kt | 7 +- .../request/CPGEnrollmentTaskRequest.kt | 23 -- .../resource/request/CPGGenerateReportTask.kt | 23 -- .../request/CPGImmunizationRequest.kt | 46 ---- .../resource/request/CPGMedicationRequest.kt | 24 +- .../request/CPGProposeDiagnosisTask.kt | 23 -- .../request/CPGQuestionnaireTaskRequest.kt | 23 -- .../request/CPGRecordDetectedIssueTask.kt | 23 -- .../request/CPGRecordInferenceTask.kt | 23 -- .../resource/request/CPGReportFlagTask.kt | 23 -- .../resource/request/CPGRequestResource.kt | 85 +++--- .../resource/request/CPGServiceRequest.kt | 44 --- .../resource/request/CPGTaskRequest.kt | 44 --- .../resource/request/StatusCodeMapper.kt | 64 +++++ .../workflow/activity/ActivityFlowTest.kt | 169 +++++++----- 31 files changed, 629 insertions(+), 1026 deletions(-) delete mode 100644 workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGCaseEvent.kt delete mode 100644 workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGCondition.kt delete mode 100644 workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGEventForOrderService.kt delete mode 100644 workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGImmunizationEvent.kt create mode 100644 workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGMedicationDispenseEvent.kt create mode 100644 workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/EventStatusCodeMapper.kt delete mode 100644 workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGEnrollmentTaskRequest.kt delete mode 100644 workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGGenerateReportTask.kt delete mode 100644 workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGImmunizationRequest.kt delete mode 100644 workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGProposeDiagnosisTask.kt delete mode 100644 workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGQuestionnaireTaskRequest.kt delete mode 100644 workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGRecordDetectedIssueTask.kt delete mode 100644 workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGRecordInferenceTask.kt delete mode 100644 workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGReportFlagTask.kt delete mode 100644 workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGServiceRequest.kt delete mode 100644 workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGTaskRequest.kt create mode 100644 workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/StatusCodeMapper.kt diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/ActivityFlow.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/ActivityFlow.kt index 9c2aa702d3..fd59aec149 100644 --- a/workflow/src/main/java/com/google/android/fhir/workflow/activity/ActivityFlow.kt +++ b/workflow/src/main/java/com/google/android/fhir/workflow/activity/ActivityFlow.kt @@ -16,7 +16,6 @@ package com.google.android.fhir.workflow.activity -import com.google.android.fhir.getResourceClass import com.google.android.fhir.workflow.activity.phase.Phase import com.google.android.fhir.workflow.activity.phase.Phase.PhaseName import com.google.android.fhir.workflow.activity.phase.Phase.PhaseName.ORDER @@ -34,157 +33,95 @@ import com.google.android.fhir.workflow.activity.resource.request.CPGCommunicati import com.google.android.fhir.workflow.activity.resource.request.CPGMedicationRequest import com.google.android.fhir.workflow.activity.resource.request.CPGRequestResource import com.google.android.fhir.workflow.activity.resource.request.Intent -import org.hl7.fhir.r4.model.IdType -import org.hl7.fhir.r4.model.Reference -import org.hl7.fhir.r4.model.Resource import org.opencds.cqf.fhir.api.Repository -internal val Reference.idType - get() = IdType(reference) - -internal val Reference.`class` - get() = getResourceClass(reference.split("/")[0]) - /** - * This abstracts the flow of various - * [CPG activities](https://build.fhir.org/ig/HL7/cqf-recommendations/activityflow.html#activity-lifecycle---request-phases-proposal-plan-order) - * throughout their various stages. - * - * The application developer may create the flow of a new proposal and move it through the various - * stages or they may resume the workflow of a request already in a later stage (plan,order). + * Manages the workflow of clinical recommendations according to the FHIR Clinical Practice + * Guidelines (CPG) specification. This class implements an + * [activity flow](https://build.fhir.org/ig/HL7/cqf-recommendations/activityflow.html#activity-lifecycle---request-phases-proposal-plan-order), + * allowing you to take proposals and guide them through various phases (proposal, plan, order, + * perform) of a clinical recommendation. You can also resume existing workflows from any phase. * - * The application developers should use the appropriate static factory [ActivityFlow.of] apis - * provided by the library to create or resume an activity flow. + * **Creating an ActivityFlow:** * - * An activity flow starts with the user creating the flow with an appropriate [CPGRequestResource]. - * The user may do valid state transitions on the current phase by calling appropriate apis on - * current phase. The user may call [getCurrentPhase] as appropriately cast it to [PlanPhase], - * [OrderPhase] or [PerformPhase] by either checking type or value of [Phase.getPhaseName]. - * - * The user may then move the activity to the next phase by creating a draft resource by calling - * appropriate draft api([draftPlan], [draftOrder] or [draftPerform]). The user may review and - * update the draft resource and then call appropriate start api([startPlan], [startOrder] or - * [startPerform]) to create a new activity phase. - * - * Since the perform creates a [CPGEventResource] and the same flow could create different event - * resources, application developer needs to provide the appropriate event type as a parameter to - * the [draftPerform]. - * - * Example of a `Order a Medication to dispense` where user completes only one [Phase] at a time and - * has to reload / resume the [ActivityFlow] to move to the next [Phase]. + * Use appropriate `ActivityFlow.of()` factory function to create an instance. You can start a new + * flow with a `CPGRequestResource` or resume an existing flow from a `CPGRequestResource` or + * `CPGEventResource` based on the last state of the flow. * + * ``` kotlin + * val request = CPGMedicationRequest(medicationRequestGeneratedByCarePlan) + * val flow = ActivityFlow.of(repository, request) * ``` - * val cpgMedicationRequest = - * CPGMedicationRequest( - * MedicationRequest().apply { - * id = "med-req-01" - * subject = Reference("Patient/pat-01") - * intent = MedicationRequest.MedicationRequestIntent.PROPOSAL - * meta.addProfile("http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-medicationrequest") - * status = MedicationRequest.MedicationRequestStatus.ACTIVE - * addNote(Annotation(MarkdownType("Proposal looks OK."))) - * }, - * ) - * - * val repository = FhirEngineRepository(FhirContext.forR4Cached(), fhirEngine) - * repository.create(cpgMedicationRequest.resource) - * - * val flow = ActivityFlow.of(repository, cpgMedicationRequest) - * - * var cachedRequestId = "" - * - * flow - * .draftPlan() - * .onSuccess { draftPlan -> - * draftPlan.update { addNote(Annotation(MarkdownType("Draft plan looks OK."))) } * - * flow.startPlan(draftPlan).onSuccess { planPhase -> - * val updatedPlan = - * planPhase.getRequest().copy().apply { - * setStatus(Status.ACTIVE) - * update { addNote(Annotation(MarkdownType("Plan looks OK."))) } - * } + * **Navigating Phases:** * - * planPhase - * .update(updatedPlan) - * .onSuccess { cachedRequestId = updatedPlan.logicalId } - * .onFailure { fail("Should have succeeded", it) } - * } - * } - * .onFailure { fail("Unexpected", it) } + * An `ActivityFlow` progresses through a series of phases, represented by the `Phase` class. You + * can access the current phase using `getCurrentPhase()`. * - * val planToResume = - * repository - * .read(MedicationRequest::class.java, IdType("MedicationRequest", cachedRequestId)) - * .let { CPGMedicationRequest(it) } - * val resumedPlanFlow = ActivityFlow.of(repository, planToResume) - * - * check(resumedPlanFlow.getCurrentPhase() is PlanPhase<*>) { - * "Flow is in ${resumedPlanFlow.getCurrentPhase().getPhaseName()} " - * } - * resumedPlanFlow - * .draftOrder() - * .onSuccess { draftOrder -> - * draftOrder.update { addNote(Annotation(MarkdownType("Draft order looks OK."))) } + * ``` kotlin + * when (val phase = flow.getCurrentPhase( ) ) { + * is Phase.ProposalPhase -> // Handle proposal phase + * is Phase.PlanPhase -> // Handle plan phase + * is Phase.OrderPhase -> // Handle order phase + * is Phase.PerformPhase -> // Handle perform phase + * } + * ``` * - * resumedPlanFlow.startOrder(draftOrder).onSuccess { orderPhase -> - * val updatedOrder = - * orderPhase.getRequest().copy().apply { - * setStatus(Status.ACTIVE) - * update { addNote(Annotation(MarkdownType("Order looks OK."))) } - * } + * **Transitioning Between Phases:** * - * orderPhase - * .update(updatedOrder) - * .onSuccess { cachedRequestId = updatedOrder.logicalId } - * .onFailure { fail("Should have succeeded", it) } - * } - * } - * .onFailure { fail("Unexpected", it) } + * [ActivityFlow] provides functions to prepare and initiate the next phase. + * * The prepare api creates a new request or event based on the phase and returns it back to you. + * It doesn't make any changes to the current phase request and also doesn't persist anything to + * the [repository]. + * * The initiate api creates a new phase based on the current phase and provided request/event. It + * does make changes to the current phase request and the provided request and persists them to + * the [repository]. For example, to move from the proposal phase to the plan phase: + * ``` kotlin + * val preparePlanResult = flow.getCurrentPhase( ).preparePlan() + * if (preparePlanResult.isFailure) { + * // Handle failure + * } * - * val orderToResume = - * repository - * .read(MedicationRequest::class.java, IdType("MedicationRequest", cachedRequestId)) - * .let { CPGMedicationRequest(it) } + * val preparedPlan = preparePlanResult.getOrThrow() + * // ... modify preparedPlan + * val planPhase = flow.getCurrentPhase().initiatePlan(preparedPlan) + * ``` * - * val resumedOrderFlow = ActivityFlow.of(repository, orderToResume) + * **Note:** The specific `prepare` and `initiate` functions available depend on the current phase. * - * check(resumedOrderFlow.getCurrentPhase() is OrderPhase<*>) { - * "Flow is in ${resumedOrderFlow.getCurrentPhase().getPhaseName()} " - * } + * **Transitioning to Perform Phase:** * - * resumedOrderFlow - * .draftPerform(CPGMedicationDispenseEvent::class.java) - * .onSuccess { draftEvent -> - * draftEvent.update { addNote(Annotation(MarkdownType("Draft event looks OK."))) } + * Since the perform creates a [CPGEventResource] and the same flow could create different event + * resources, you need to provide the appropriate event type as a parameter to the [preparePerform]. * - * resumedOrderFlow.startPerform(draftEvent).onSuccess { performPhase -> - * val updatedEvent = - * performPhase.getEvent().copy().apply { - * setStatus(EventStatus.INPROGRESS) - * update { addNote(Annotation(MarkdownType("Event looks OK."))) } - * } + * Example: + * ``` kotlin + * // Prepare and initiate the perform phase + * val preparedPerformEvent = flow.getCurrentPhase().preparePerform(CPGMedicationDispenseEvent::class.java) . getOrThrow( ) + * // update preparedPerformEvent + * val performPhase = flow.getCurrentPhase( ) . initiatePerform(preparedPerformEvent) . getOrThrow( ) + * ``` * - * performPhase - * .update(updatedEvent) - * .onSuccess { cachedRequestId = updatedEvent.logicalId } - * .onFailure { fail("Should have succeeded", it) } - * } - * } - * .onFailure { fail("Unexpected", it) } + * **Updating states in a phase:** * - * val eventToResume = - * repository - * .read(MedicationDispense::class.java, IdType("MedicationDispense", cachedRequestId)) - * .let { CPGMedicationDispenseEvent(it) } + * `ProposalPhase`, `PlanPhase` and `OrderPhase` are all a type of `Phase.RequestPhase` and allows + * you to update state of the request. * - * val resumedPerformFlow = ActivityFlow.of(repository, eventToResume) + * ``` kotlin + * val planPhase = flow.getCurrentPhase().initiatePlan(preparedPlan) + * val medicationRequest = planPhase.getRequestResource() + * // update medicationRequest + * planPhase.update(updated medicationRequest) + * ``` * - * check(resumedPerformFlow.getCurrentPhase() is Phase.EventPhase<*>) { - * "Flow is in ${resumedPerformFlow.getCurrentPhase().getPhaseName()} " - * } + * `PerformPhase` is a type of `Phase.EventPhase` and allows you to update the state of the event. * - * (resumedPerformFlow.getCurrentPhase() as Phase.EventPhase<*>).complete() + * ``` kotlin + * val performPhase = ... + * val medicationDispense = performPhase.getEventResource() + * // update medicationDispense + * performPhase.update(updated medicationDispense) + * performPhase.complete() * ``` */ @Suppress( @@ -210,7 +147,7 @@ private constructor( Intent.ORDER -> OrderPhase(repository, requestResource) else -> throw IllegalArgumentException( - "Couldn't create the flow for ${requestResource.getIntent().code} intent. Supported intents are 'proposal', 'plan' and 'order'.", + "Couldn't create the flow for ${requestResource.getIntent()} intent. Supported intents are 'proposal', 'plan' and 'order'.", ) } } else { @@ -238,57 +175,65 @@ private constructor( } /** - * Creates a draft plan resource based on the state of the [currentPhase]. + * Prepares a plan resource based on the state of the [currentPhase] and returns it to the caller + * without persisting any changes into [repository]. * * @return [R] if the action is successful, error otherwise. */ - fun draftPlan(): Result { - return PlanPhase.draft(currentPhase) + fun preparePlan(): Result { + return PlanPhase.prepare(currentPhase) } /** - * Starts a plan phase based on the state of the [currentPhase] and [draftPlan]. + * Initiates a plan phase based on the state of the [currentPhase] and [preparedPlan]. This api + * will persist the [preparedPlan] into [repository]. * * @return [PlanPhase] if the action is successful, error otherwise. */ - fun startPlan(draftPlan: R) = - PlanPhase.start(repository, currentPhase, draftPlan).also { it.onSuccess { currentPhase = it } } + fun initiatePlan(preparedPlan: R) = + PlanPhase.initiate(repository, currentPhase, preparedPlan).also { + it.onSuccess { currentPhase = it } + } /** - * Creates a draft order resource based on the state of the [currentPhase]. + * Prepares an order resource based on the state of the [currentPhase] and returns it to the + * caller without persisting any changes into [repository]. * * @return [R] if the action is successful, error otherwise. */ - fun draftOrder(): Result { - return OrderPhase.draft(currentPhase) + fun prepareOrder(): Result { + return OrderPhase.prepare(currentPhase) } /** - * Starts an order phase based on the state of the [currentPhase] and [draftPlan]. + * Initiates an order phase based on the state of the [currentPhase] and [preparePlan]. This api + * will persist the [preparedOrder] into [repository]. * * @return [OrderPhase] if the action is successful, error otherwise. */ - fun startOrder(updatedDraftOrder: R) = - OrderPhase.start(repository, currentPhase, updatedDraftOrder).also { + fun initiateOrder(preparedOrder: R) = + OrderPhase.initiate(repository, currentPhase, preparedOrder).also { it.onSuccess { currentPhase = it } } /** - * Creates a draft event resource based on the state of the [currentPhase]. + * Prepares an event resource based on the state of the [currentPhase] and returns it to the + * caller without persisting any changes into [repository]. * * @return [D] if the action is successful, error otherwise. */ - fun draftPerform(klass: Class): Result { - return PerformPhase.draft(klass, currentPhase) + fun preparePerform(klass: Class): Result { + return PerformPhase.prepare(klass, currentPhase) } /** - * Starts a perform phase based on the state of the [currentPhase] and [draftPlan]. + * Initiate a perform phase based on the state of the [currentPhase] and [preparePlan]. This api + * will persist the [preparedEvent] into [repository]. * * @return [PerformPhase] if the action is successful, error otherwise. */ - fun startPerform(updatedDraftPerform: D) = - PerformPhase.start(repository, currentPhase, updatedDraftPerform).also { + fun initiatePerform(preparedEvent: D) = + PerformPhase.initiate(repository, currentPhase, preparedEvent).also { it.onSuccess { currentPhase = it } } @@ -345,22 +290,5 @@ private constructor( resource: CPGOrderMedicationEvent<*>, ): ActivityFlow> = ActivityFlow(repository, null, resource) - - // Collect information - // fun of(repository: Repository, resource: CPGTaskRequest) = - // ActivityFlow(repository, resource, null) - - // // Order a service - // fun of( - // repository: Repository, - // resource: CPGServiceRequest, - // ): ActivityFlow> = - // ActivityFlow(repository, resource) - // - // fun of( - // repository: Repository, - // resource: CPGImmunizationRequest, - // ): ActivityFlow = ActivityFlow(repository, - // resource) } } diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/phase/Phase.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/phase/Phase.kt index 71dd427d47..c4dc11886a 100644 --- a/workflow/src/main/java/com/google/android/fhir/workflow/activity/phase/Phase.kt +++ b/workflow/src/main/java/com/google/android/fhir/workflow/activity/phase/Phase.kt @@ -18,6 +18,7 @@ package com.google.android.fhir.workflow.activity.phase import com.google.android.fhir.workflow.activity.resource.event.CPGEventResource import com.google.android.fhir.workflow.activity.resource.request.CPGRequestResource +import org.hl7.fhir.r4.model.IdType import org.hl7.fhir.r4.model.Reference /** Defines the various phases of a CPG Request. */ @@ -34,7 +35,7 @@ sealed interface Phase { /** Activity Phases for a CPG Request. */ interface RequestPhase> : Phase { - fun getRequest(): R + fun getRequestResource(): R fun update(r: R): Result @@ -49,9 +50,9 @@ sealed interface Phase { /** Activity phases for a CPG Event. */ interface EventPhase> : Phase { - fun getEvent(): E + fun getEventResource(): E - fun update(r: E): Result + fun update(e: E): Result fun suspend(reason: String?): Result @@ -69,4 +70,9 @@ sealed interface Phase { } } -fun checkEquals(a: Reference, b: Reference) = a.reference == b.reference +/** Checks if two references are equal by equating their value. */ +internal fun checkEquals(a: Reference, b: Reference) = a.reference == b.reference + +/** Returns an [IdType] of a [Reference]. This is required for [Repository.read] api. */ +internal val Reference.idType + get() = IdType(reference) diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/phase/event/PerformPhase.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/phase/event/PerformPhase.kt index 0e1a7cb08e..168c95bdca 100644 --- a/workflow/src/main/java/com/google/android/fhir/workflow/activity/phase/event/PerformPhase.kt +++ b/workflow/src/main/java/com/google/android/fhir/workflow/activity/phase/event/PerformPhase.kt @@ -16,16 +16,18 @@ package com.google.android.fhir.workflow.activity.phase.event -import com.google.android.fhir.workflow.activity.`class` -import com.google.android.fhir.workflow.activity.idType +import com.google.android.fhir.getResourceClass import com.google.android.fhir.workflow.activity.phase.Phase import com.google.android.fhir.workflow.activity.phase.checkEquals +import com.google.android.fhir.workflow.activity.phase.idType import com.google.android.fhir.workflow.activity.phase.request.BaseRequestPhase import com.google.android.fhir.workflow.activity.resource.event.CPGEventResource import com.google.android.fhir.workflow.activity.resource.event.EventStatus import com.google.android.fhir.workflow.activity.resource.request.CPGRequestResource import com.google.android.fhir.workflow.activity.resource.request.Intent import com.google.android.fhir.workflow.activity.resource.request.Status +import org.hl7.fhir.r4.model.Reference +import org.hl7.fhir.r4.model.Resource import org.opencds.cqf.fhir.api.Repository /** @@ -37,20 +39,24 @@ import org.opencds.cqf.fhir.api.Repository "UnstableApiUsage", /* Repository is marked @Beta */ "UNCHECKED_CAST", /* Cast type erased CPGRequestResource<*> & CPGEventResource<*> to a concrete type classes */ ) -class PerformPhase>(val repository: Repository, e: E) : - Phase.EventPhase { - private var event: E = e +class PerformPhase>( + /** Implementation of [Repository] to store / retrieve FHIR resources. */ + private val repository: Repository, + /** Concrete implementation of sealed [CPGEventResource] class. e.g. `CPGCommunicationEvent`. */ + e: E, +) : Phase.EventPhase { + private var event: E = e.copy() as E override fun getPhaseName() = Phase.PhaseName.PERFORM - override fun getEvent() = event + override fun getEventResource() = event.copy() as E override fun update(e: E) = runCatching { // TODO Add some basic checks to make sure e is update event and not a completely different // resource. require(e.getStatus() in listOf(EventStatus.PREPARATION, EventStatus.INPROGRESS)) { - "Status is ${e.getStatus()}" + "Status is ${e.getStatusCode()}" } repository.update(e.resource) event = e @@ -59,7 +65,7 @@ class PerformPhase>(val repository: Repository, e: E) : override fun suspend(reason: String?) = runCatching { check(event.getStatus() == EventStatus.INPROGRESS) { - " Can't suspend an event with status ${event.getStatus()} " + " Can't suspend an event with status ${event.getStatusCode()} " } event.setStatus(EventStatus.ONHOLD, reason) @@ -69,7 +75,7 @@ class PerformPhase>(val repository: Repository, e: E) : override fun resume() = runCatching { check(event.getStatus() == EventStatus.ONHOLD) { - " Can't resume an event with status ${event.getStatus()} " + " Can't resume an event with status ${event.getStatusCode()} " } event.setStatus(EventStatus.INPROGRESS) @@ -85,7 +91,7 @@ class PerformPhase>(val repository: Repository, e: E) : override fun start() = runCatching { check(event.getStatus() == EventStatus.PREPARATION) { - " Can't start an event with status ${event.getStatus()} " + " Can't start an event with status ${event.getStatusCode()} " } event.setStatus(EventStatus.INPROGRESS) @@ -95,7 +101,7 @@ class PerformPhase>(val repository: Repository, e: E) : override fun notDone(reason: String?) = runCatching { check(event.getStatus() == EventStatus.PREPARATION) { - " Can't not-done an event with status ${event.getStatus()} " + " Can't not-done an event with status ${event.getStatusCode()} " } event.setStatus(EventStatus.NOTDONE, reason) @@ -105,7 +111,7 @@ class PerformPhase>(val repository: Repository, e: E) : override fun stop(reason: String?) = runCatching { check(event.getStatus() == EventStatus.INPROGRESS) { - " Can't stop an event with status ${event.getStatus()} " + " Can't stop an event with status ${event.getStatusCode()} " } event.setStatus(EventStatus.STOPPED, reason) @@ -115,7 +121,7 @@ class PerformPhase>(val repository: Repository, e: E) : override fun complete() = runCatching { check(event.getStatus() == EventStatus.INPROGRESS) { - " Can't complete an event with status ${event.getStatus()} " + " Can't complete an event with status ${event.getStatusCode()} " } event.setStatus(EventStatus.COMPLETED) @@ -127,14 +133,21 @@ class PerformPhase>(val repository: Repository, e: E) : private val AllowedIntents = listOf(Intent.PROPOSAL, Intent.PLAN, Intent.ORDER) private val AllowedPhases = listOf(Phase.PhaseName.PROPOSAL, Phase.PhaseName.PLAN, Phase.PhaseName.ORDER) - val AllowedStatusForPhaseStart = listOf(EventStatus.INPROGRESS, EventStatus.PREPARATION) + private val AllowedStatusForPhaseStart = listOf(EventStatus.INPROGRESS, EventStatus.PREPARATION) + + /** + * Returns the [Resource] class for the resource. e.g. If the Reference is `Patient/1234`, then + * this would return the `Class` for `org.hl7.fhir.r4.model.Patient`. + */ + private val Reference.`class` + get() = getResourceClass(reference.split("/")[0]) /** * Creates a draft event of type [E] based on the state of the provided [inputPhase]. See * [beginPerform](https://build.fhir.org/ig/HL7/cqf-recommendations/activityflow.html#perform) * for more details. */ - fun , E : CPGEventResource<*>> draft( + fun , E : CPGEventResource<*>> prepare( eventClass: Class<*>, inputPhase: Phase, ): Result = runCatching { @@ -142,14 +155,14 @@ class PerformPhase>(val repository: Repository, e: E) : "Event can't be created for a flow in ${inputPhase.getPhaseName().name} phase. " } - val inputRequest = (inputPhase as BaseRequestPhase<*>).getRequest() + val inputRequest = (inputPhase as BaseRequestPhase<*>).request check(inputRequest.getIntent() in AllowedIntents) { - "Event can't be created for a request with ${inputRequest.getIntent().code} intent." + "Event can't be created for a request with ${inputRequest.getIntent()} intent." } check(inputRequest.getStatus() == Status.ACTIVE) { - "${inputPhase.getPhaseName().name} request is still in ${inputRequest.getStatus()} status." + "${inputPhase.getPhaseName().name} request is still in ${inputRequest.getStatusCode()} status." } val eventRequest = CPGEventResource.of(inputRequest, eventClass) @@ -158,7 +171,7 @@ class PerformPhase>(val repository: Repository, e: E) : eventRequest as E } - fun , E : CPGEventResource<*>> start( + fun , E : CPGEventResource<*>> initiate( repository: Repository, inputPhase: Phase, inputEvent: E, @@ -167,12 +180,12 @@ class PerformPhase>(val repository: Repository, e: E) : "A Perform can't be started for a flow in ${inputPhase.getPhaseName().name} phase." } - val currentPhase = inputPhase as Phase.RequestPhase<*> + val currentPhase = inputPhase as BaseRequestPhase<*> val basedOn = inputEvent.getBasedOn() require(basedOn != null) { "${inputEvent.resource.resourceType}.basedOn can't be null." } - require(checkEquals(basedOn, currentPhase.getRequest().asReference())) { + require(checkEquals(basedOn, currentPhase.request.asReference())) { "Provided draft is not based on the request in current phase." } @@ -186,11 +199,11 @@ class PerformPhase>(val repository: Repository, e: E) : } require(basedOnRequest.getStatus() == Status.ACTIVE) { - "Plan can't be based on a request with ${basedOnRequest.getStatus()} status." + "Plan can't be based on a request with ${basedOnRequest.getStatusCode()} status." } require(inputEvent.getStatus() in AllowedStatusForPhaseStart) { - "Input event is in ${inputEvent.getStatus().name} status." + "Input event is in ${inputEvent.getStatusCode()} status." } basedOnRequest.setStatus(Status.COMPLETED) diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/phase/request/BaseRequestPhase.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/phase/request/BaseRequestPhase.kt index 09d75cd9d4..b253df6ead 100644 --- a/workflow/src/main/java/com/google/android/fhir/workflow/activity/phase/request/BaseRequestPhase.kt +++ b/workflow/src/main/java/com/google/android/fhir/workflow/activity/phase/request/BaseRequestPhase.kt @@ -25,18 +25,28 @@ import org.opencds.cqf.fhir.api.Repository @Suppress( "UnstableApiUsage", /* Repository is marked @Beta */ ) -abstract class BaseRequestPhase>(val repository: Repository, r: R) : - Phase.RequestPhase { - private var request: R = r +abstract class BaseRequestPhase>( + /** Implementation of [Repository] to store / retrieve FHIR resources. */ + private val repository: Repository, + /** + * Concrete implementation of sealed [CPGRequestResource] class. e.g. `CPGCommunicationRequest`. + */ + r: R, + /** PhaseName of the concrete implementation. */ + private val phaseName: Phase.PhaseName, +) : Phase.RequestPhase { + internal var request: R = r.copy() as R // TODO : Maybe this should return a copy of the resource so that if the user does any changes to // this resource, it doesn't affect the state of the flow. - override fun getRequest() = request + override fun getRequestResource() = request.copy() as R + + override fun getPhaseName() = phaseName override fun suspend(reason: String?) = runCatching { check(request.getStatus() == Status.ACTIVE) { - " Can't suspend an event with status ${request.getStatus()} " + " Can't suspend an event with status ${request.getStatusCode()} " } request.setStatus(Status.ONHOLD, reason) @@ -46,7 +56,7 @@ abstract class BaseRequestPhase>(val repository: Repos override fun resume() = runCatching { check(request.getStatus() == Status.ONHOLD) { - " Can't resume an event with status ${request.getStatus()} " + " Can't resume an event with status ${request.getStatusCode()} " } request.setStatus(Status.ACTIVE) @@ -57,7 +67,9 @@ abstract class BaseRequestPhase>(val repository: Repos runCatching { // TODO Add some basic checks to make sure e is update event and not a completely different // resource. - require(r.getStatus() in listOf(Status.DRAFT, Status.ACTIVE)) { "Status is ${r.getStatus()}" } + require(r.getStatus() in listOf(Status.DRAFT, Status.ACTIVE)) { + "Status is ${r.getStatusCode()}" + } repository.update(r.resource) request = r } @@ -71,7 +83,7 @@ abstract class BaseRequestPhase>(val repository: Repos override fun reject(reason: String?) = runCatching { check(request.getStatus() == Status.ACTIVE) { - " Can't reject an event with status ${request.getStatus()} " + " Can't reject an event with status ${request.getStatusCode()} " } request.setStatus(Status.REVOKED, reason) diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/phase/request/OrderPhase.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/phase/request/OrderPhase.kt index dcb65d8fd1..c9e21b1598 100644 --- a/workflow/src/main/java/com/google/android/fhir/workflow/activity/phase/request/OrderPhase.kt +++ b/workflow/src/main/java/com/google/android/fhir/workflow/activity/phase/request/OrderPhase.kt @@ -16,9 +16,9 @@ package com.google.android.fhir.workflow.activity.phase.request -import com.google.android.fhir.workflow.activity.idType import com.google.android.fhir.workflow.activity.phase.Phase import com.google.android.fhir.workflow.activity.phase.checkEquals +import com.google.android.fhir.workflow.activity.phase.idType import com.google.android.fhir.workflow.activity.resource.request.CPGRequestResource import com.google.android.fhir.workflow.activity.resource.request.Intent import com.google.android.fhir.workflow.activity.resource.request.Status @@ -34,10 +34,14 @@ import org.opencds.cqf.fhir.api.Repository "UnstableApiUsage", /* Repository is marked @Beta */ "UNCHECKED_CAST", /* Cast type erased CPGRequestResource<*> & CPGEventResource<*> to a concrete type classes */ ) -class OrderPhase>(repository: Repository, r: R) : - BaseRequestPhase(repository, r) { - - override fun getPhaseName() = Phase.PhaseName.ORDER +class OrderPhase>( + /** Implementation of [Repository] to store / retrieve FHIR resources. */ + repository: Repository, + /** + * Concrete implementation of sealed [CPGRequestResource] class. e.g. `CPGCommunicationRequest`. + */ + r: R, +) : BaseRequestPhase(repository, r, Phase.PhaseName.ORDER) { companion object { @@ -49,19 +53,19 @@ class OrderPhase>(repository: Repository, r: R) : * [beginOrder](https://build.fhir.org/ig/HL7/cqf-recommendations/activityflow.html#order) for * more details. */ - internal fun > draft(inputPhase: Phase): Result = runCatching { + internal fun > prepare(inputPhase: Phase): Result = runCatching { check(inputPhase.getPhaseName() in AllowedPhases) { "An Order can't be created for a flow in ${inputPhase.getPhaseName().name} phase. " } - val inputRequest = (inputPhase as Phase.RequestPhase<*>).getRequest() + val inputRequest = (inputPhase as BaseRequestPhase<*>).request check(inputRequest.getIntent() in AllowedIntents) { - "Order can't be created for a request with ${inputRequest.getIntent().code} intent." + "Order can't be created for a request with ${inputRequest.getIntent()} intent." } check(inputRequest.getStatus() == Status.ACTIVE) { - "${inputPhase.getPhaseName().name} request is still in ${inputRequest.getStatus()} status." + "${inputPhase.getPhaseName().name} request is still in ${inputRequest.getStatusCode()} status." } inputRequest.copy( @@ -76,7 +80,7 @@ class OrderPhase>(repository: Repository, r: R) : * [endPlan](https://build.fhir.org/ig/HL7/cqf-recommendations/activityflow.html#plan) for more * details. */ - fun > start( + fun > initiate( repository: Repository, inputPhase: Phase, inputOrder: R, @@ -85,12 +89,12 @@ class OrderPhase>(repository: Repository, r: R) : "An Order can't be started for a flow in ${inputPhase.getPhaseName().name} phase." } - val currentPhase = inputPhase as Phase.RequestPhase<*> + val currentPhase = inputPhase as BaseRequestPhase<*> val basedOn = inputOrder.getBasedOn() require(basedOn != null) { "${inputOrder.resource.resourceType}.basedOn can't be null." } - require(checkEquals(basedOn, currentPhase.getRequest().asReference())) { + require(checkEquals(basedOn, currentPhase.request.asReference())) { "Provided draft is not based on the request in current phase." } @@ -106,15 +110,15 @@ class OrderPhase>(repository: Repository, r: R) : } require(basedOnRequest.getStatus() == Status.ACTIVE) { - "Plan can't be based on a request with ${basedOnRequest.getStatus()} status." + "Plan can't be based on a request with ${basedOnRequest.getStatusCode()} status." } require(inputOrder.getIntent() == Intent.ORDER) { - "Input request has '${inputOrder.getIntent().code}' intent." + "Input request has '${inputOrder.getIntent()}' intent." } require(inputOrder.getStatus() in AllowedStatusForPhaseStart) { - "Input request is in ${inputOrder.getStatus().name} status." + "Input request is in ${inputOrder.getStatusCode()} status." } basedOnRequest.setStatus(Status.COMPLETED) diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/phase/request/PlanPhase.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/phase/request/PlanPhase.kt index 99079a8665..bb02f0e79c 100644 --- a/workflow/src/main/java/com/google/android/fhir/workflow/activity/phase/request/PlanPhase.kt +++ b/workflow/src/main/java/com/google/android/fhir/workflow/activity/phase/request/PlanPhase.kt @@ -16,9 +16,9 @@ package com.google.android.fhir.workflow.activity.phase.request -import com.google.android.fhir.workflow.activity.idType import com.google.android.fhir.workflow.activity.phase.Phase import com.google.android.fhir.workflow.activity.phase.checkEquals +import com.google.android.fhir.workflow.activity.phase.idType import com.google.android.fhir.workflow.activity.resource.request.CPGRequestResource import com.google.android.fhir.workflow.activity.resource.request.Intent import com.google.android.fhir.workflow.activity.resource.request.Status @@ -34,9 +34,14 @@ import org.opencds.cqf.fhir.api.Repository "UnstableApiUsage", /* Repository is marked @Beta */ "UNCHECKED_CAST", /* Cast type erased CPGRequestResource<*> & CPGEventResource<*> to a concrete type classes */ ) -class PlanPhase>(repository: Repository, r: R) : - BaseRequestPhase(repository, r) { - override fun getPhaseName() = Phase.PhaseName.PLAN +class PlanPhase>( + /** Implementation of [Repository] to store / retrieve FHIR resources. */ + repository: Repository, + /** + * Concrete implementation of sealed [CPGRequestResource] class. e.g. `CPGCommunicationRequest`. + */ + r: R, +) : BaseRequestPhase(repository, r, Phase.PhaseName.PLAN) { companion object { @@ -45,18 +50,18 @@ class PlanPhase>(repository: Repository, r: R) : * [beginPlan](https://build.fhir.org/ig/HL7/cqf-recommendations/activityflow.html#plan) for * more details. */ - internal fun > draft(inputPhase: Phase): Result = runCatching { + internal fun > prepare(inputPhase: Phase): Result = runCatching { check(inputPhase.getPhaseName() == Phase.PhaseName.PROPOSAL) { "A Plan can't be created for a flow in ${inputPhase.getPhaseName().name} phase." } - val inputRequest = (inputPhase as Phase.RequestPhase<*>).getRequest() + val inputRequest = (inputPhase as BaseRequestPhase<*>).request check(inputRequest.getIntent() == Intent.PROPOSAL) { - "Plan can't be created for a request with ${inputRequest.getIntent().code} intent." + "Plan can't be created for a request with ${inputRequest.getIntent()} intent." } check(inputRequest.getStatus() == Status.ACTIVE) { - "${inputPhase.getPhaseName().name} request is still in ${inputRequest.getStatus()} status." + "${inputPhase.getPhaseName().name} request is still in ${inputRequest.getStatusCode()} status." } val planRequest: CPGRequestResource<*> = @@ -74,7 +79,7 @@ class PlanPhase>(repository: Repository, r: R) : * [endPlan](https://build.fhir.org/ig/HL7/cqf-recommendations/activityflow.html#plan) for more * details. */ - internal fun > start( + internal fun > initiate( repository: Repository, inputPhase: Phase, draftPlan: R, @@ -83,12 +88,12 @@ class PlanPhase>(repository: Repository, r: R) : "A Plan can't be started for a flow in ${inputPhase.getPhaseName().name} phase." } - val currentPhase = inputPhase as Phase.RequestPhase<*> + val currentPhase = inputPhase as BaseRequestPhase<*> val basedOn = draftPlan.getBasedOn() require(basedOn != null) { "${draftPlan.resource.resourceType}.basedOn can't be null." } - require(checkEquals(basedOn, currentPhase.getRequest().asReference())) { + require(checkEquals(basedOn, currentPhase.request.asReference())) { "Provided draft is not based on the request in current phase." } @@ -103,15 +108,15 @@ class PlanPhase>(repository: Repository, r: R) : } require(basedOnRequest.getStatus() == Status.ACTIVE) { - "Plan can't be based on a request with ${basedOnRequest.getStatus()} status." + "Plan can't be based on a request with ${basedOnRequest.getStatusCode()} status." } require(draftPlan.getIntent() == Intent.PLAN) { - "Input request has '${draftPlan.getIntent().code}' intent." + "Input request has '${draftPlan.getIntent()}' intent." } require(draftPlan.getStatus() in AllowedStatusForPhaseStart) { - "Input request is in ${draftPlan.getStatus().name} status." + "Input request is in ${draftPlan.getStatusCode()} status." } basedOnRequest.setStatus(Status.COMPLETED) diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/phase/request/ProposalPhase.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/phase/request/ProposalPhase.kt index b54c0a997e..59bb12e9e6 100644 --- a/workflow/src/main/java/com/google/android/fhir/workflow/activity/phase/request/ProposalPhase.kt +++ b/workflow/src/main/java/com/google/android/fhir/workflow/activity/phase/request/ProposalPhase.kt @@ -28,7 +28,11 @@ import org.opencds.cqf.fhir.api.Repository @Suppress( "UnstableApiUsage", /* Repository is marked @Beta */ ) -class ProposalPhase>(repository: Repository, r: R) : - BaseRequestPhase(repository, r) { - override fun getPhaseName() = Phase.PhaseName.PROPOSAL -} +class ProposalPhase>( + /** Implementation of [Repository] to store / retrieve FHIR resources. */ + repository: Repository, + /** + * Concrete implementation of sealed [CPGRequestResource] class. e.g. `CPGCommunicationRequest`. + */ + r: R, +) : BaseRequestPhase(repository, r, Phase.PhaseName.PROPOSAL) diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGCaseEvent.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGCaseEvent.kt deleted file mode 100644 index 4e74fe59a4..0000000000 --- a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGCaseEvent.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.fhir.workflow.activity.resource.event - -import org.hl7.fhir.r4.model.EpisodeOfCare -import org.hl7.fhir.r4.model.Reference - -class CPGCaseEvent(override val resource: EpisodeOfCare) : - CPGEventResource(resource) { - override fun setStatus(status: EventStatus, reason: String?) { - resource.status = EpisodeOfCare.EpisodeOfCareStatus.fromCode(status.code) - } - - override fun getStatus() = EventStatus.of(resource.status.toCode()) - - override fun setBasedOn(reference: Reference) { - resource.addReferralRequest(reference) - } - - override fun getBasedOn() = resource.referralRequest.lastOrNull() - - override fun copy() = CPGCaseEvent(resource.copy()) -} diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGCommunicationEvent.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGCommunicationEvent.kt index 0edc7ad511..b5d2d4f90c 100644 --- a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGCommunicationEvent.kt +++ b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGCommunicationEvent.kt @@ -16,6 +16,7 @@ package com.google.android.fhir.workflow.activity.resource.event +import com.google.android.fhir.workflow.activity.resource.event.EventStatus.PREPARATION import com.google.android.fhir.workflow.activity.resource.request.CPGCommunicationRequest import java.util.UUID import org.hl7.fhir.r4.model.CodeableConcept @@ -24,14 +25,14 @@ import org.hl7.fhir.r4.model.Communication import org.hl7.fhir.r4.model.Reference class CPGCommunicationEvent(override val resource: Communication) : - CPGEventResource(resource) { + CPGEventResource(resource, CommunicationEventMapper) { override fun setStatus(status: EventStatus, reason: String?) { - resource.status = Communication.CommunicationStatus.fromCode(status.code) + resource.status = Communication.CommunicationStatus.fromCode(mapper.mapStatusToCode(status)) resource.statusReason = reason?.let { CodeableConcept(Coding().setCode(it)) } } - override fun getStatus() = EventStatus.of(resource.status.toCode()) + override fun getStatusCode() = resource.status?.toCode() override fun setBasedOn(reference: Reference) { resource.addBasedOn(reference) @@ -65,4 +66,6 @@ class CPGCommunicationEvent(override val resource: Communication) : ) } } + + private object CommunicationEventMapper : EventStatusCodeMapperImpl() } diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGCondition.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGCondition.kt deleted file mode 100644 index 0be58d0f03..0000000000 --- a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGCondition.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.fhir.workflow.activity.resource.event - -import org.hl7.fhir.r4.model.CodeableConcept -import org.hl7.fhir.r4.model.Condition -import org.hl7.fhir.r4.model.Reference - -class CPGCondition(override val resource: Condition) : CPGEventResource(resource) { - // clinical status http://hl7.org/fhir/ValueSet/condition-clinical - override fun setStatus(status: EventStatus, reason: String?) { - resource.clinicalStatus = CodeableConcept() - // resource.ras = reason?.let { CodeableConcept(Coding().setCode(it)) } - } - - override fun getStatus(): EventStatus { - TODO("Not yet implemented") - } - - override fun setBasedOn(reference: Reference) { - TODO("Not yet implemented") - } - - override fun getBasedOn(): Reference? { - TODO("Not yet implemented") - } - - override fun copy() = CPGCondition(resource.copy()) - // -} diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGEventForOrderService.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGEventForOrderService.kt deleted file mode 100644 index 600f768523..0000000000 --- a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGEventForOrderService.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.fhir.workflow.activity.resource.event - -import org.hl7.fhir.r4.model.Observation -import org.hl7.fhir.r4.model.Procedure -import org.hl7.fhir.r4.model.Reference -import org.hl7.fhir.r4.model.Resource - -abstract class CPGEventForOrderService(override val resource: R) : - CPGEventResource(resource) - -class CPGProcedureEvent(override val resource: Procedure) : - CPGEventForOrderService(resource) { - override fun setStatus(status: EventStatus, reason: String?) { - TODO("Not yet implemented") - } - - override fun getStatus(): EventStatus { - TODO("Not yet implemented") - } - - override fun setBasedOn(reference: Reference) { - TODO("Not yet implemented") - } - - override fun getBasedOn(): Reference? { - TODO("Not yet implemented") - } - - override fun copy() = CPGProcedureEvent(resource.copy()) -} - -class CPGObservationEvent(override val resource: Observation) : - CPGEventForOrderService(resource) { - - override fun setStatus(status: EventStatus, reason: String?) { - TODO("Not yet implemented") - } - - override fun getStatus(): EventStatus { - TODO("Not yet implemented") - } - - override fun setBasedOn(reference: Reference) { - resource.addExtension("http://hl7.org/fhir/StructureDefinition/event-basedOn", reference) - } - - override fun getBasedOn() = - resource.getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/event-basedOn")?.value - as Reference? - - override fun copy() = CPGObservationEvent(resource.copy()) -} diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGEventResource.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGEventResource.kt index 296b8d9b03..699545c416 100644 --- a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGEventResource.kt +++ b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGEventResource.kt @@ -40,7 +40,10 @@ import org.hl7.fhir.r4.model.ResourceType * The application users may use appropriate [of] static factories to create the required * [CPGEventResource]s. */ -sealed class CPGEventResource(internal open val resource: R) where R : Resource { +sealed class CPGEventResource( + internal open val resource: R, + internal val mapper: EventStatusCodeMapper, +) where R : Resource { val resourceType: ResourceType get() = resource.resourceType @@ -50,16 +53,14 @@ sealed class CPGEventResource(internal open val resource: R) where R : Re abstract fun setStatus(status: EventStatus, reason: String? = null) - abstract fun getStatus(): EventStatus + fun getStatus(): EventStatus = mapper.mapCodeToStatus(getStatusCode()) + + abstract fun getStatusCode(): String? abstract fun setBasedOn(reference: Reference) abstract fun getBasedOn(): Reference? - fun update(update: R.() -> Unit) { - resource.update() - } - abstract fun copy(): CPGEventResource companion object { @@ -76,19 +77,24 @@ sealed class CPGEventResource(internal open val resource: R) where R : Re } } -enum class EventStatus(val code: String) { - PREPARATION("preparation"), - INPROGRESS("in-progress"), - CANCELLED("not-done"), - ONHOLD("on-hold"), - COMPLETED("completed"), - ENTEREDINERROR("entered-in-error"), - STOPPED("stopped"), - DECLINED("decline"), - UNKNOWN("unknown"), - NOTDONE("not-done"), - NULL("null"), - ; +sealed interface EventStatus { + data object PREPARATION : EventStatus + + data object INPROGRESS : EventStatus + + data object NOTDONE : EventStatus + + data object ONHOLD : EventStatus + + data object COMPLETED : EventStatus + + data object ENTEREDINERROR : EventStatus + + data object STOPPED : EventStatus + + data object UNKNOWN : EventStatus + + class OTHER(val code: String?) : EventStatus companion object { @@ -96,15 +102,12 @@ enum class EventStatus(val code: String) { when (code) { "preparation" -> PREPARATION "in-progress" -> INPROGRESS - "not-done" -> CANCELLED + "not-done" -> NOTDONE "on-hold" -> ONHOLD "completed" -> COMPLETED "entered-in-error" -> ENTEREDINERROR "stopped" -> STOPPED - "decline" -> DECLINED - "unknown" -> UNKNOWN - "null" -> NULL - else -> UNKNOWN + else -> OTHER(code) } } } diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGImmunizationEvent.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGImmunizationEvent.kt deleted file mode 100644 index fa29271e31..0000000000 --- a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGImmunizationEvent.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.fhir.workflow.activity.resource.event - -import com.google.android.fhir.workflow.activity.resource.request.CPGCommunicationRequest -import java.util.UUID -import org.hl7.fhir.r4.model.CodeableConcept -import org.hl7.fhir.r4.model.Coding -import org.hl7.fhir.r4.model.Immunization -import org.hl7.fhir.r4.model.Reference - -class CPGImmunizationEvent(override val resource: Immunization) : - CPGEventResource(resource) { - override fun setStatus(status: EventStatus, reason: String?) { - resource.status = Immunization.ImmunizationStatus.fromCode(status.code) - resource.statusReason = reason?.let { CodeableConcept(Coding().setCode(it)) } - } - - override fun getStatus() = EventStatus.of(resource.status.toCode()) - - override fun setBasedOn(reference: Reference) { - TODO(" Based on not present") - } - - override fun getBasedOn(): Reference = TODO(" Based on not present") - - override fun copy() = CPGImmunizationEvent(resource.copy()) - - companion object { - fun from(request: CPGCommunicationRequest): CPGImmunizationEvent { - return CPGImmunizationEvent( - Immunization().apply { - id = UUID.randomUUID().toString() - status = Immunization.ImmunizationStatus.NOTDONE - patient = request.resource.subject - }, - ) - } - } -} diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGMedicationDispenseEvent.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGMedicationDispenseEvent.kt new file mode 100644 index 0000000000..0d04f3d2c0 --- /dev/null +++ b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGMedicationDispenseEvent.kt @@ -0,0 +1,102 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.fhir.workflow.activity.resource.event + +import com.google.android.fhir.workflow.activity.resource.event.EventStatus.NOTDONE +import com.google.android.fhir.workflow.activity.resource.request.CPGMedicationRequest +import java.util.UUID +import org.hl7.fhir.r4.model.CodeableConcept +import org.hl7.fhir.r4.model.Coding +import org.hl7.fhir.r4.model.MedicationDispense +import org.hl7.fhir.r4.model.MedicationRequest +import org.hl7.fhir.r4.model.Reference + +class CPGMedicationDispenseEvent(override val resource: MedicationDispense) : + CPGOrderMedicationEvent(resource, MedicationDispenseEventEventMapper) { + + override fun setStatus(status: EventStatus, reason: String?) { + resource.status = + MedicationDispense.MedicationDispenseStatus.fromCode(mapper.mapStatusToCode(status)) + resource.statusReason = reason?.let { CodeableConcept(Coding().setCode(it)) } + } + + override fun getStatusCode() = resource.status?.toCode() + + override fun setBasedOn(reference: Reference) { + resource.addAuthorizingPrescription(reference) + } + + override fun getBasedOn(): Reference? = resource.authorizingPrescription.lastOrNull() + + override fun copy() = CPGMedicationDispenseEvent(resource.copy()) + + companion object { + + fun from(request: CPGMedicationRequest): CPGMedicationDispenseEvent { + return CPGMedicationDispenseEvent( + MedicationDispense().apply { + id = UUID.randomUUID().toString() + status = MedicationDispense.MedicationDispenseStatus.PREPARATION + + if (request.resource.hasCategory() && request.resource.category.size == 1) { + // Only set category if single, otherwise let application fill it in. + category = request.resource.category.first() + } + + medication = request.resource.medication + subject = request.resource.subject + context = request.resource.encounter + if (request.resource.hasSubstitution()) { + substitution = request.resource.substitution.toMedicationDispenseSubstitutionComponent() + } + note = request.resource.note + dosageInstruction = request.resource.dosageInstruction + detectedIssue = request.resource.detectedIssue + eventHistory = request.resource.eventHistory + }, + ) + } + + private fun MedicationRequest.MedicationRequestSubstitutionComponent + .toMedicationDispenseSubstitutionComponent() = + MedicationDispense.MedicationDispenseSubstitutionComponent().apply { + id = this@toMedicationDispenseSubstitutionComponent.id + extension = this@toMedicationDispenseSubstitutionComponent.extension + modifierExtension = this@toMedicationDispenseSubstitutionComponent.modifierExtension + allowed = this@toMedicationDispenseSubstitutionComponent.allowed + if (this@toMedicationDispenseSubstitutionComponent.hasReason()) { + addReason(this@toMedicationDispenseSubstitutionComponent.reason) + } + } + } +} + +private object MedicationDispenseEventEventMapper : EventStatusCodeMapperImpl() { + override fun mapCodeToStatus(code: String?): EventStatus { + return when (code) { + "cancelled" -> NOTDONE + else -> super.mapCodeToStatus(code) + } + } + + override fun mapStatusToCode(status: EventStatus): String? { + return when (status) { + NOTDONE -> "cancelled" + else -> super.mapStatusToCode(status) + } + } +} diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGOrderMedicationEvent.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGOrderMedicationEvent.kt index 7e5a7e5fae..a4dceb9d6e 100644 --- a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGOrderMedicationEvent.kt +++ b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/CPGOrderMedicationEvent.kt @@ -17,154 +17,18 @@ package com.google.android.fhir.workflow.activity.resource.event import com.google.android.fhir.workflow.activity.resource.request.CPGMedicationRequest -import java.util.UUID -import org.hl7.fhir.r4.model.CodeableConcept -import org.hl7.fhir.r4.model.Coding -import org.hl7.fhir.r4.model.MedicationAdministration -import org.hl7.fhir.r4.model.MedicationDispense -import org.hl7.fhir.r4.model.MedicationRequest -import org.hl7.fhir.r4.model.MedicationStatement -import org.hl7.fhir.r4.model.Reference import org.hl7.fhir.r4.model.Resource -abstract class CPGOrderMedicationEvent(override val resource: R) : - CPGEventResource(resource) { +abstract class CPGOrderMedicationEvent +internal constructor(override val resource: R, mapper: EventStatusCodeMapper) : + CPGEventResource(resource, mapper) { companion object { fun from(request: CPGMedicationRequest, eventClass: Class<*>) = when (eventClass) { CPGMedicationDispenseEvent::class.java -> CPGMedicationDispenseEvent.from(request) - CPGMedicationAdministrationEvent::class.java -> - CPGMedicationAdministrationEvent.from(request) - CPGMedicationStatementEvent::class.java -> CPGMedicationStatementEvent.from(request) else -> throw IllegalArgumentException(" Unknown Event type $eventClass") } } } - -class CPGMedicationDispenseEvent(override val resource: MedicationDispense) : - CPGOrderMedicationEvent(resource) { - - override fun setStatus(status: EventStatus, reason: String?) { - resource.status = MedicationDispense.MedicationDispenseStatus.fromCode(status.code) - resource.statusReason = reason?.let { CodeableConcept(Coding().setCode(it)) } - } - - override fun getStatus() = EventStatus.of(resource.status.toCode()) - - override fun setBasedOn(reference: Reference) { - resource.addAuthorizingPrescription(reference) - } - - override fun getBasedOn(): Reference? = resource.authorizingPrescription.lastOrNull() - - override fun copy() = CPGMedicationDispenseEvent(resource.copy()) - - companion object { - - fun from(request: CPGMedicationRequest): CPGMedicationDispenseEvent { - return CPGMedicationDispenseEvent( - MedicationDispense().apply { - id = UUID.randomUUID().toString() - status = MedicationDispense.MedicationDispenseStatus.PREPARATION - - if (request.resource.hasCategory() && request.resource.category.size == 1) { - // Only set category if single, otherwise let application fill it in. - category = request.resource.category.first() - } - - medication = request.resource.medication - subject = request.resource.subject - context = request.resource.encounter - if (request.resource.hasSubstitution()) { - substitution = request.resource.substitution.toMedicationDispenseSubstitutionComponent() - } - note = request.resource.note - dosageInstruction = request.resource.dosageInstruction - detectedIssue = request.resource.detectedIssue - eventHistory = request.resource.eventHistory - }, - ) - } - - private fun MedicationRequest.MedicationRequestSubstitutionComponent - .toMedicationDispenseSubstitutionComponent() = - MedicationDispense.MedicationDispenseSubstitutionComponent().apply { - id = this@toMedicationDispenseSubstitutionComponent.id - extension = this@toMedicationDispenseSubstitutionComponent.extension - modifierExtension = this@toMedicationDispenseSubstitutionComponent.modifierExtension - allowed = this@toMedicationDispenseSubstitutionComponent.allowed - if (this@toMedicationDispenseSubstitutionComponent.hasReason()) { - addReason(this@toMedicationDispenseSubstitutionComponent.reason) - } - } - } -} - -class CPGMedicationAdministrationEvent(override val resource: MedicationAdministration) : - CPGOrderMedicationEvent(resource) { - override fun setStatus(status: EventStatus, reason: String?) { - resource.status = MedicationAdministration.MedicationAdministrationStatus.fromCode(status.code) - resource.statusReason = reason?.let { listOf(CodeableConcept(Coding().setCode(it))) } - } - - override fun getStatus() = EventStatus.of(resource.status.toCode()) - - override fun setBasedOn(reference: Reference) { - resource.request = reference - } - - override fun getBasedOn(): Reference = resource.request - - override fun copy() = CPGMedicationAdministrationEvent(resource.copy()) - - companion object { - fun from(request: CPGMedicationRequest): CPGMedicationAdministrationEvent { - return CPGMedicationAdministrationEvent( - MedicationAdministration().apply { - id = UUID.randomUUID().toString() - status = MedicationAdministration.MedicationAdministrationStatus.UNKNOWN - subject = request.resource.subject - medication = request.resource.medication - this.request = request.asReference() - medication = request.resource.medication - }, - ) - } - } -} - -class CPGMedicationStatementEvent(override val resource: MedicationStatement) : - CPGOrderMedicationEvent(resource) { - override fun setStatus(status: EventStatus, reason: String?) { - resource.status = MedicationStatement.MedicationStatementStatus.fromCode(status.code) - } - - override fun getStatus() = EventStatus.of(resource.status.toCode()) - - override fun setBasedOn(reference: Reference) { - resource.addBasedOn(reference) - } - - override fun getBasedOn(): Reference? = resource.basedOn.firstOrNull() - - override fun copy() = CPGMedicationStatementEvent(resource.copy()) - - companion object { - fun from(request: CPGMedicationRequest): CPGMedicationStatementEvent { - return CPGMedicationStatementEvent( - MedicationStatement().apply { - id = UUID.randomUUID().toString() - status = MedicationStatement.MedicationStatementStatus.UNKNOWN - subject = request.resource.subject - medication = request.resource.medication - addBasedOn(request.asReference()) - addDerivedFrom(request.asReference()) - informationSource = request.asReference() - medication = request.resource.medication - }, - ) - } - } -} diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/EventStatusCodeMapper.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/EventStatusCodeMapper.kt new file mode 100644 index 0000000000..a5be87f6bc --- /dev/null +++ b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/event/EventStatusCodeMapper.kt @@ -0,0 +1,64 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.fhir.workflow.activity.resource.event + +import com.google.android.fhir.workflow.activity.resource.event.EventStatus.INPROGRESS +import com.google.android.fhir.workflow.activity.resource.event.EventStatus.NOTDONE +import com.google.android.fhir.workflow.activity.resource.event.EventStatus.PREPARATION +import com.google.android.fhir.workflow.activity.resource.event.EventStatus.STOPPED + +/** + * Since event resources may have different code for same status, each [CPGEventResource] should + * provide its own mapper. See + * [columns next to status](https://build.fhir.org/ig/HL7/cqf-recommendations/activityflow.html#activity-lifecycle---event-phase) + */ +internal interface EventStatusCodeMapper { + fun mapCodeToStatus(code: String?): EventStatus + + fun mapStatusToCode(status: EventStatus): String? +} + +/** A base implementation where the status and code map each other. */ +internal open class EventStatusCodeMapperImpl : EventStatusCodeMapper { + override fun mapCodeToStatus(code: String?): EventStatus { + return when (code) { + "preparation" -> PREPARATION + "in-progress" -> INPROGRESS + "not-done" -> NOTDONE + "on-hold" -> EventStatus.ONHOLD + "completed" -> EventStatus.COMPLETED + "entered-in-error" -> EventStatus.ENTEREDINERROR + "stopped" -> STOPPED + "unknown" -> EventStatus.UNKNOWN + else -> EventStatus.OTHER(code) + } + } + + override fun mapStatusToCode(status: EventStatus): String? { + return when (status) { + PREPARATION -> "preparation" + INPROGRESS -> "in-progress" + NOTDONE -> "not-done" + EventStatus.ONHOLD -> "on-hold" + EventStatus.COMPLETED -> "completed" + EventStatus.ENTEREDINERROR -> "entered-in-error" + STOPPED -> "stopped" + EventStatus.UNKNOWN -> "unknown" + is EventStatus.OTHER -> status.code + } + } +} diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGCommunicationRequest.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGCommunicationRequest.kt index c5d359fa64..e626b6854f 100644 --- a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGCommunicationRequest.kt +++ b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGCommunicationRequest.kt @@ -23,7 +23,7 @@ import org.hl7.fhir.r4.model.Reference import org.hl7.fhir.r4.model.StringType class CPGCommunicationRequest(override val resource: CommunicationRequest) : - CPGRequestResource(resource) { + CPGRequestResource(resource, StatusCodeMapperImpl()) { override fun setIntent(intent: Intent) { if (resource.hasExtension("http://hl7.org/fhir/StructureDefinition/request-intent")) { resource @@ -44,11 +44,12 @@ class CPGCommunicationRequest(override val resource: CommunicationRequest) : ?: Intent.of(null) override fun setStatus(status: Status, reason: String?) { - resource.status = CommunicationRequest.CommunicationRequestStatus.fromCode(status.string) + resource.status = + CommunicationRequest.CommunicationRequestStatus.fromCode(mapper.mapStatusToCode(status)) resource.statusReason = reason?.let { CodeableConcept(Coding().setCode(it)) } } - override fun getStatus() = Status.of(resource.status.toCode()) + override fun getStatusCode() = resource.status?.toCode() override fun setBasedOn(reference: Reference) { resource.addBasedOn(reference) diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGEnrollmentTaskRequest.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGEnrollmentTaskRequest.kt deleted file mode 100644 index 279da63871..0000000000 --- a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGEnrollmentTaskRequest.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.fhir.workflow.activity.resource.request - -import org.hl7.fhir.r4.model.Task - -class CPGEnrollmentTaskRequest(override val resource: Task) : CPGTaskRequest(resource) { - override fun copy() = CPGEnrollmentTaskRequest(resource.copy()) -} diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGGenerateReportTask.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGGenerateReportTask.kt deleted file mode 100644 index 2f3d435672..0000000000 --- a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGGenerateReportTask.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.fhir.workflow.activity.resource.request - -import org.hl7.fhir.r4.model.Task - -class CPGGenerateReportTask(override val resource: Task) : CPGTaskRequest(resource) { - override fun copy() = CPGGenerateReportTask(resource.copy()) -} diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGImmunizationRequest.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGImmunizationRequest.kt deleted file mode 100644 index 729dfed9e0..0000000000 --- a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGImmunizationRequest.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.fhir.workflow.activity.resource.request - -import org.hl7.fhir.r4.model.CodeableConcept -import org.hl7.fhir.r4.model.Coding -import org.hl7.fhir.r4.model.MedicationRequest -import org.hl7.fhir.r4.model.Reference - -class CPGImmunizationRequest(override val resource: MedicationRequest) : - CPGRequestResource(resource) { - override fun setIntent(intent: Intent) { - resource.intent = MedicationRequest.MedicationRequestIntent.fromCode(intent.code) - } - - override fun getIntent() = Intent.of(resource.intent?.toCode()) - - override fun setStatus(status: Status, reason: String?) { - resource.status = MedicationRequest.MedicationRequestStatus.fromCode(status.string) - resource.statusReason = reason?.let { CodeableConcept(Coding().setCode(it)) } - } - - override fun getStatus() = Status.of(resource.status.toCode()) - - override fun setBasedOn(reference: Reference) { - resource.addBasedOn(reference) - } - - override fun getBasedOn() = resource.basedOn.lastOrNull() - - override fun copy() = CPGImmunizationRequest(resource.copy()) -} diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGMedicationRequest.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGMedicationRequest.kt index c39ec82d2e..4e78d23f7f 100644 --- a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGMedicationRequest.kt +++ b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGMedicationRequest.kt @@ -16,13 +16,14 @@ package com.google.android.fhir.workflow.activity.resource.request +import com.google.android.fhir.workflow.activity.resource.request.Status.REVOKED import org.hl7.fhir.r4.model.CodeableConcept import org.hl7.fhir.r4.model.Coding import org.hl7.fhir.r4.model.MedicationRequest import org.hl7.fhir.r4.model.Reference class CPGMedicationRequest(override val resource: MedicationRequest) : - CPGRequestResource(resource) { + CPGRequestResource(resource, MedicationRequestStatusMapper) { override fun setIntent(intent: Intent) { resource.intent = MedicationRequest.MedicationRequestIntent.fromCode(intent.code) } @@ -30,11 +31,12 @@ class CPGMedicationRequest(override val resource: MedicationRequest) : override fun getIntent() = Intent.of(resource.intent?.toCode()) override fun setStatus(status: Status, reason: String?) { - resource.status = MedicationRequest.MedicationRequestStatus.fromCode(status.string) + resource.status = + MedicationRequest.MedicationRequestStatus.fromCode(mapper.mapStatusToCode(status)) resource.statusReason = reason?.let { CodeableConcept(Coding().setCode(it)) } } - override fun getStatus() = Status.of(resource.status.toCode()) + override fun getStatusCode() = resource.status?.toCode() override fun setBasedOn(reference: Reference) { resource.addBasedOn(reference) @@ -43,4 +45,20 @@ class CPGMedicationRequest(override val resource: MedicationRequest) : override fun getBasedOn() = resource.basedOn.lastOrNull() override fun copy() = CPGMedicationRequest(resource.copy()) + + private object MedicationRequestStatusMapper : StatusCodeMapperImpl() { + override fun mapCodeToStatus(code: String?): Status { + return when (code) { + "stopped" -> REVOKED + else -> super.mapCodeToStatus(code) + } + } + + override fun mapStatusToCode(status: Status): String? { + return when (status) { + REVOKED -> "stopped" + else -> super.mapStatusToCode(status) + } + } + } } diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGProposeDiagnosisTask.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGProposeDiagnosisTask.kt deleted file mode 100644 index 1b0d0a3c4a..0000000000 --- a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGProposeDiagnosisTask.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.fhir.workflow.activity.resource.request - -import org.hl7.fhir.r4.model.Task - -class CPGProposeDiagnosisTask(override val resource: Task) : CPGTaskRequest(resource) { - override fun copy() = CPGProposeDiagnosisTask(resource.copy()) -} diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGQuestionnaireTaskRequest.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGQuestionnaireTaskRequest.kt deleted file mode 100644 index dd254e0631..0000000000 --- a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGQuestionnaireTaskRequest.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.fhir.workflow.activity.resource.request - -import org.hl7.fhir.r4.model.Task - -class CPGQuestionnaireTaskRequest(override val resource: Task) : CPGTaskRequest(resource) { - override fun copy() = CPGQuestionnaireTaskRequest(resource.copy()) -} diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGRecordDetectedIssueTask.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGRecordDetectedIssueTask.kt deleted file mode 100644 index dd3744ef94..0000000000 --- a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGRecordDetectedIssueTask.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.fhir.workflow.activity.resource.request - -import org.hl7.fhir.r4.model.Task - -class CPGRecordDetectedIssueTask(override val resource: Task) : CPGTaskRequest(resource) { - override fun copy() = CPGRecordDetectedIssueTask(resource.copy()) -} diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGRecordInferenceTask.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGRecordInferenceTask.kt deleted file mode 100644 index d0f11af9c3..0000000000 --- a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGRecordInferenceTask.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.fhir.workflow.activity.resource.request - -import org.hl7.fhir.r4.model.Task - -class CPGRecordInferenceTask(override val resource: Task) : CPGTaskRequest(resource) { - override fun copy() = CPGRecordInferenceTask(resource.copy()) -} diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGReportFlagTask.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGReportFlagTask.kt deleted file mode 100644 index 58e1fe4e6c..0000000000 --- a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGReportFlagTask.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.fhir.workflow.activity.resource.request - -import org.hl7.fhir.r4.model.Task - -class CPGReportFlagTask(override val resource: Task) : CPGTaskRequest(resource) { - override fun copy() = CPGReportFlagTask(resource.copy()) -} diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGRequestResource.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGRequestResource.kt index d758746191..34a959fa2c 100644 --- a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGRequestResource.kt +++ b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGRequestResource.kt @@ -18,6 +18,10 @@ package com.google.android.fhir.workflow.activity.resource.request import com.google.android.fhir.logicalId import com.google.android.fhir.workflow.activity.resource.request.CPGRequestResource.Companion.of +import com.google.android.fhir.workflow.activity.resource.request.Intent.ORDER +import com.google.android.fhir.workflow.activity.resource.request.Intent.OTHER +import com.google.android.fhir.workflow.activity.resource.request.Intent.PLAN +import com.google.android.fhir.workflow.activity.resource.request.Intent.PROPOSAL import org.hl7.fhir.r4.model.CommunicationRequest import org.hl7.fhir.r4.model.IdType import org.hl7.fhir.r4.model.MedicationRequest @@ -47,7 +51,10 @@ import org.hl7.fhir.r4.model.Task * are derived from [MedicationRequest]. So the [MedicationRequest.meta.profile] is required to * create the appropriate [CPGRequestResource]. */ -sealed class CPGRequestResource(internal open val resource: R) where R : Resource { +sealed class CPGRequestResource( + internal open val resource: R, + internal val mapper: StatusCodeMapper, +) where R : Resource { val resourceType: ResourceType get() = resource.resourceType @@ -61,16 +68,14 @@ sealed class CPGRequestResource(internal open val resource: R) where R : Reso abstract fun setStatus(status: Status, reason: String? = null) - abstract fun getStatus(): Status + fun getStatus(): Status = mapper.mapCodeToStatus(getStatusCode()) + + abstract fun getStatusCode(): String? abstract fun setBasedOn(reference: Reference) abstract fun getBasedOn(): Reference? - fun update(update: R.() -> Unit) { - resource.update() - } - internal abstract fun copy(): CPGRequestResource internal fun copy(id: String, status: Status, intent: Intent): CPGRequestResource { @@ -92,8 +97,6 @@ sealed class CPGRequestResource(internal open val resource: R) where R : Reso CPGCommunicationRequest::class.java -> CPGCommunicationRequest(resource as CommunicationRequest) CPGMedicationRequest::class.java -> CPGMedicationRequest(resource as MedicationRequest) - CPGImmunizationRequest::class.java -> CPGImmunizationRequest(resource as MedicationRequest) - CPGServiceRequest::class.java -> CPGServiceRequest(resource as ServiceRequest) else -> { throw IllegalArgumentException("Unknown CPG Request type ${resource::class}.") } @@ -110,18 +113,10 @@ sealed class CPGRequestResource(internal open val resource: R) where R : Reso ) ) { CPGMedicationRequest(resource) - } else if ( - resource.meta.hasProfile( - "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-immunizationrequest", - ) - ) { - CPGImmunizationRequest(resource) } else { throw IllegalArgumentException("Unknown cpg profile") } - fun of(resource: ServiceRequest) = CPGServiceRequest(resource) - fun of(resource: CommunicationRequest) = CPGCommunicationRequest(resource) /** @@ -144,10 +139,11 @@ sealed class CPGRequestResource(internal open val resource: R) where R : Reso } /** - * PROPOSAL, PLAN and ORDER are the only intents we are interested in. All the other Request Intent - * values are represented by OTHER. See - * [codesystem-request-intent](https://www.hl7.org/FHIR/codesystem-request-intent.html) for the list - * of intents. + * [PROPOSAL], [PLAN] and [ORDER] are the only intents we are interested in. All the other Request + * Intent values are represented by [OTHER]. + * + * See [codesystem-request-intent](https://www.hl7.org/FHIR/codesystem-request-intent.html) for the + * list of intents. */ internal sealed class Intent(val code: String?) { data object PROPOSAL : Intent("proposal") @@ -158,6 +154,10 @@ internal sealed class Intent(val code: String?) { class OTHER(code: String?) : Intent(code) + override fun toString(): String { + return code ?: "null" + } + companion object { fun of(code: String?): Intent { return when (code) { @@ -171,33 +171,24 @@ internal sealed class Intent(val code: String?) { } /** - * This may not represent all the Request Resource status. For the activity flow, we may ony be - * interested in a few values and they should be represented here. + * For the activity flow, we are interested in a few status and they are be represented as + * individual values here. Everything else is represented by [OTHER]. + * + * See [codesystem-resource-status](https://build.fhir.org/codesystem-resource-status.html) for list + * of the status. */ -enum class Status(val string: String) { - DRAFT("draft"), - ACTIVE("active"), - ONHOLD("on-hold"), - REVOKED("revoked"), - COMPLETED("completed"), - ENTEREDINERROR("entered-in-error"), - UNKNOWN("unknown"), - NULL("null"), - ; +sealed interface Status { + data object DRAFT : Status - companion object { - fun of(code: String): Status { - return when (code) { - "draft" -> DRAFT - "active" -> ACTIVE - "on-hold" -> ONHOLD - "revoked" -> REVOKED - "completed" -> COMPLETED - "entered-in-error" -> ENTEREDINERROR - "unknown" -> UNKNOWN - "null" -> NULL - else -> NULL - } - } - } + data object ACTIVE : Status + + data object ONHOLD : Status + + data object REVOKED : Status + + data object COMPLETED : Status + + data object ENTEREDINERROR : Status + + class OTHER(val code: String?) : Status } diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGServiceRequest.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGServiceRequest.kt deleted file mode 100644 index 0c2da73bdf..0000000000 --- a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGServiceRequest.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.fhir.workflow.activity.resource.request - -import org.hl7.fhir.r4.model.Reference -import org.hl7.fhir.r4.model.ServiceRequest - -class CPGServiceRequest(override val resource: ServiceRequest) : - CPGRequestResource(resource) { - override fun setIntent(intent: Intent) { - resource.intent = ServiceRequest.ServiceRequestIntent.fromCode(intent.code) - } - - override fun getIntent() = Intent.of(resource.intent?.toCode()) - - override fun setStatus(status: Status, reason: String?) { - resource.status = ServiceRequest.ServiceRequestStatus.fromCode(status.string) - // resource.statusReason = reason?.let { CodeableConcept(Coding().setCode(it)) } - } - - override fun getStatus() = Status.of(resource.status.toCode()) - - override fun setBasedOn(reference: Reference) { - resource.addBasedOn(reference) - } - - override fun getBasedOn() = resource.basedOn.lastOrNull() - - override fun copy() = CPGServiceRequest(resource.copy()) -} diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGTaskRequest.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGTaskRequest.kt deleted file mode 100644 index ae0a10adf5..0000000000 --- a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/CPGTaskRequest.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.fhir.workflow.activity.resource.request - -import org.hl7.fhir.r4.model.CodeableConcept -import org.hl7.fhir.r4.model.Coding -import org.hl7.fhir.r4.model.Reference -import org.hl7.fhir.r4.model.Task - -abstract class CPGTaskRequest internal constructor(override val resource: Task) : - CPGRequestResource(resource) { - override fun setIntent(intent: Intent) { - resource.intent = Task.TaskIntent.fromCode(intent.code) - } - - override fun getIntent() = Intent.of(resource.intent?.toCode()) - - override fun setStatus(status: Status, reason: String?) { - resource.status = Task.TaskStatus.fromCode(status.string) - resource.statusReason = reason?.let { CodeableConcept(Coding().setCode(it)) } - } - - override fun getStatus() = Status.of(resource.status.toCode()) - - override fun setBasedOn(reference: Reference) { - resource.addBasedOn(reference) - } - - override fun getBasedOn() = resource.basedOn.lastOrNull() -} diff --git a/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/StatusCodeMapper.kt b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/StatusCodeMapper.kt new file mode 100644 index 0000000000..1657eadab7 --- /dev/null +++ b/workflow/src/main/java/com/google/android/fhir/workflow/activity/resource/request/StatusCodeMapper.kt @@ -0,0 +1,64 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.fhir.workflow.activity.resource.request + +import com.google.android.fhir.workflow.activity.resource.request.Status.ACTIVE +import com.google.android.fhir.workflow.activity.resource.request.Status.COMPLETED +import com.google.android.fhir.workflow.activity.resource.request.Status.DRAFT +import com.google.android.fhir.workflow.activity.resource.request.Status.ENTEREDINERROR +import com.google.android.fhir.workflow.activity.resource.request.Status.ONHOLD +import com.google.android.fhir.workflow.activity.resource.request.Status.OTHER +import com.google.android.fhir.workflow.activity.resource.request.Status.REVOKED + +/** + * Since request resources may have different code for same status, each [CPGRequestResource] should + * provide its own mapper. See + * [columns next to status](https://build.fhir.org/ig/HL7/cqf-recommendations/activityflow.html#activity-lifecycle---request-phases-proposal-plan-order) + */ +internal interface StatusCodeMapper { + + fun mapCodeToStatus(code: String?): Status + + fun mapStatusToCode(status: Status): String? +} + +/** A base implementation where the status and code map each other. */ +internal open class StatusCodeMapperImpl : StatusCodeMapper { + override fun mapCodeToStatus(code: String?): Status { + return when (code) { + "draft" -> DRAFT + "active" -> ACTIVE + "on-hold" -> ONHOLD + "revoked" -> REVOKED + "completed" -> COMPLETED + "entered-in-error" -> ENTEREDINERROR + else -> OTHER(code) + } + } + + override fun mapStatusToCode(status: Status): String? { + return when (status) { + DRAFT -> "draft" + ACTIVE -> "active" + ONHOLD -> "on-hold" + REVOKED -> "revoked" + COMPLETED -> "completed" + ENTEREDINERROR -> "entered-in-error" + is OTHER -> status.code + } + } +} diff --git a/workflow/src/test/java/com/google/android/fhir/workflow/activity/ActivityFlowTest.kt b/workflow/src/test/java/com/google/android/fhir/workflow/activity/ActivityFlowTest.kt index 13b545d2ec..d88bee2de6 100644 --- a/workflow/src/test/java/com/google/android/fhir/workflow/activity/ActivityFlowTest.kt +++ b/workflow/src/test/java/com/google/android/fhir/workflow/activity/ActivityFlowTest.kt @@ -91,15 +91,15 @@ class ActivityFlowTest { var cachedResourceId = "" flow - .draftPlan() + .preparePlan() .onSuccess { draftPlan -> - draftPlan.update { addNote(Annotation(MarkdownType("Draft plan looks OK."))) } + draftPlan.resource.addNote(Annotation(MarkdownType("Draft plan looks OK."))) - flow.startPlan(draftPlan).onSuccess { planPhase -> + flow.initiatePlan(draftPlan).onSuccess { planPhase -> val updatedPlan = - planPhase.getRequest().copy().apply { + planPhase.getRequestResource().copy().apply { setStatus(Status.ACTIVE) - update { addNote(Annotation(MarkdownType("Plan looks OK."))) } + resource.addNote(Annotation(MarkdownType("Plan looks OK."))) } planPhase @@ -121,21 +121,22 @@ class ActivityFlowTest { .read(MedicationRequest::class.java, IdType("MedicationRequest", cachedResourceId)) .let { CPGMedicationRequest(it) } val resumedPlanFlow = ActivityFlow.of(repository, planToResume) - + (resumedPlanFlow.getCurrentPhase() as Phase.RequestPhase) + .getRequestResource() assertThat(resumedPlanFlow.getCurrentPhase().getPhaseName()).isEqualTo(Phase.PhaseName.PLAN) - assertThat(resumedPlanFlow.draftPlan().isFailure).isTrue() + assertThat(resumedPlanFlow.preparePlan().isFailure).isTrue() resumedPlanFlow - .draftOrder() + .prepareOrder() .onSuccess { draftOrder -> - draftOrder.update { addNote(Annotation(MarkdownType("Draft order looks OK."))) } + draftOrder.resource.addNote(Annotation(MarkdownType("Draft order looks OK."))) - resumedPlanFlow.startOrder(draftOrder).onSuccess { orderPhase -> + resumedPlanFlow.initiateOrder(draftOrder).onSuccess { orderPhase -> val updatedOrder = - orderPhase.getRequest().copy().apply { + orderPhase.getRequestResource().copy().apply { setStatus(Status.ACTIVE) - update { addNote(Annotation(MarkdownType("Order looks OK."))) } + resource.addNote(Annotation(MarkdownType("Order looks OK."))) } orderPhase @@ -154,20 +155,20 @@ class ActivityFlowTest { assertThat(resumedOrderFlow.getCurrentPhase().getPhaseName()).isEqualTo(Phase.PhaseName.ORDER) - assertThat(resumedOrderFlow.draftPlan().isFailure).isTrue() - assertThat(resumedOrderFlow.draftOrder().isFailure).isTrue() + assertThat(resumedOrderFlow.preparePlan().isFailure).isTrue() + assertThat(resumedOrderFlow.prepareOrder().isFailure).isTrue() resumedOrderFlow - .draftPerform(CPGMedicationDispenseEvent::class.java) + .preparePerform(CPGMedicationDispenseEvent::class.java) .onSuccess { draftEvent -> - draftEvent.update { addNote(Annotation(MarkdownType("Draft event looks OK."))) } + draftEvent.resource.let { it.addNote(Annotation(MarkdownType("Draft event looks OK."))) } - resumedOrderFlow.startPerform(draftEvent).onSuccess { performPhase -> + resumedOrderFlow.initiatePerform(draftEvent).onSuccess { performPhase -> val updatedEvent = - performPhase.getEvent().copy().apply { + performPhase.getEventResource().copy().apply { setStatus(EventStatus.INPROGRESS) - update { addNote(Annotation(MarkdownType("Event looks OK."))) } + resource.let { it.addNote(Annotation(MarkdownType("Event looks OK."))) } } performPhase @@ -187,9 +188,9 @@ class ActivityFlowTest { assertThat(resumedPerformFlow.getCurrentPhase().getPhaseName()) .isEqualTo(Phase.PhaseName.PERFORM) - assertThat(resumedPerformFlow.draftPlan().isFailure).isTrue() - assertThat(resumedPerformFlow.draftOrder().isFailure).isTrue() - assertThat(resumedPerformFlow.draftPerform(CPGMedicationDispenseEvent::class.java).isFailure) + assertThat(resumedPerformFlow.preparePlan().isFailure).isTrue() + assertThat(resumedPerformFlow.prepareOrder().isFailure).isTrue() + assertThat(resumedPerformFlow.preparePerform(CPGMedicationDispenseEvent::class.java).isFailure) .isTrue() (resumedPerformFlow.getCurrentPhase() as Phase.EventPhase<*>).complete() @@ -226,44 +227,54 @@ class ActivityFlowTest { val draftPlanResult = flow - .draftPlan() - .onSuccess { - it.update { addNote().apply { text = "This Draft Plan looks OK, so approving it." } } + .preparePlan() + .onSuccess { planCPGRequest -> + planCPGRequest.resource.let { + it.addNote().apply { text = "This Draft Plan looks OK, so approving it." } + } } .onFailure { throw it } val draftPlan = draftPlanResult.getOrNull()!! - val newPhase = flow.startPlan(draftPlan) + val newPhase = flow.initiatePlan(draftPlan) assertThat(newPhase.isSuccess).isTrue() assertThat(newPhase.getOrNull()!!.getPhaseName()).isEqualTo(Phase.PhaseName.PLAN) newPhase.onSuccess { planPhase -> - val request = planPhase.getRequest() - request.update { addNote().apply { text = "This Plan looks OK, so marking it active." } } + val request = planPhase.getRequestResource() + request.resource.let { + it.addNote().apply { text = "This Plan looks OK, so marking it active." } + } request.setStatus(Status.ACTIVE) planPhase.update(request).onSuccess { assertThat(planPhase.resume().isFailure).isTrue() // Get latest request from the phase. - val request = planPhase.getRequest() + val request = planPhase.getRequestResource() - request.update { addNote().apply { text = "This Plan looks OK, so marking it active." } } + request.resource.let { + it.addNote().apply { text = "This Plan looks OK, so marking it active." } + } val draftOrder = - flow.draftOrder().onSuccess { - it.update { addNote().apply { text = "This Draft Order looks OK, so approving it." } } + flow.prepareOrder().onSuccess { + request.resource.let { + it.addNote().apply { text = "This Draft Order looks OK, so approving it." } + } } - val newPhase = flow.startOrder(draftOrder.getOrThrow()) + val newPhase = flow.initiateOrder(draftOrder.getOrThrow()) assertThat(newPhase.isSuccess).isTrue() newPhase.onSuccess { orderPhase -> - val order = orderPhase.getRequest() + val order = orderPhase.getRequestResource() println(" order == draftOrder : ${order == draftOrder.getOrNull()}") - order.update { addNote().apply { text = "This Order looks OK, so marking it active." } } + request.resource.let { + it.addNote().apply { text = "This Order looks OK, so marking it active." } + } order.setStatus(Status.ACTIVE) orderPhase.update(order) @@ -271,34 +282,42 @@ class ActivityFlowTest { assertThat(orderPhase.resume().isFailure).isTrue() println( - "\nPrinting all the notes for the request in Phase: ${flow.getCurrentPhase().getPhaseName().name} Status: ${orderPhase.getRequest().getStatus()}", + "\nPrinting all the notes for the request in Phase: ${flow.getCurrentPhase().getPhaseName().name} Status: ${orderPhase.getRequestResource().getStatusCode()}", ) - orderPhase.getRequest().resource.note.forEachIndexed { a, b -> + orderPhase.getRequestResource().resource.note.forEachIndexed { a, b -> println("Note $a. ${b.text}") } val draftEvent = - flow.draftPerform(CPGCommunicationEvent::class.java).onSuccess { - it.update { addNote().apply { text = "This Event looks OK, so approving it." } } + flow.preparePerform(CPGCommunicationEvent::class.java).onSuccess { + it.resource.let { + it.addNote().apply { text = "This Event looks OK, so approving it." } + } } assertThat(draftEvent.isSuccess).isTrue() - val newPhase = flow.startPerform(draftEvent.getOrThrow()) + val newPhase = flow.initiatePerform(draftEvent.getOrThrow()) assertThat(newPhase.isSuccess).isTrue() newPhase.onSuccess { performPhase -> - val event = performPhase.getEvent() - event.update { addNote().apply { text = "This Event looks OK, so marking it active." } } + val event = performPhase.getEventResource() + event.resource.let { + it.addNote().apply { text = "This Event looks OK, so marking it active." } + } event.setStatus(EventStatus.INPROGRESS) performPhase.update(event) performPhase.complete() val phase = flow.getCurrentPhase() as Phase.EventPhase - println("\nPrinting all the notes for the event in ${phase.getEvent().getStatus()}") - phase.getEvent().resource.note.forEachIndexed { a, b -> println("Note $a. ${b.text}") } + println( + "\nPrinting all the notes for the event in ${phase.getEventResource().getStatusCode()}", + ) + phase.getEventResource().resource.note.forEachIndexed { a, b -> + println("Note $a. ${b.text}") + } } } } @@ -327,15 +346,15 @@ class ActivityFlowTest { var cachedRequestId = "" flow - .draftPlan() + .preparePlan() .onSuccess { draftPlan -> - draftPlan.update { addNote(Annotation(MarkdownType("Draft plan looks OK."))) } + draftPlan.resource.let { it.addNote(Annotation(MarkdownType("Draft plan looks OK."))) } - flow.startPlan(draftPlan).onSuccess { planPhase -> + flow.initiatePlan(draftPlan).onSuccess { planPhase -> val updatedPlan = - planPhase.getRequest().copy().apply { + planPhase.getRequestResource().copy().apply { setStatus(Status.ACTIVE) - update { addNote(Annotation(MarkdownType("Plan looks OK."))) } + resource.let { it.addNote(Annotation(MarkdownType("Plan looks OK."))) } } planPhase @@ -356,15 +375,15 @@ class ActivityFlowTest { "Flow is in ${resumedPlanFlow.getCurrentPhase().getPhaseName()} " } resumedPlanFlow - .draftOrder() + .prepareOrder() .onSuccess { draftOrder -> - draftOrder.update { addNote(Annotation(MarkdownType("Draft order looks OK."))) } + draftOrder.resource.let { it.addNote(Annotation(MarkdownType("Draft order looks OK."))) } - resumedPlanFlow.startOrder(draftOrder).onSuccess { orderPhase -> + resumedPlanFlow.initiateOrder(draftOrder).onSuccess { orderPhase -> val updatedOrder = - orderPhase.getRequest().copy().apply { + orderPhase.getRequestResource().copy().apply { setStatus(Status.ACTIVE) - update { addNote(Annotation(MarkdownType("Order looks OK."))) } + resource.let { it.addNote(Annotation(MarkdownType("Order looks OK."))) } } orderPhase @@ -387,15 +406,15 @@ class ActivityFlowTest { } resumedOrderFlow - .draftPerform(CPGMedicationDispenseEvent::class.java) + .preparePerform(CPGMedicationDispenseEvent::class.java) .onSuccess { draftEvent -> - draftEvent.update { addNote(Annotation(MarkdownType("Draft event looks OK."))) } + draftEvent.resource.let { it.addNote(Annotation(MarkdownType("Draft event looks OK."))) } - resumedOrderFlow.startPerform(draftEvent).onSuccess { performPhase -> + resumedOrderFlow.initiatePerform(draftEvent).onSuccess { performPhase -> val updatedEvent = - performPhase.getEvent().copy().apply { + performPhase.getEventResource().copy().apply { setStatus(EventStatus.INPROGRESS) - update { addNote(Annotation(MarkdownType("Event looks OK."))) } + resource.let { it.addNote(Annotation(MarkdownType("Event looks OK."))) } } performPhase @@ -442,7 +461,7 @@ class ActivityFlowTest { val proposalPhase = ActivityFlow.of(repository, cpgCommunicationRequest) assertThat(proposalPhase.getCurrentPhase()).isInstanceOf(ProposalPhase::class.java) - assertThat(proposalPhase.draftPlan().isSuccess).isTrue() + assertThat(proposalPhase.preparePlan().isSuccess).isTrue() } @Test @@ -470,7 +489,7 @@ class ActivityFlowTest { val planPhase = ActivityFlow.of(repository, cpgCommunicationPlanRequest) assertThat(planPhase.getCurrentPhase()).isInstanceOf(PlanPhase::class.java) - assertThat(planPhase.draftPlan().isFailure).isTrue() + assertThat(planPhase.preparePlan().isFailure).isTrue() } @Test @@ -498,7 +517,7 @@ class ActivityFlowTest { val orderPhase = ActivityFlow.of(repository, cpgCommunicationOrderRequest) assertThat(orderPhase.getCurrentPhase()).isInstanceOf(OrderPhase::class.java) - assertThat(orderPhase.draftPlan().isFailure).isTrue() + assertThat(orderPhase.preparePlan().isFailure).isTrue() } @Test @@ -521,7 +540,7 @@ class ActivityFlowTest { val performPhase = ActivityFlow.of(repository, cpgCommunicationEvent) assertThat(performPhase.getCurrentPhase()).isInstanceOf(PerformPhase::class.java) - assertThat(performPhase.draftPlan().isFailure).isTrue() + assertThat(performPhase.preparePlan().isFailure).isTrue() } @Test @@ -546,7 +565,7 @@ class ActivityFlowTest { val proposalPhase = ActivityFlow.of(repository, cpgCommunicationRequest) assertThat(proposalPhase.getCurrentPhase()).isInstanceOf(ProposalPhase::class.java) - assertThat(proposalPhase.draftOrder().isSuccess).isTrue() + assertThat(proposalPhase.prepareOrder().isSuccess).isTrue() } @Test @@ -574,7 +593,7 @@ class ActivityFlowTest { val planPhase = ActivityFlow.of(repository, cpgCommunicationPlanRequest) assertThat(planPhase.getCurrentPhase()).isInstanceOf(PlanPhase::class.java) - assertThat(planPhase.draftOrder().isSuccess).isTrue() + assertThat(planPhase.prepareOrder().isSuccess).isTrue() } @Test @@ -602,7 +621,7 @@ class ActivityFlowTest { val orderPhase = ActivityFlow.of(repository, cpgCommunicationOrderRequest) assertThat(orderPhase.getCurrentPhase()).isInstanceOf(OrderPhase::class.java) - assertThat(orderPhase.draftOrder().isFailure).isTrue() + assertThat(orderPhase.prepareOrder().isFailure).isTrue() } @Test @@ -625,7 +644,7 @@ class ActivityFlowTest { val performPhase = ActivityFlow.of(repository, cpgCommunicationEvent) assertThat(performPhase.getCurrentPhase()).isInstanceOf(PerformPhase::class.java) - assertThat(performPhase.draftOrder().isFailure).isTrue() + assertThat(performPhase.prepareOrder().isFailure).isTrue() } @Test @@ -650,7 +669,7 @@ class ActivityFlowTest { val proposalPhase = ActivityFlow.of(repository, cpgCommunicationRequest) assertThat(proposalPhase.getCurrentPhase()).isInstanceOf(ProposalPhase::class.java) - assertThat(proposalPhase.draftPerform(CPGCommunicationEvent::class.java).isSuccess).isTrue() + assertThat(proposalPhase.preparePerform(CPGCommunicationEvent::class.java).isSuccess).isTrue() } @Test @@ -678,7 +697,7 @@ class ActivityFlowTest { val planPhase = ActivityFlow.of(repository, cpgCommunicationPlanRequest) assertThat(planPhase.getCurrentPhase()).isInstanceOf(PlanPhase::class.java) - assertThat(planPhase.draftPerform(CPGCommunicationEvent::class.java).isSuccess).isTrue() + assertThat(planPhase.preparePerform(CPGCommunicationEvent::class.java).isSuccess).isTrue() } @Test @@ -706,7 +725,7 @@ class ActivityFlowTest { val orderPhase = ActivityFlow.of(repository, cpgCommunicationOrderRequest) assertThat(orderPhase.getCurrentPhase()).isInstanceOf(OrderPhase::class.java) - assertThat(orderPhase.draftPerform(CPGCommunicationEvent::class.java).isSuccess).isTrue() + assertThat(orderPhase.preparePerform(CPGCommunicationEvent::class.java).isSuccess).isTrue() } @Test @@ -729,7 +748,7 @@ class ActivityFlowTest { val performPhase = ActivityFlow.of(repository, cpgCommunicationEvent) assertThat(performPhase.getCurrentPhase()).isInstanceOf(PerformPhase::class.java) - assertThat(performPhase.draftPerform(CPGCommunicationEvent::class.java).isFailure).isTrue() + assertThat(performPhase.preparePerform(CPGCommunicationEvent::class.java).isFailure).isTrue() } @Test @@ -756,20 +775,20 @@ class ActivityFlowTest { assertThat(flow.getCurrentPhase()).isInstanceOf(ProposalPhase::class.java) flow - .draftPlan() + .preparePlan() .onSuccess { it.setStatus(Status.ACTIVE) - flow.startPlan(it) + flow.initiatePlan(it) } .onFailure { fail("Unexpected", it) } assertThat(flow.getCurrentPhase()).isInstanceOf(PlanPhase::class.java) flow - .draftOrder() + .prepareOrder() .onSuccess { it.setStatus(Status.ACTIVE) - flow.startOrder(it) + flow.initiateOrder(it) } .onFailure { fail("Unexpected", it) }