Skip to content

Commit

Permalink
Update tracing and appointments logic (#30)
Browse files Browse the repository at this point in the history
* update tracing and appointments logic

* Update AppointmentRegisterDao.kt
  • Loading branch information
sevenreup authored Mar 1, 2024
1 parent 91c25a5 commit 133a40f
Show file tree
Hide file tree
Showing 16 changed files with 141 additions and 143 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@

package org.smartregister.fhircore.engine.data.local.register.dao

import ca.uhn.fhir.rest.gclient.TokenClientParam
import ca.uhn.fhir.rest.param.ParamPrefixEnum
import com.google.android.fhir.FhirEngine
import com.google.android.fhir.logicalId
import com.google.android.fhir.search.Operation
import com.google.android.fhir.search.filter.TokenParamFilterCriterion
import com.google.android.fhir.search.has
import com.google.android.fhir.search.search
import java.util.Calendar
import javax.inject.Inject
Expand All @@ -44,7 +48,7 @@ import org.smartregister.fhircore.engine.domain.model.RegisterData
import org.smartregister.fhircore.engine.domain.repository.RegisterDao
import org.smartregister.fhircore.engine.domain.util.PaginationConstant
import org.smartregister.fhircore.engine.util.DefaultDispatcherProvider
import org.smartregister.fhircore.engine.util.LOGGED_IN_PRACTITIONER
import org.smartregister.fhircore.engine.util.SharedPreferenceKey
import org.smartregister.fhircore.engine.util.SharedPreferencesHelper
import org.smartregister.fhircore.engine.util.extension.asReference
import org.smartregister.fhircore.engine.util.extension.extractAge
Expand All @@ -65,7 +69,10 @@ constructor(
) : RegisterDao {

private val currentPractitioner by lazy {
sharedPreferencesHelper.read<Practitioner>(key = LOGGED_IN_PRACTITIONER, decodeWithGson = true)
sharedPreferencesHelper.read(
key = SharedPreferenceKey.PRACTITIONER_ID.name,
defaultValue = null
)
}

private fun Appointment.patientRef() =
Expand All @@ -85,7 +92,6 @@ constructor(
return fhirEngine
.search<Appointment> {
filter(Appointment.STATUS, { value = of(Appointment.AppointmentStatus.BOOKED.toCode()) })
filter(Appointment.DATE, { value = of(DateTimeType.today()) })
}
.map { it.resource }
.count {
Expand Down Expand Up @@ -116,6 +122,7 @@ constructor(
page: Int = -1
): List<Appointment> {
filters as AppointmentRegisterFilter
val patientTypeFilterTag = applicationConfiguration().patientTypeFilterTagViaMetaCodingSystem
val searchResults =
fhirEngine.search<Appointment> {
if (!loadAll) count = PaginationConstant.DEFAULT_PAGE_SIZE
Expand All @@ -139,33 +146,31 @@ constructor(
}
)

// if (filters.myPatients && currentPractititoner != null)
// filter(Appointment.PRACTITIONER, { value =
// currentPractititoner!!.referenceValue() })
//
// filters.patientCategory?.let {
// val paramQueries: List<(TokenParamFilterCriterion.() -> Unit)> =
// it.flatMap { healthStatus ->
// val coding: Coding =
// Coding().apply {
// system = "https://d-tree.org"
// code = healthStatus.name.lowercase().replace("_", "-")
// }
// val alternativeCoding: Coding =
// Coding().apply {
// system = "https://d-tree.org"
// code = healthStatus.name.lowercase()
// }
//
// return@flatMap listOf<Coding>(coding, alternativeCoding).map<
// Coding, TokenParamFilterCriterion.() -> Unit> { c -> { value = of(c) } }
// }
//
// has<Patient>(Appointment.PATIENT){
// filter(TokenClientParam("_tag"), *paramQueries.toTypedArray(), operation =
// Operation.OR)
// }
// }
if (filters.myPatients && currentPractitioner != null)
filter(Appointment.PRACTITIONER, { value = currentPractitioner!! })

filters.patientCategory?.let {
val paramQueries: List<(TokenParamFilterCriterion.() -> Unit)> =
it.flatMap { healthStatus ->
val coding: Coding =
Coding().apply {
system = patientTypeFilterTag
code = healthStatus.name.lowercase().replace("_", "-")
}
val alternativeCoding: Coding =
Coding().apply {
system = patientTypeFilterTag
code = healthStatus.name.lowercase()
}

return@flatMap listOf<Coding>(coding, alternativeCoding).map<
Coding, TokenParamFilterCriterion.() -> Unit> { c -> { value = of(c) } }
}

has<Patient>(Appointment.PATIENT) {
filter(TokenClientParam("_tag"), *paramQueries.toTypedArray(), operation = Operation.OR)
}
}

filters.reasonCode?.let {
val codeableConcept =
Expand All @@ -184,7 +189,8 @@ constructor(
return searchResults.map { it.resource }.filter {
val patientAssignmentFilter =
!filters.myPatients ||
(it.practitionerRef()?.reference == currentPractitioner?.asReference()?.reference)
(it.practitionerRef()?.reference ==
currentPractitioner?.asReference(ResourceType.Practitioner)?.reference)
val patientCategoryFilter =
filters.patientCategory == null || (patientCategoryMatches(it, filters.patientCategory))

Expand All @@ -195,7 +201,8 @@ constructor(
it.hasStart() &&
patientAssignmentFilter &&
patientCategoryFilter &&
appointmentReasonFilter
appointmentReasonFilter &&
it.patientRef() != null
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,10 @@ constructor(
sharedPreferencesHelper
) {

override val tracingCoding: Coding = Coding("https://d-tree.org", "home-tracing", "Home Tracing")
override val tracingCoding: Coding = taskCode

companion object {
val taskCode: Coding =
Coding("https://d-tree.org/fhir/contact-tracing", "home-tracing", "Home Tracing")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ constructor(
sharedPreferencesHelper
) {

override val tracingCoding: Coding =
Coding("https://d-tree.org", "phone-tracing", "Phone Tracing")
override val tracingCoding: Coding = taskCode

companion object {
val taskCode: Coding =
Coding("https://d-tree.org/fhir/contact-tracing", "phone-tracing", "Phone Tracing")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ import org.smartregister.fhircore.engine.domain.model.RegisterData
import org.smartregister.fhircore.engine.domain.repository.RegisterDao
import org.smartregister.fhircore.engine.domain.util.PaginationConstant
import org.smartregister.fhircore.engine.util.DefaultDispatcherProvider
import org.smartregister.fhircore.engine.util.LOGGED_IN_PRACTITIONER
import org.smartregister.fhircore.engine.util.SharedPreferenceKey
import org.smartregister.fhircore.engine.util.SharedPreferencesHelper
import org.smartregister.fhircore.engine.util.extension.asDdMmmYyyy
import org.smartregister.fhircore.engine.util.extension.asReference
Expand Down Expand Up @@ -94,7 +94,10 @@ constructor(
patient.extractOfficialIdentifier() ?: HivRegisterDao.ResourceValue.BLANK

private val currentPractitioner by lazy {
sharedPreferencesHelper.read<Practitioner>(key = LOGGED_IN_PRACTITIONER, decodeWithGson = true)
sharedPreferencesHelper.read(
key = SharedPreferenceKey.PRACTITIONER_ID.name,
defaultValue = null
)
}

private val filtersForValidTask: BaseSearch.() -> Unit = {
Expand Down Expand Up @@ -125,6 +128,7 @@ constructor(
page: Int = -1
): List<Pair<Patient, Iterable<Task>>> {
filters as TracingRegisterFilter
val filterFilter = applicationConfiguration().patientTypeFilterTagViaMetaCodingSystem
val patients: List<Patient> =
fhirEngine
.search<Patient> {
Expand All @@ -139,12 +143,12 @@ constructor(
it.flatMap { healthStatus ->
val coding: Coding =
Coding().apply {
system = "https://d-tree.org"
system = filterFilter
code = healthStatus.name.lowercase().replace("_", "-")
}
val alternativeCoding: Coding =
Coding().apply {
system = "https://d-tree.org"
system = filterFilter
code = healthStatus.name.lowercase()
}

Expand All @@ -155,8 +159,11 @@ constructor(
filter(TokenClientParam("_tag"), *paramQueries.toTypedArray(), operation = Operation.OR)
}

if (filters.isAssignedToMe) {
filter(Patient.GENERAL_PRACTITIONER, { value = currentPractitioner!!.referenceValue() })
if (filters.isAssignedToMe && currentPractitioner != null) {
filter(
Patient.GENERAL_PRACTITIONER,
{ value = currentPractitioner?.asReference(ResourceType.Practitioner)?.reference }
)
}
}
.map { it.resource }
Expand Down Expand Up @@ -298,8 +305,9 @@ constructor(
.patientTypeFilterTagViaMetaCodingSystem
val tasks = validTasks(patient)

val attempt = tracingRepository.getTracingAttempt(patient)
val attempt = tracingRepository.getTracingAttempt(patient, tasks)
var dueData = getDueDate(it)

if (dueData == null) {
tasks.minOfOrNull { task -> task.authoredOn }?.let { date -> dueData = date }
}
Expand Down Expand Up @@ -354,7 +362,7 @@ constructor(
subjectType = ResourceType.Patient,
subjectParam = CarePlan.SUBJECT
)
.filter { carePlan -> carePlan.status.equals(CarePlan.CarePlanStatus.ACTIVE) }
.filter { carePlan -> carePlan.status == CarePlan.CarePlanStatus.ACTIVE }

suspend fun Patient.activeConditions() =
defaultRepository.patientConditions(this.logicalId).filter { condition ->
Expand All @@ -364,6 +372,7 @@ constructor(
suspend fun Patient.practitioners(): List<Practitioner> {
return generalPractitioner.mapNotNull {
try {
if (it.reference == null) return@mapNotNull null
val id = it.reference.replace("Practitioner/", "")
fhirEngine.get(ResourceType.Practitioner, id) as Practitioner
} catch (e: Exception) {
Expand Down Expand Up @@ -427,6 +436,7 @@ constructor(
tracingRepository
.getTracingAttempt(list = listResource)
.copy(reasons = tasks.mapNotNull { task -> task.reasonCode?.codingFirstRep?.code })

val oldestTaskDate = tasks.minOfOrNull { it.authoredOn }
return RegisterData.TracingRegisterData(
logicalId = this.logicalId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ import com.google.android.fhir.logicalId
import com.google.android.fhir.search.Operation
import com.google.android.fhir.search.Order
import com.google.android.fhir.search.search
import com.google.android.fhir.testing.jsonParser
import java.util.Date
import javax.inject.Inject
import org.hl7.fhir.r4.model.CodeableConcept
import org.hl7.fhir.r4.model.Coding
import org.hl7.fhir.r4.model.Encounter
import org.hl7.fhir.r4.model.IdType
import org.hl7.fhir.r4.model.ListResource
import org.hl7.fhir.r4.model.Observation
import org.hl7.fhir.r4.model.Patient
Expand All @@ -38,7 +38,9 @@ import org.smartregister.fhircore.engine.domain.model.TracingHistory
import org.smartregister.fhircore.engine.domain.model.TracingOutcome
import org.smartregister.fhircore.engine.domain.model.TracingOutcomeDetails
import org.smartregister.fhircore.engine.domain.util.PaginationConstant
import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid
import org.smartregister.fhircore.engine.util.extension.referenceValue
import timber.log.Timber

class TracingRepository @Inject constructor(val fhirEngine: FhirEngine) {
suspend fun getTracingHistory(
Expand Down Expand Up @@ -237,13 +239,14 @@ class TracingRepository @Inject constructor(val fhirEngine: FhirEngine) {
.firstOrNull()
}

suspend fun getTracingAttempt(patient: Patient): TracingAttempt {
suspend fun getTracingAttempt(patient: Patient, tasks: List<Task>): TracingAttempt {
val list = getPatientListResource(patient)
list?.let { Timber.e(jsonParser.encodeResourceToString(list)) }
return list?.let { toTracingAttempt(it) }
?: TracingAttempt(
historyId = null,
lastAttempt = null,
numberOfAttempts = 0,
numberOfAttempts = if (tasks.isEmpty()) Int.MAX_VALUE else 0,
outcome = "",
reasons = listOf()
)
Expand All @@ -260,62 +263,25 @@ class TracingRepository @Inject constructor(val fhirEngine: FhirEngine) {
)
}

private suspend fun toTracingAttempt(list: ListResource): TracingAttempt {
var lastAttempt: Encounter? = null
list
.entry
.map { it.item }
.filter { it.referenceElement.resourceType == ResourceType.Encounter.name }
.forEach { ref ->
val resourceId = IdType(ref.reference).idPart
kotlin
.runCatching { fhirEngine.get<Encounter>(resourceId) }
.onSuccess { enc ->
lastAttempt = maxOf(lastAttempt, enc, nullsFirst(compareBy { it.period?.start?.time }))
}
.onFailure { it.printStackTrace() }
}

val obs =
lastAttempt?.let {
fhirEngine.search<Observation> {
filter(Observation.ENCOUNTER, { value = it.referenceValue() })
filter(
Observation.CODE,
{
value =
of(
CodeableConcept(
Coding().apply {
system = "https://d-tree.org"
code = "tracing-outcome-conducted"
}
)
)
},
{
value =
of(
CodeableConcept(
Coding().apply {
system = "https://d-tree.org"
code = "tracing-outcome-unconducted"
}
)
)
},
operation = Operation.OR
)
count = 1
}
}
private fun toTracingAttempt(list: ListResource): TracingAttempt {
val lastAttempt: ListResource.ListEntryComponent? =
list
.entry
.filter { it.item.referenceElement.resourceType == ResourceType.Encounter.name }
.sortedByDescending { it.date }
.firstOrNull()

val outcome =
obs
?.map { it.resource }
?.firstOrNull { it.hasValueCodeableConcept() }
?.valueCodeableConcept
?.text
lastAttempt?.let { _ ->
list.entry
.firstOrNull { entry ->
entry.flag.codingFirstRep.display ==
lastAttempt.item.reference.extractLogicalIdUuid() &&
entry.flag.codingFirstRep.code == "tracing-outcome"
}
?.flag
?.text
}
?: ""

val attempts =
Expand All @@ -326,7 +292,7 @@ class TracingRepository @Inject constructor(val fhirEngine: FhirEngine) {

return TracingAttempt(
numberOfAttempts = attempts,
lastAttempt = lastAttempt?.period?.start,
lastAttempt = lastAttempt?.date,
outcome = outcome,
reasons = listOf(),
historyId = list.logicalId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import org.hl7.fhir.r4.model.Encounter
import org.hl7.fhir.r4.model.EpisodeOfCare
import org.hl7.fhir.r4.model.Group
import org.hl7.fhir.r4.model.Immunization
import org.hl7.fhir.r4.model.Observation
import org.hl7.fhir.r4.model.Patient
import org.hl7.fhir.r4.model.PlanDefinition
import org.hl7.fhir.r4.model.ResourceFactory
Expand Down Expand Up @@ -72,6 +73,7 @@ class TransformSupportServices @Inject constructor(val simpleWorkerContext: Simp
"PlanDefinition_Action" -> PlanDefinition.PlanDefinitionActionComponent()
"Group_Characteristic" -> Group.GroupCharacteristicComponent()
"Appointment_Participant" -> Appointment.AppointmentParticipantComponent()
"Observation_Component" -> Observation.ObservationComponentComponent()
else -> ResourceFactory.createResourceOrType(name)
}
}
Expand Down
Loading

0 comments on commit 133a40f

Please sign in to comment.