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 ede47ac171..c0f936a362 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 @@ -17,23 +17,37 @@ package com.google.android.fhir.workflow.activity import androidx.annotation.WorkerThread +import ca.uhn.fhir.model.api.IQueryParameterType +import ca.uhn.fhir.rest.param.ReferenceParam 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 import com.google.android.fhir.workflow.activity.phase.Phase.PhaseName.PERFORM import com.google.android.fhir.workflow.activity.phase.Phase.PhaseName.PLAN import com.google.android.fhir.workflow.activity.phase.Phase.PhaseName.PROPOSAL +import com.google.android.fhir.workflow.activity.phase.PreviousPhaseIterator +import com.google.android.fhir.workflow.activity.phase.ReadOnlyRequestPhase import com.google.android.fhir.workflow.activity.phase.event.PerformPhase +import com.google.android.fhir.workflow.activity.phase.event.PerformPhase.Companion.`class` +import com.google.android.fhir.workflow.activity.phase.idType import com.google.android.fhir.workflow.activity.phase.request.OrderPhase import com.google.android.fhir.workflow.activity.phase.request.PlanPhase import com.google.android.fhir.workflow.activity.phase.request.ProposalPhase import com.google.android.fhir.workflow.activity.resource.event.CPGCommunicationEvent import com.google.android.fhir.workflow.activity.resource.event.CPGEventResource import com.google.android.fhir.workflow.activity.resource.event.CPGOrderMedicationEvent +import com.google.android.fhir.workflow.activity.resource.event.EventStatus import com.google.android.fhir.workflow.activity.resource.request.CPGCommunicationRequest 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 com.google.android.fhir.workflow.activity.resource.request.Status +import org.hl7.fhir.r4.model.Bundle +import org.hl7.fhir.r4.model.Communication +import org.hl7.fhir.r4.model.CommunicationRequest +import org.hl7.fhir.r4.model.MedicationDispense +import org.hl7.fhir.r4.model.MedicationRequest +import org.hl7.fhir.r4.model.Reference import org.opencds.cqf.fhir.api.Repository /** @@ -181,6 +195,48 @@ private constructor( return currentPhase } + /** + * Returns an iterator to go over the previous states of the activity flow. The iterator provides + * a read-only view to the phase. + */ + fun getPreviousPhase(): PreviousPhaseIterator { + return object : PreviousPhaseIterator { + var current: Phase? = currentPhase + + override fun hasPrevious(): Boolean { + return if (current is Phase.RequestPhase<*>) { + (current as? Phase.RequestPhase<*>)?.getRequestResource()?.getBasedOn() != null + } else { + (current as? Phase.EventPhase<*>)?.getEventResource()?.getBasedOn() != null + } + } + + override fun previous(): ReadOnlyRequestPhase? { + val basedOn: Reference? = + if (current is Phase.RequestPhase<*>) { + (current as Phase.RequestPhase<*>).getRequestResource().getBasedOn() + } else if (current is Phase.EventPhase<*>) { + (current as Phase.EventPhase<*>).getEventResource().getBasedOn() + } else { + null + } + + val basedOnRequest = + basedOn?.let { + repository.read(it.`class`, it.idType)?.let { CPGRequestResource.of(it) as R } + } + current = + when (basedOnRequest?.getIntent()) { + Intent.PROPOSAL -> ProposalPhase(repository, basedOnRequest) + Intent.PLAN -> PlanPhase(repository, basedOnRequest) + Intent.ORDER -> OrderPhase(repository, basedOnRequest) + else -> null + } + return basedOnRequest?.let { ReadOnlyRequestPhase(it) } + } + } + } + /** * Prepares a plan resource based on the state of the [currentPhase] and returns it to the caller * without persisting any changes into [repository]. @@ -303,5 +359,109 @@ private constructor( resource: CPGOrderMedicationEvent<*>, ): ActivityFlow> = ActivityFlow(repository, null, resource) + + /** Returns a list of active flows associated with the [patientId]. */ + fun of( + repository: Repository, + patientId: String, + ): List, CPGEventResource<*>>> { + val eventTypes = + listOf( + MedicationDispense::class.java, + Communication::class.java, + ) + + val events = + eventTypes + .flatMap { + repository + .search( + Bundle::class.java, + it, + mutableMapOf>( + "subject" to mutableListOf(ReferenceParam("Patient/$patientId")), + ), + null, + ) + .entry + .map { it.resource } + } + .map { CPGEventResource.of(it) } + + val requestTypes = + listOf( + MedicationRequest::class.java, + CommunicationRequest::class.java, + ) + + val cache: MutableMap> = + requestTypes + .flatMap { + repository + .search( + Bundle::class.java, + it, + mutableMapOf>( + "subject" to mutableListOf(ReferenceParam("Patient/$patientId")), + ), + null, + ) + .entry + .map { it.resource } + } + .map { CPGRequestResource.of(it) } + .associateByTo(LinkedHashMap()) { "${it.resourceType}/${it.logicalId}" } + + fun addBasedOn( + request: RequestChain, + ): RequestChain? { + val basedOn = request.request?.getBasedOn() ?: request.event?.getBasedOn() + // look up the cache for the parent resource and add to the chain + return basedOn?.let { reference -> + cache[reference.reference]?.let { requestResource -> + cache.remove(reference.reference) + RequestChain(request = requestResource).apply { this.basedOn = addBasedOn(this) } + } + } + } + + val requestChain = + events.map { RequestChain(event = it).apply { this.basedOn = addBasedOn(this) } } + + cache.values + .filter { + it.getIntent() == Intent.ORDER || + it.getIntent() == Intent.PLAN || + it.getIntent() == Intent.PROPOSAL + } + .sortedByDescending { it.getIntent().code } + .mapNotNull { + if (cache.containsKey("${it.resourceType}/${it.logicalId}")) { + RequestChain(request = it).apply { this.basedOn = addBasedOn(this) } + } else { + null + } + } + return requestChain + .filter { + if (it.event != null) { + it.event.getStatus() != EventStatus.COMPLETED + } else if (it.request != null) { + it.request.getStatus() != Status.COMPLETED + } else { + false + } + } + .map { ActivityFlow(repository, it.request, it.event) } + } } } + +/** + * Represents the chain of event/requests of an activity flow. A [RequestChain] would either have a + * [request] or an [event]. + */ +internal data class RequestChain( + val request: CPGRequestResource<*>? = null, + val event: CPGEventResource<*>? = null, + var basedOn: RequestChain? = null, +) 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 a11ce85e23..afb0b7a91c 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 @@ -19,6 +19,7 @@ package com.google.android.fhir.workflow.activity.phase import androidx.annotation.WorkerThread import com.google.android.fhir.workflow.activity.resource.event.CPGEventResource 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 @@ -77,3 +78,28 @@ internal fun checkEquals(a: Reference, b: Reference) = a.reference == b.referenc /** Returns an [IdType] of a [Reference]. This is required for [Repository.read] api. */ internal val Reference.idType get() = IdType(reference) + +/** Iterate through the previous phases of a current activity flow. */ +interface PreviousPhaseIterator> { + fun hasPrevious(): Boolean + + fun previous(): ReadOnlyRequestPhase? +} + +/** Provides a read-only view of a request phase. */ +class ReadOnlyRequestPhase>(private val cpgRequestResource: R) { + /** Returns the [Phase.PhaseName] of this phase. */ + fun getPhase() = + when (cpgRequestResource.getIntent()) { + Intent.PROPOSAL -> Phase.PhaseName.PROPOSAL + Intent.PLAN -> Phase.PhaseName.PLAN + Intent.ORDER -> Phase.PhaseName.ORDER + else -> + throw IllegalArgumentException( + "Resource intent can't be ${cpgRequestResource.getIntent().code} ", + ) + } + + /** Returns the underlying [CPGRequestResource] of this phase. */ + fun getRequestResource() = cpgRequestResource +} 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 168c95bdca..60f0ce6cd9 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 @@ -139,7 +139,7 @@ class PerformPhase>( * 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` + internal val Reference.`class` get() = getResourceClass(reference.split("/")[0]) /** @@ -165,7 +165,7 @@ class PerformPhase>( "${inputPhase.getPhaseName().name} request is still in ${inputRequest.getStatusCode()} status." } - val eventRequest = CPGEventResource.of(inputRequest, eventClass) + val eventRequest = CPGEventResource.from(inputRequest, eventClass) eventRequest.setStatus(EventStatus.PREPARATION) eventRequest.setBasedOn(inputRequest.asReference()) eventRequest as E 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 699545c416..5304977dfb 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 @@ -23,6 +23,7 @@ import com.google.android.fhir.workflow.activity.resource.request.CPGMedicationR import com.google.android.fhir.workflow.activity.resource.request.CPGRequestResource import com.google.android.fhir.workflow.activity.resource.request.CPGRequestResource.Companion.of import org.hl7.fhir.r4.model.Communication +import org.hl7.fhir.r4.model.MedicationDispense import org.hl7.fhir.r4.model.Reference import org.hl7.fhir.r4.model.Resource import org.hl7.fhir.r4.model.ResourceType @@ -41,7 +42,7 @@ import org.hl7.fhir.r4.model.ResourceType * [CPGEventResource]s. */ sealed class CPGEventResource( - internal open val resource: R, + open val resource: R, internal val mapper: EventStatusCodeMapper, ) where R : Resource { @@ -65,12 +66,22 @@ sealed class CPGEventResource( companion object { - fun of(request: CPGRequestResource<*>, eventClass: Class<*>): CPGEventResource<*> { + internal fun from(from: CPGRequestResource<*>, to: Class<*>): CPGEventResource<*> { + return when (from) { + is CPGCommunicationRequest -> CPGCommunicationEvent.from(from) + is CPGMedicationRequest -> CPGOrderMedicationEvent.from(from, to) + else -> { + throw IllegalArgumentException("Unknown CPG Request type ${from::class}.") + } + } + } + + fun of(request: Resource): CPGEventResource<*> { return when (request) { - is CPGCommunicationRequest -> CPGCommunicationEvent.from(request) - is CPGMedicationRequest -> CPGOrderMedicationEvent.from(request, eventClass) + is Communication -> CPGCommunicationEvent(request) + is MedicationDispense -> CPGMedicationDispenseEvent(request) else -> { - throw IllegalArgumentException("Unknown CPG Request type ${request::class}.") + throw IllegalArgumentException("Unknown CPG event type ${request::class}.") } } } 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 34a959fa2c..33cf02126a 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 @@ -19,7 +19,6 @@ 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 @@ -28,8 +27,6 @@ import org.hl7.fhir.r4.model.MedicationRequest import org.hl7.fhir.r4.model.Reference import org.hl7.fhir.r4.model.Resource import org.hl7.fhir.r4.model.ResourceType -import org.hl7.fhir.r4.model.ServiceRequest -import org.hl7.fhir.r4.model.Task /** * This abstracts the @@ -52,7 +49,7 @@ import org.hl7.fhir.r4.model.Task * create the appropriate [CPGRequestResource]. */ sealed class CPGRequestResource( - internal open val resource: R, + open val resource: R, internal val mapper: StatusCodeMapper, ) where R : Resource { @@ -64,7 +61,7 @@ sealed class CPGRequestResource( internal abstract fun setIntent(intent: Intent) - internal abstract fun getIntent(): Intent + abstract fun getIntent(): Intent abstract fun setStatus(status: Status, reason: String? = null) @@ -125,9 +122,7 @@ sealed class CPGRequestResource( */ fun of(resource: R): CPGRequestResource { return when (resource) { - is Task -> of(resource) is MedicationRequest -> of(resource) - is ServiceRequest -> of(resource) is CommunicationRequest -> of(resource) else -> { throw IllegalArgumentException("Unknown CPG Request type ${resource::class}.") @@ -145,7 +140,7 @@ sealed class CPGRequestResource( * 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?) { +sealed class Intent(val code: String?) { data object PROPOSAL : Intent("proposal") data object PLAN : Intent("plan") diff --git a/workflow_demo/src/main/java/com/google/android/fhir/workflow/demo/ui/main/ActivityHandler.kt b/workflow_demo/src/main/java/com/google/android/fhir/workflow/demo/ui/main/ActivityHandler.kt index bdc668ad59..cbb6d96a0e 100644 --- a/workflow_demo/src/main/java/com/google/android/fhir/workflow/demo/ui/main/ActivityHandler.kt +++ b/workflow_demo/src/main/java/com/google/android/fhir/workflow/demo/ui/main/ActivityHandler.kt @@ -23,7 +23,7 @@ import com.google.android.fhir.workflow.activity.resource.request.CPGRequestReso import com.google.android.fhir.workflow.activity.resource.request.Status class ActivityHandler( - private val activityFlow: ActivityFlow, CPGEventResource<*>>, + val activityFlow: ActivityFlow, CPGEventResource<*>>, ) { suspend fun prepareAndInitiatePlan(): Result { diff --git a/workflow_demo/src/main/java/com/google/android/fhir/workflow/demo/ui/main/MainViewModel.kt b/workflow_demo/src/main/java/com/google/android/fhir/workflow/demo/ui/main/MainViewModel.kt index 9116182bc4..cbf7a5bab4 100644 --- a/workflow_demo/src/main/java/com/google/android/fhir/workflow/demo/ui/main/MainViewModel.kt +++ b/workflow_demo/src/main/java/com/google/android/fhir/workflow/demo/ui/main/MainViewModel.kt @@ -25,18 +25,18 @@ import com.google.android.fhir.FhirEngine import com.google.android.fhir.FhirEngineConfiguration import com.google.android.fhir.FhirEngineProvider import com.google.android.fhir.knowledge.KnowledgeManager -import com.google.android.fhir.search.search import com.google.android.fhir.workflow.FhirOperator import com.google.android.fhir.workflow.activity.ActivityFlow +import com.google.android.fhir.workflow.activity.phase.Phase import com.google.android.fhir.workflow.activity.resource.event.CPGEventResource import com.google.android.fhir.workflow.activity.resource.event.CPGMedicationDispenseEvent -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.repositories.FhirEngineRepository import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.hl7.fhir.r4.model.MedicationDispense @@ -89,42 +89,22 @@ class MainViewModel(private val application: Application) : AndroidViewModel(app val adapterData = combine(activityOptionFlow, enabledPhaseFlow) { configuration, phase -> - Log.d("TAG", "phaseFlow configuration: $configuration phase: $phase") - val nextPhase = loadChainAndReturnNextPhase() - enabledPhaseFlow.value = nextPhase - - // Initialize the activity flow - if (activityHandler == null) { - val resource = - if (order != null) { - order - } else if (plan != null) { - plan - } else if (proposal != null) { - proposal - } else { - null - } + Log.d("MVModel", "phaseFlow configuration: ${configuration.id} phase: $phase") + val nextPhase = loadChainAndReturnNextPhase() - resource?.let { - activityHandler = - perform?.let { - ActivityHandler( - ActivityFlow.of(repository, CPGMedicationDispenseEvent(it as MedicationDispense)) - as ActivityFlow, CPGEventResource<*>>, - ) - } - ?: ActivityHandler( - ActivityFlow.of(repository, CPGRequestResource.of(it) as CPGMedicationRequest) - as ActivityFlow, CPGEventResource<*>>, - ) + // Initialize the activity flow + if (activityHandler == null) { + ActivityFlow.of(repository, "active_apple_guy").firstOrNull()?.let { + activityHandler = ActivityHandler(it) + } } + enabledPhaseFlow.value = nextPhase + generateData(nextPhase, ::handleOnClick) } - generateData(nextPhase, ::handleOnClick) - } + .flowOn(Dispatchers.IO) private fun handleOnClick(phase: FlowPhase) { - Log.d("TAG", "handleOnClick: $phase") + Log.d("MVModel", "handleOnClick: $phase") viewModelScope.launch(Dispatchers.IO) { when (phase) { FlowPhase.PROPOSAL -> { @@ -206,36 +186,44 @@ class MainViewModel(private val application: Application) : AndroidViewModel(app } private suspend fun loadChainAndReturnNextPhase(): FlowPhase { - // TODO : Add logic to load resources as per the selected configuration. - val request = - fhirEngine.search { - filter(MedicationRequest.SUBJECT, { value = "Patient/active_apple_guy" }) - } - - val request2 = - fhirEngine.search { - filter(MedicationRequest.SUBJECT, { value = "Patient/active_apple_guy" }) - } - - // take the last resource and move backwards - proposal = null plan = null order = null perform = null - request.forEach { - if (it.resource.intent == MedicationRequest.MedicationRequestIntent.PROPOSAL) { - proposal = it.resource - } else if (it.resource.intent == MedicationRequest.MedicationRequestIntent.PLAN) { - plan = it.resource - } else if (it.resource.intent == MedicationRequest.MedicationRequestIntent.ORDER) { - order = it.resource + + fun setPhase(curPhase: Phase.PhaseName, resource: Resource) { + if (curPhase == Phase.PhaseName.PERFORM) { + perform = resource + } else if (curPhase == Phase.PhaseName.ORDER) { + order = resource + } else if (curPhase == Phase.PhaseName.PLAN) { + plan = resource + } else if (curPhase == Phase.PhaseName.PROPOSAL) { + proposal = resource + } + } + + fun setPhase(curPhase: Phase) { + if (curPhase is Phase.EventPhase<*>) { + setPhase(curPhase.getPhaseName(), curPhase.getEventResource().resource) } else { - // perform = it.resource + setPhase( + curPhase.getPhaseName(), + (curPhase as Phase.RequestPhase<*>).getRequestResource().resource, + ) } } - perform = request2.firstOrNull()?.resource + activityHandler?.let { + val curPhase = it.activityFlow.getCurrentPhase() + setPhase(curPhase) + val previousPhaseIterator = it.activityFlow.getPreviousPhase() + while (previousPhaseIterator.hasPrevious()) { + previousPhaseIterator.previous()?.let { + setPhase(it.getPhase(), it.getRequestResource().resource) + } + } + } return if (!proposalHandler.checkInstalledDependencies(activityOptionFlow.value)) { FlowPhase.INITIALIZE @@ -287,79 +275,41 @@ class MainViewModel(private val application: Application) : AndroidViewModel(app return list } - // TODO: CPGRequestResource : make getIntent public and then uncomment this. - // private fun getPhaseDetails(requestResource: Resource?): String { - // return if (requestResource == null) { - // "" - // } else if (requestResource is MedicationRequest) { - // val cpgRequestResource = CPGRequestResource.of(requestResource) - // - // val dosage = - // FhirContext.forR4Cached() - // .newJsonParser() - // .encodeToString(requestResource.dosageInstruction.first()) - // - // """ - // ID : ${cpgRequestResource.resourceType}/${cpgRequestResource.logicalId} - // Intent : ${cpgRequestResource.getIntent().code} - // Status : ${cpgRequestResource.getStatusCode()} - // BasedOn: ${cpgRequestResource.getBasedOn()?.reference} - // - // Additional Info: $dosage - // """ - // .trimIndent() - // } else if (requestResource is MedicationDispense) { - // val cpgRequestResource = CPGMedicationDispenseEvent(requestResource) - // - // val dosage = - // FhirContext.forR4Cached() - // .newJsonParser() - // .encodeToString(requestResource.dosageInstruction.first()) - // - // """ - // ID : ${cpgRequestResource.resourceType}/${cpgRequestResource.logicalId} - // Status : ${cpgRequestResource.getStatusCode()} - // BasedOn: ${cpgRequestResource.getBasedOn()?.reference} - // - // Additional Info: $dosage - // """ - // .trimIndent() - // } else { - // "" - // } - // } - private fun getPhaseDetails(requestResource: Resource?): String { return if (requestResource == null) { "" } else if (requestResource is MedicationRequest) { + val cpgRequestResource = CPGRequestResource.of(requestResource) + val dosage = FhirContext.forR4Cached() .newJsonParser() .encodeToString(requestResource.dosageInstruction.first()) """ - ID : ${requestResource.resourceType}/${requestResource.logicalId} - Intent : ${requestResource.intent.display} - Status : ${requestResource.status.display} - BasedOn: ${requestResource.getBasedOn().firstOrNull()?.reference} - - Additional Info: $dosage - """ + ID : ${cpgRequestResource.resourceType}/${cpgRequestResource.logicalId} + Intent : ${cpgRequestResource.getIntent().code} + Status : ${cpgRequestResource.getStatusCode()} + BasedOn: ${cpgRequestResource.getBasedOn()?.reference} + + Additional Info: $dosage + """ .trimIndent() } else if (requestResource is MedicationDispense) { + val cpgRequestResource = CPGMedicationDispenseEvent(requestResource) + val dosage = FhirContext.forR4Cached() .newJsonParser() .encodeToString(requestResource.dosageInstruction.first()) """ - ID : ${requestResource.resourceType}/${requestResource.logicalId} - Status : ${requestResource.status.display} - BasedOn: ${requestResource.authorizingPrescription.firstOrNull()?.reference} - - Additional Info: $dosage - """ + ID : ${cpgRequestResource.resourceType}/${cpgRequestResource.logicalId} + Status : ${cpgRequestResource.getStatusCode()} + BasedOn: ${cpgRequestResource.getBasedOn()?.reference} + + Additional Info: $dosage + """ .trimIndent() } else { ""