Skip to content

Commit

Permalink
🔧 Hot Fix : Correctly append PlanDefinition reference in CarePlan (#3385
Browse files Browse the repository at this point in the history
)

* Fix adding correct reference to CarePlan for the PlanDefinition

* Resolve PR feedback

* Restore gradle file as before

* Remove deprication comment

---------

Co-authored-by: Simon Kiarie <[email protected]>
  • Loading branch information
f-odhiambo and qiarie authored Jul 17, 2024
1 parent 5d5454b commit 83595aa
Show file tree
Hide file tree
Showing 2 changed files with 170 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,10 @@ constructor(
@ApplicationContext val context: Context,
) {
private val structureMapUtilities by lazy {
StructureMapUtilities(transformSupportServices.simpleWorkerContext, transformSupportServices)
StructureMapUtilities(
transformSupportServices.simpleWorkerContext,
transformSupportServices,
)
}

suspend fun generateOrUpdateCarePlan(
Expand Down Expand Up @@ -118,7 +121,10 @@ constructor(
val output =
fhirEngine
.search<CarePlan> {
filter(CarePlan.INSTANTIATES_CANONICAL, { value = planDefinition.referenceValue() })
filter(
CarePlan.INSTANTIATES_CANONICAL,
{ value = planDefinition.referenceValue() },
)
filter(CarePlan.SUBJECT, { value = subject.referenceValue() })
filter(
CarePlan.STATUS,
Expand All @@ -131,8 +137,7 @@ constructor(
.map { it.resource }
.firstOrNull()
?: CarePlan().apply {
// TODO delete this section once all PlanDefinitions are using new
// recommended approach
// TODO delete this section once all PlanDefinitions are using new recommended approach
this.title = planDefinition.title
this.description = planDefinition.description
this.instantiatesCanonical = listOf(CanonicalType(planDefinition.asReference().reference))
Expand All @@ -155,6 +160,8 @@ constructor(

val carePlanTasks = output.contained.filterIsInstance<Task>()

output.cleanPlanDefinitionCanonical()

if (carePlanModified) saveCarePlan(output, relatedEntityLocationTags)

if (carePlanTasks.isNotEmpty()) {
Expand All @@ -167,6 +174,19 @@ constructor(
return if (output.hasActivity()) output else null
}

// TODO refactor this code to remove hardcoded appended "PlanDefinition/" on
// https://github.com/opensrp/fhircore/issues/3386
private fun CarePlan.cleanPlanDefinitionCanonical() {
val canonicalValue = this.instantiatesCanonical.first().value
if (canonicalValue.contains('/').not()) {
this.instantiatesCanonical = listOf(CanonicalType("PlanDefinition/$canonicalValue"))
}
}

@VisibleForTesting
fun invokeCleanPlanDefinitionCanonical(carePlan: CarePlan) =
carePlan.cleanPlanDefinitionCanonical()

/** Implements OpenSRP's $lite version of CarePlan & Tasks generation via StructureMap(s) */
private suspend fun liteApplyPlanDefinitionOnPatient(
planDefinition: PlanDefinition,
Expand Down Expand Up @@ -209,7 +229,15 @@ constructor(
definition.dynamicValue.forEach { dynamicValue ->
if (definition.kind == ActivityDefinition.ActivityDefinitionKind.CAREPLAN) {
dynamicValue.expression.expression
.let { fhirPathEngine.evaluate(null, input, planDefinition, subject, it) }
.let {
fhirPathEngine.evaluate(
null,
input,
planDefinition,
subject,
it,
)
}
?.takeIf { it.isNotEmpty() }
?.let { evaluatedValue ->
// TODO handle cases where we explicitly need to set previous value as null,
Expand Down Expand Up @@ -255,8 +283,17 @@ constructor(
.filter { it.reference.startsWith(ResourceType.Task.name) }
.mapNotNull { getTask(it.extractId()) }
.forEach {
if (it.status.isIn(TaskStatus.REQUESTED, TaskStatus.READY, TaskStatus.INPROGRESS)) {
cancelTaskByTaskId(it.logicalId, "${carePlan.fhirType()} ${carePlan.status}")
if (
it.status.isIn(
TaskStatus.REQUESTED,
TaskStatus.READY,
TaskStatus.INPROGRESS,
)
) {
cancelTaskByTaskId(
it.logicalId,
"${carePlan.fhirType()} ${carePlan.status}",
)
}
}
}
Expand Down Expand Up @@ -367,7 +404,10 @@ constructor(
return taskPeriods
}

private fun extractTaskPeriodsFromDosage(dosage: List<Dosage>, carePlan: CarePlan): List<Period> {
private fun extractTaskPeriodsFromDosage(
dosage: List<Dosage>,
carePlan: CarePlan,
): List<Period> {
val taskPeriods = mutableListOf<Period>()
dosage
.flatMap { extractTaskPeriodsFromTiming(it.timing, carePlan) }
Expand Down Expand Up @@ -406,7 +446,11 @@ constructor(
)

if (resourceClosureConditionsMet) {
defaultRepository.updateResourcesRecursively(eventResource, subject, eventWorkFlow)
defaultRepository.updateResourcesRecursively(
eventResource,
subject,
eventWorkFlow,
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -566,8 +566,14 @@ class FhirCarePlanGeneratorTest : RobolectricTest() {
carePlan.description,
)
assertEquals(patient.logicalId, carePlan.subject.extractId())
assertEquals(DateTimeType.now().value.makeItReadable(), carePlan.created.makeItReadable())
assertEquals(patient.generalPractitionerFirstRep.extractId(), carePlan.author.extractId())
assertEquals(
DateTimeType.now().value.makeItReadable(),
carePlan.created.makeItReadable(),
)
assertEquals(
patient.generalPractitionerFirstRep.extractId(),
carePlan.author.extractId(),
)
assertEquals(
DateTimeType.now().value.makeItReadable(),
carePlan.period.start.makeItReadable(),
Expand Down Expand Up @@ -645,7 +651,10 @@ class FhirCarePlanGeneratorTest : RobolectricTest() {
assertEquals("HH Routine visit Plan", carePlan.title)
assertEquals("sample plan", carePlan.description)
assertEquals(group.logicalId, carePlan.subject.extractId())
assertEquals(DateTimeType.now().value.makeItReadable(), carePlan.created.makeItReadable())
assertEquals(
DateTimeType.now().value.makeItReadable(),
carePlan.created.makeItReadable(),
)
assertNotNull(carePlan.period.start)
assertTrue(carePlan.activityFirstRep.outcomeReference.isNotEmpty())

Expand Down Expand Up @@ -716,7 +725,10 @@ class FhirCarePlanGeneratorTest : RobolectricTest() {
carePlan.description,
)
assertEquals(group.logicalId, carePlan.subject.extractId())
assertEquals(DateTimeType.now().value.makeItReadable(), carePlan.created.makeItReadable())
assertEquals(
DateTimeType.now().value.makeItReadable(),
carePlan.created.makeItReadable(),
)
assertNotNull(carePlan.period.start)
assertTrue(carePlan.activityFirstRep.outcomeReference.isNotEmpty())

Expand Down Expand Up @@ -880,8 +892,12 @@ class FhirCarePlanGeneratorTest : RobolectricTest() {
val createdTasksSlot = mutableListOf<Resource>()
val updatedTasksSlot = mutableListOf<Resource>()
val booleanSlot = slot<Boolean>()
coEvery { defaultRepository.addOrUpdate(capture(booleanSlot), capture(createdTasksSlot)) } just
runs
coEvery {
defaultRepository.addOrUpdate(
capture(booleanSlot),
capture(createdTasksSlot),
)
} just runs
coEvery { defaultRepository.addOrUpdate(any(), capture(updatedTasksSlot)) } just runs
coEvery { fhirEngine.update(any()) } just runs
coEvery { fhirEngine.get<StructureMap>("528a8603-2e43-4a2e-a33d-1ec2563ffd3e") } returns
Expand Down Expand Up @@ -912,7 +928,9 @@ class FhirCarePlanGeneratorTest : RobolectricTest() {
subject = patient,
data =
Bundle()
.addEntry(Bundle.BundleEntryComponent().apply { resource = questionnaireResponse }),
.addEntry(
Bundle.BundleEntryComponent().apply { resource = questionnaireResponse },
),
)
?.also { carePlan: CarePlan ->
assertEquals(CarePlan.CarePlanStatus.COMPLETED, carePlan.status)
Expand Down Expand Up @@ -974,7 +992,10 @@ class FhirCarePlanGeneratorTest : RobolectricTest() {
.generateOrUpdateCarePlan(
plandefinition,
patient,
Bundle().addEntry(Bundle.BundleEntryComponent().apply { resource = questionnaireResponse }),
Bundle()
.addEntry(
Bundle.BundleEntryComponent().apply { resource = questionnaireResponse },
),
)
.also { _ ->
resourcesSlot.forEach { println(it.encodeResourceToString()) }
Expand Down Expand Up @@ -1190,7 +1211,10 @@ class FhirCarePlanGeneratorTest : RobolectricTest() {

val ancStart =
fhirCarePlanGenerator
.evaluateToDate(DateTimeType(lmp.value), "\$this + 3 'month'")!!
.evaluateToDate(
DateTimeType(lmp.value),
"\$this + 3 'month'",
)!!
.value
this.forEachIndexed { index, task ->
assertEquals(
Expand Down Expand Up @@ -1336,7 +1360,14 @@ class FhirCarePlanGeneratorTest : RobolectricTest() {
it.code.codingFirstRep.code == "33879002"
},
)
assertTrue(tasks.all { it.description.contains(it.reasonCode.text, true) })
assertTrue(
tasks.all {
it.description.contains(
it.reasonCode.text,
true,
)
},
)
assertTrue(
tasks.all {
it.`for`.reference == questionnaireResponses.first().subject.reference
Expand Down Expand Up @@ -1508,7 +1539,14 @@ class FhirCarePlanGeneratorTest : RobolectricTest() {
it.code.codingFirstRep.code == "33879002"
},
)
assertTrue(tasks.all { it.description.contains(it.reasonCode.text, true) })
assertTrue(
tasks.all {
it.description.contains(
it.reasonCode.text,
true,
)
},
)
assertTrue(
tasks.all {
it.`for`.reference == questionnaireResponses.first().subject.reference
Expand Down Expand Up @@ -1536,7 +1574,11 @@ class FhirCarePlanGeneratorTest : RobolectricTest() {
"IPV" -> assertEquals(task.groupIdentifier.value, "14_wk")
"MEASLES 1" -> assertEquals(task.groupIdentifier.value, "9_mo")
"MEASLES 2" -> assertEquals(task.groupIdentifier.value, "15_mo")
"YELLOW FEVER" -> assertEquals(task.groupIdentifier.value, "9_mo")
"YELLOW FEVER" ->
assertEquals(
task.groupIdentifier.value,
"9_mo",
)
"TYPHOID" -> assertEquals(task.groupIdentifier.value, "9_mo")
"HPV 1" -> assertEquals(task.groupIdentifier.value, "108_mo")
"HPV 2" -> assertEquals(task.groupIdentifier.value, "114_mo")
Expand Down Expand Up @@ -1605,8 +1647,14 @@ class FhirCarePlanGeneratorTest : RobolectricTest() {
val pcv2 = tasks.first { it.description.contains("PCV 2") }
val bcg = tasks.first { it.description.contains("BCG") }

assertEquals(opv2.partOf.first().reference.toString(), opv1.referenceValue())
assertEquals(pcv3.partOf.first().reference.toString(), pcv2.referenceValue())
assertEquals(
opv2.partOf.first().reference.toString(),
opv1.referenceValue(),
)
assertEquals(
pcv3.partOf.first().reference.toString(),
pcv2.referenceValue(),
)
assertTrue(bcg.partOf.isEmpty())
val c = Calendar.getInstance()
c.time = opv1.restriction?.period?.start!!
Expand Down Expand Up @@ -1666,10 +1714,21 @@ class FhirCarePlanGeneratorTest : RobolectricTest() {
carePlan.description,
)
assertEquals(patient.logicalId, carePlan.subject.extractId())
assertEquals(DateTimeType.now().value.makeItReadable(), carePlan.created.makeItReadable())
assertEquals(patient.generalPractitionerFirstRep.extractId(), carePlan.author.extractId())
assertEquals(
DateTimeType.now().value.makeItReadable(),
carePlan.created.makeItReadable(),
)
assertEquals(
patient.generalPractitionerFirstRep.extractId(),
carePlan.author.extractId(),
)
assertTrue(carePlan.activityFirstRep.outcomeReference.isNotEmpty())
coEvery { defaultRepository.addOrUpdate(capture(booleanSlot), capture(resourcesSlot)) }
coEvery {
defaultRepository.addOrUpdate(
capture(booleanSlot),
capture(resourcesSlot),
)
}
resourcesSlot
.filter { res -> res.resourceType == ResourceType.Task }
.map { it as Task }
Expand Down Expand Up @@ -1876,7 +1935,12 @@ class FhirCarePlanGeneratorTest : RobolectricTest() {
@Test
fun `updateDependentTaskDueDate should update dependent task without output`() {
coEvery { fhirEngine.search<Task>(any()) } returns emptyList()
coEvery { fhirEngine.get(ResourceType.Task, "650203d2-f327-4eb4-a9fd-741e0ce29c3f") } returns
coEvery {
fhirEngine.get(
ResourceType.Task,
"650203d2-f327-4eb4-a9fd-741e0ce29c3f",
)
} returns
opv0.apply {
status = TaskStatus.READY
partOf = listOf(Reference("Task/650203d2-f327-4eb4-a9fd-741e0ce29c3f"))
Expand All @@ -1902,7 +1966,12 @@ class FhirCarePlanGeneratorTest : RobolectricTest() {

@Test
fun `updateDependentTaskDueDate should run with dependent task, with output but no execution period start date`() {
coEvery { fhirEngine.get(ResourceType.Task, "650203d2-f327-4eb4-a9fd-741e0ce29c3f") } returns
coEvery {
fhirEngine.get(
ResourceType.Task,
"650203d2-f327-4eb4-a9fd-741e0ce29c3f",
)
} returns
opv0.apply {
status = TaskStatus.INPROGRESS
output = listOf()
Expand All @@ -1918,7 +1987,12 @@ class FhirCarePlanGeneratorTest : RobolectricTest() {
@Test
fun `updateDependentTaskDueDate with dependent task with output, execution period start date, and encounter part of reference that is null`() {
coEvery { fhirEngine.search<Task>(any()) } returns emptyList()
coEvery { fhirEngine.get(ResourceType.Task, "650203d2-f327-4eb4-a9fd-741e0ce29c3f") } returns
coEvery {
fhirEngine.get(
ResourceType.Task,
"650203d2-f327-4eb4-a9fd-741e0ce29c3f",
)
} returns
opv1.apply {
status = TaskStatus.INPROGRESS
output =
Expand Down Expand Up @@ -1951,7 +2025,12 @@ class FhirCarePlanGeneratorTest : RobolectricTest() {
@Test
fun `updateDependentTaskDueDate with dependent task with output, execution period start date, and encounter part of reference that is not an Immunization`() {
coEvery { fhirEngine.search<Task>(any()) } returns emptyList()
coEvery { fhirEngine.get(ResourceType.Task, "650203d2-f327-4eb4-a9fd-741e0ce29c3f") } returns
coEvery {
fhirEngine.get(
ResourceType.Task,
"650203d2-f327-4eb4-a9fd-741e0ce29c3f",
)
} returns
opv1.apply {
status = TaskStatus.INPROGRESS
output =
Expand Down Expand Up @@ -1991,7 +2070,12 @@ class FhirCarePlanGeneratorTest : RobolectricTest() {

@Test
fun `updateDependentTaskDueDate with Task input value equal or greater than difference between administration date and depedentTask executionPeriod start`() {
coEvery { fhirEngine.get(ResourceType.Task, "650203d2-f327-4eb4-a9fd-741e0ce29c3f") } returns
coEvery {
fhirEngine.get(
ResourceType.Task,
"650203d2-f327-4eb4-a9fd-741e0ce29c3f",
)
} returns
opv1.apply {
status = TaskStatus.INPROGRESS
output =
Expand Down Expand Up @@ -2496,7 +2580,10 @@ class FhirCarePlanGeneratorTest : RobolectricTest() {
assertEquals(planDefinition.title, carePlan.title)
assertEquals(planDefinition.description, carePlan.description)
assertEquals(patient.logicalId, carePlan.subject.extractId())
assertEquals(DateTimeType(dateToday).value.makeItReadable(), carePlan.created.makeItReadable())
assertEquals(
DateTimeType(dateToday).value.makeItReadable(),
carePlan.created.makeItReadable(),
)
assertEquals(patient.generalPractitionerFirstRep.extractId(), carePlan.author.extractId())

assertEquals(referenceDate.makeItReadable(), carePlan.period.start.makeItReadable())
Expand Down Expand Up @@ -2529,6 +2616,13 @@ class FhirCarePlanGeneratorTest : RobolectricTest() {
"HPV 1" to patient.birthDate.plusMonths(108),
"HPV 2" to patient.birthDate.plusMonths(114),
)

@Test
fun cleanPlanDefinitionCanonical() {
val carePlan = CarePlan().apply { instantiatesCanonical = listOf(CanonicalType("123456")) }
fhirCarePlanGenerator.invokeCleanPlanDefinitionCanonical(carePlan)
assertEquals("PlanDefinition/123456", carePlan.instantiatesCanonical.first().value)
}
}

private fun Date.asYyyyMmDd(): String = this.formatDate(SDF_YYYY_MM_DD)

0 comments on commit 83595aa

Please sign in to comment.