|
17 | 17 | package com.google.android.fhir.workflow.activity |
18 | 18 |
|
19 | 19 | import androidx.annotation.WorkerThread |
| 20 | +import ca.uhn.fhir.model.api.IQueryParameterType |
| 21 | +import ca.uhn.fhir.rest.param.ReferenceParam |
20 | 22 | import com.google.android.fhir.workflow.activity.phase.Phase |
21 | 23 | import com.google.android.fhir.workflow.activity.phase.Phase.PhaseName |
22 | 24 | import com.google.android.fhir.workflow.activity.phase.Phase.PhaseName.ORDER |
23 | 25 | import com.google.android.fhir.workflow.activity.phase.Phase.PhaseName.PERFORM |
24 | 26 | import com.google.android.fhir.workflow.activity.phase.Phase.PhaseName.PLAN |
25 | 27 | import com.google.android.fhir.workflow.activity.phase.Phase.PhaseName.PROPOSAL |
| 28 | +import com.google.android.fhir.workflow.activity.phase.ReadOnlyRequestPhase |
26 | 29 | import com.google.android.fhir.workflow.activity.phase.event.PerformPhase |
| 30 | +import com.google.android.fhir.workflow.activity.phase.event.PerformPhase.Companion.`class` |
| 31 | +import com.google.android.fhir.workflow.activity.phase.idType |
27 | 32 | import com.google.android.fhir.workflow.activity.phase.request.OrderPhase |
28 | 33 | import com.google.android.fhir.workflow.activity.phase.request.PlanPhase |
29 | 34 | import com.google.android.fhir.workflow.activity.phase.request.ProposalPhase |
30 | 35 | import com.google.android.fhir.workflow.activity.resource.event.CPGCommunicationEvent |
31 | 36 | import com.google.android.fhir.workflow.activity.resource.event.CPGEventResource |
32 | 37 | import com.google.android.fhir.workflow.activity.resource.event.CPGOrderMedicationEvent |
| 38 | +import com.google.android.fhir.workflow.activity.resource.event.EventStatus |
33 | 39 | import com.google.android.fhir.workflow.activity.resource.request.CPGCommunicationRequest |
34 | 40 | import com.google.android.fhir.workflow.activity.resource.request.CPGMedicationRequest |
35 | 41 | import com.google.android.fhir.workflow.activity.resource.request.CPGRequestResource |
36 | 42 | import com.google.android.fhir.workflow.activity.resource.request.Intent |
| 43 | +import com.google.android.fhir.workflow.activity.resource.request.Status |
| 44 | +import org.hl7.fhir.r4.model.Bundle |
| 45 | +import org.hl7.fhir.r4.model.Communication |
| 46 | +import org.hl7.fhir.r4.model.CommunicationRequest |
| 47 | +import org.hl7.fhir.r4.model.MedicationDispense |
| 48 | +import org.hl7.fhir.r4.model.MedicationRequest |
| 49 | +import org.hl7.fhir.r4.model.Reference |
37 | 50 | import org.opencds.cqf.fhir.api.Repository |
38 | 51 |
|
39 | 52 | /** |
@@ -181,6 +194,37 @@ private constructor( |
181 | 194 | return currentPhase |
182 | 195 | } |
183 | 196 |
|
| 197 | + /** Returns a read only list of all the previous phases of the flow. */ |
| 198 | + fun getPreviousPhases(): List<ReadOnlyRequestPhase<R>> { |
| 199 | + val phases = mutableListOf<ReadOnlyRequestPhase<R>>() |
| 200 | + var current: Phase? = currentPhase |
| 201 | + |
| 202 | + while (current != null) { |
| 203 | + val basedOn: Reference? = |
| 204 | + if (current is Phase.RequestPhase<*>) { |
| 205 | + (current).getRequestResource().getBasedOn() |
| 206 | + } else if (current is Phase.EventPhase<*>) { |
| 207 | + (current).getEventResource().getBasedOn() |
| 208 | + } else { |
| 209 | + null |
| 210 | + } |
| 211 | + |
| 212 | + val basedOnRequest = |
| 213 | + basedOn?.let { |
| 214 | + repository.read(it.`class`, it.idType)?.let { CPGRequestResource.of(it) as R } |
| 215 | + } |
| 216 | + current = |
| 217 | + when (basedOnRequest?.getIntent()) { |
| 218 | + Intent.PROPOSAL -> ProposalPhase(repository, basedOnRequest) |
| 219 | + Intent.PLAN -> PlanPhase(repository, basedOnRequest) |
| 220 | + Intent.ORDER -> OrderPhase(repository, basedOnRequest) |
| 221 | + else -> null |
| 222 | + } |
| 223 | + current?.let { phases.add(it as ReadOnlyRequestPhase<R>) } |
| 224 | + } |
| 225 | + return phases |
| 226 | + } |
| 227 | + |
184 | 228 | /** |
185 | 229 | * Prepares a plan resource based on the state of the [currentPhase] and returns it to the caller |
186 | 230 | * without persisting any changes into [repository]. |
@@ -303,5 +347,116 @@ private constructor( |
303 | 347 | resource: CPGOrderMedicationEvent<*>, |
304 | 348 | ): ActivityFlow<CPGMedicationRequest, CPGOrderMedicationEvent<*>> = |
305 | 349 | ActivityFlow(repository, null, resource) |
| 350 | + |
| 351 | + /** Returns a list of active flows associated with the [patientId]. */ |
| 352 | + fun of( |
| 353 | + repository: Repository, |
| 354 | + patientId: String, |
| 355 | + ): List<ActivityFlow<CPGRequestResource<*>, CPGEventResource<*>>> { |
| 356 | + /** |
| 357 | + * NOTE: After adding a new |
| 358 | + * [activity](https://build.fhir.org/ig/HL7/cqf-recommendations/examples-activities.html), add |
| 359 | + * appropriate resource classes to eventTypes & requestTypes for the api to be able to search |
| 360 | + * for flows in database. |
| 361 | + */ |
| 362 | + val eventTypes = |
| 363 | + listOf( |
| 364 | + MedicationDispense::class.java, |
| 365 | + Communication::class.java, |
| 366 | + ) |
| 367 | + |
| 368 | + val events = |
| 369 | + eventTypes |
| 370 | + .flatMap { |
| 371 | + repository |
| 372 | + .search( |
| 373 | + Bundle::class.java, |
| 374 | + it, |
| 375 | + mutableMapOf<String, MutableList<IQueryParameterType>>( |
| 376 | + "subject" to mutableListOf(ReferenceParam("Patient/$patientId")), |
| 377 | + ), |
| 378 | + null, |
| 379 | + ) |
| 380 | + .entry |
| 381 | + .map { it.resource } |
| 382 | + } |
| 383 | + .map { CPGEventResource.of(it) } |
| 384 | + |
| 385 | + val requestTypes = |
| 386 | + listOf( |
| 387 | + MedicationRequest::class.java, |
| 388 | + CommunicationRequest::class.java, |
| 389 | + ) |
| 390 | + |
| 391 | + // This is used to fetch the `basedOn` resource for a request/event to form RequestChain |
| 392 | + val idToRequestMap: MutableMap<String, CPGRequestResource<*>> = |
| 393 | + requestTypes |
| 394 | + .flatMap { |
| 395 | + repository |
| 396 | + .search( |
| 397 | + Bundle::class.java, |
| 398 | + it, |
| 399 | + mutableMapOf<String, MutableList<IQueryParameterType>>( |
| 400 | + "subject" to mutableListOf(ReferenceParam("Patient/$patientId")), |
| 401 | + ), |
| 402 | + null, |
| 403 | + ) |
| 404 | + .entry |
| 405 | + .map { it.resource } |
| 406 | + } |
| 407 | + .map { CPGRequestResource.of(it) } |
| 408 | + .associateByTo(LinkedHashMap()) { "${it.resourceType}/${it.logicalId}" } |
| 409 | + |
| 410 | + fun addBasedOn( |
| 411 | + request: RequestChain, |
| 412 | + ): RequestChain? { |
| 413 | + val basedOn = request.request?.getBasedOn() ?: request.event?.getBasedOn() |
| 414 | + // look up the cache for the parent resource and add to the chain |
| 415 | + return basedOn?.let { reference -> |
| 416 | + idToRequestMap[reference.reference]?.let { requestResource -> |
| 417 | + idToRequestMap.remove(reference.reference) |
| 418 | + RequestChain(request = requestResource).apply { this.basedOn = addBasedOn(this) } |
| 419 | + } |
| 420 | + } |
| 421 | + } |
| 422 | + |
| 423 | + val requestChain = |
| 424 | + events.map { RequestChain(event = it).apply { this.basedOn = addBasedOn(this) } } + |
| 425 | + idToRequestMap.values |
| 426 | + .filter { |
| 427 | + it.getIntent() == Intent.ORDER || |
| 428 | + it.getIntent() == Intent.PLAN || |
| 429 | + it.getIntent() == Intent.PROPOSAL |
| 430 | + } |
| 431 | + .sortedByDescending { it.getIntent().code } |
| 432 | + .mapNotNull { |
| 433 | + if (idToRequestMap.containsKey("${it.resourceType}/${it.logicalId}")) { |
| 434 | + RequestChain(request = it).apply { this.basedOn = addBasedOn(this) } |
| 435 | + } else { |
| 436 | + null |
| 437 | + } |
| 438 | + } |
| 439 | + return requestChain |
| 440 | + .filter { |
| 441 | + if (it.event != null) { |
| 442 | + it.event.getStatus() != EventStatus.COMPLETED |
| 443 | + } else if (it.request != null) { |
| 444 | + it.request.getStatus() != Status.COMPLETED |
| 445 | + } else { |
| 446 | + false |
| 447 | + } |
| 448 | + } |
| 449 | + .map { ActivityFlow(repository, it.request, it.event) } |
| 450 | + } |
306 | 451 | } |
307 | 452 | } |
| 453 | + |
| 454 | +/** |
| 455 | + * Represents the chain of event/requests of an activity flow. A [RequestChain] would either have a |
| 456 | + * [request] or an [event]. |
| 457 | + */ |
| 458 | +private data class RequestChain( |
| 459 | + val request: CPGRequestResource<*>? = null, |
| 460 | + val event: CPGEventResource<*>? = null, |
| 461 | + var basedOn: RequestChain? = null, |
| 462 | +) |
0 commit comments