Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP - performance improvements #60

Merged
merged 10 commits into from
Jul 8, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import dagger.hilt.android.HiltAndroidApp
import javax.inject.Inject
import org.dtree.fhircore.dataclerk.data.QuestXFhirQueryResolver
import org.smartregister.fhircore.engine.data.remote.fhir.resource.ReferenceUrlResolver
import org.smartregister.fhircore.engine.trace.ReleaseTree
import org.smartregister.fhircore.engine.ui.questionnaire.QuestionnaireItemViewHolderFactoryMatchersProviderFactoryImpl
import org.smartregister.fhircore.engine.ui.questionnaire.items.CustomQuestItemDataProvider
import timber.log.Timber
Expand All @@ -53,6 +54,8 @@ class DataClerkApplication : Application(), DataCaptureConfig.Provider, Configur
Firebase.crashlytics.setCrashlyticsCollectionEnabled(false)
Firebase.analytics.setAnalyticsCollectionEnabled(false)
Timber.plant(Timber.DebugTree())
} else {
Timber.plant(ReleaseTree())
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class PatientPagingSource(private val dataStore: AppDataStore) : PagingSource<In
nextKey = nextPage,
)
} catch (e: Exception) {
Timber.e(e)
LoadResult.Error(e)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import org.dtree.fhircore.dataclerk.util.getFormattedAge
import org.smartregister.fhircore.engine.ui.questionnaire.QuestionnaireActivity
import org.smartregister.fhircore.engine.ui.questionnaire.QuestionnaireType
import org.smartregister.fhircore.engine.util.extension.toHumanDisplay
import timber.log.Timber

@HiltViewModel
class PatientViewModel
Expand Down Expand Up @@ -131,6 +132,7 @@ constructor(
screenState.emit(PatientDetailScreenState.Success(patient, data))
fetchResources(hashList)
} catch (e: Exception) {
Timber.e(e)
screenState.emit(PatientDetailScreenState.Error(e.message ?: "Error"))
}
}
Expand All @@ -153,6 +155,7 @@ constructor(
this[resourceId] = MutableStateFlow(ResourcePropertyState.Success(resource))
}
} catch (e: Exception) {
Timber.e(e)
resourceMapStatus.value =
resourceMapStatus.value.toMutableMap().apply {
this[resourceId] = MutableStateFlow(ResourcePropertyState.Error(e.message ?: "Error"))
Expand Down
7 changes: 3 additions & 4 deletions android/engine/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ dependencies {
exclude(group = "com.google.android.fhir", module = "common")
exclude(group = "com.google.android.fhir", module = "engine")
}
api("org.smartregister:data-capture:1.1.0-preview8.1-SNAPSHOT") {
api("org.smartregister:data-capture:1.1.0-preview11-SNAPSHOT") {
isTransitive = true
exclude(group = "ca.uhn.hapi.fhir")
exclude(group = "com.google.android.fhir", module = "engine")
Expand All @@ -195,9 +195,7 @@ dependencies {

api("org.smartregister:common:0.1.0-alpha05-preview3-SNAPSHOT") { isTransitive = true }

// api("org.smartregister:engine:1.0.0-preview7.1-SNAPSHOT") {
api("org.smartregister:engine:1.0.0-preview8-PERF-TEST-SNAPSHOT") {
// api("org.smartregister:engine:1.0.0-preview7.1-PERF-TEST5-SNAPSHOT") {
api("org.smartregister:engine:1.0.0-preview11-MWCore-SNAPSHOT") {
isTransitive = true
exclude(group = "com.google.android.fhir", module = "common")
exclude(group = "com.github.ben-manes.caffeine")
Expand All @@ -224,6 +222,7 @@ dependencies {
implementation("com.google.firebase:firebase-analytics")

implementation("androidx.core:core-splashscreen:1.0.1")
implementation("me.zhanghai.compose.preference:library:1.0.0")

// Hilt test dependencies
testImplementation("com.google.dagger:hilt-android-testing:${Deps.versions.hiltVersion}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import org.smartregister.fhircore.engine.ui.questionnaire.QuestionnaireConfig
import org.smartregister.fhircore.engine.util.DispatcherProvider
import org.smartregister.fhircore.engine.util.SharedPreferencesHelper
import org.smartregister.fhircore.engine.util.extension.decodeJson
import timber.log.Timber

/**
* A configuration store used to store all the application configurations. Application
Expand Down Expand Up @@ -63,6 +64,7 @@ constructor(
applicationConfiguration.update { config }
true
} catch (ex: ResourceNotFoundException) {
Timber.e(ex)
false
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright 2021 Ona Systems, Inc
*
* 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 org.smartregister.fhircore.engine.configuration.preferences

enum class SyncUploadStrategy {
Default,
Batch,
Single,
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package org.smartregister.fhircore.engine.data.domain.util

import androidx.paging.PagingSource
import androidx.paging.PagingState
import timber.log.Timber

/**
* Subclass of [PagingSource] that is used to paginate data on the register. Requires
Expand Down Expand Up @@ -64,8 +65,9 @@ class PaginatedDataSource<I : Any, O : Any>(
}

LoadResult.Page(data = data, prevKey = prevKey, nextKey = null)
} catch (exception: Exception) {
LoadResult.Error(exception)
} catch (e: Exception) {
Timber.e(e)
LoadResult.Error(e)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ package org.smartregister.fhircore.engine.data.local.purger

import com.google.android.fhir.FhirEngine
import com.google.android.fhir.datacapture.extensions.logicalId
import com.google.android.fhir.search.Operation
import com.google.android.fhir.search.Search
import com.google.android.fhir.search.filter.ReferenceParamFilterCriterion
import com.google.android.fhir.search.search
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
Expand All @@ -30,7 +30,6 @@ import org.hl7.fhir.r4.model.AuditEvent
import org.hl7.fhir.r4.model.CarePlan
import org.hl7.fhir.r4.model.Encounter
import org.hl7.fhir.r4.model.Observation
import org.hl7.fhir.r4.model.Patient
import org.hl7.fhir.r4.model.QuestionnaireResponse
import org.hl7.fhir.r4.model.Resource
import org.hl7.fhir.r4.model.ResourceType
Expand All @@ -50,13 +49,15 @@ class ResourcePurger(private val fhirEngine: FhirEngine) {
onPurgeAppointment()
}

private suspend fun <R : Resource> query(type: ResourceType): Flow<List<R>> = flow {
private suspend fun <R : Resource> query(
type: ResourceType,
query: (Search.() -> Unit)? = null,
): Flow<List<R>> = flow {
var start = 0
do {
val resources =
fhirEngine.search<R>(Search(type, count = PAGE_COUNT, from = PAGE_COUNT * start++)).map {
it.resource
}
val search = Search(type, count = PAGE_COUNT, from = PAGE_COUNT * start++)
query?.let { search.it() }
val resources = fhirEngine.search<R>(search).map { it.resource }
emit(resources)
} while (resources.isNotEmpty())
}
Expand All @@ -69,7 +70,7 @@ class ResourcePurger(private val fhirEngine: FhirEngine) {
fhirEngine.purge(this.resourceType, logicalId)
Timber.tag("purge").d("Purged $resourceType with id: $logicalId")
} catch (e: Exception) {
Timber.tag("purge:Exception").e(e.message!!)
Timber.tag("purge:Exception").e(e)
}

private suspend fun Iterable<Resource>.purge() = this.forEach { it.purge() }
Expand Down Expand Up @@ -104,73 +105,40 @@ class ResourcePurger(private val fhirEngine: FhirEngine) {
.purge()
}

/** Purge Inactive [CarePlan] together with it's associated [Task] */
private suspend fun onPurgeInActiveCarePlanWithTasks(carePlans: List<CarePlan>) =
carePlans
.filter { it.status != CarePlan.CarePlanStatus.ACTIVE }
.also { onPurgeCarePlanWithAssociatedTask(it, false) }

/** Purge Multiple [CarePlan.CarePlanStatus.ACTIVE] together with it's associated [Task] */
private suspend fun onPurgeMultipleActiveCarePlanWithTasks(carePlans: List<CarePlan>) =
carePlans
.filter { it.status == CarePlan.CarePlanStatus.ACTIVE }
.sortedBy { it.meta.lastUpdated }
.also { if (it.size > 1) onPurgeCarePlanWithAssociatedTask(it.subList(1, it.size), true) }

// TODO: Filter out the care_plans in the query before hand
private suspend fun onPurgeCarePlans() {
query<Patient>(ResourceType.Patient)
.filter { it.isNotEmpty() }
.collect { patients ->
val patientIdsReferenceParamFilterCriteria =
patients.map(Patient::logicalId).chunked(50) {
it.map<String, ReferenceParamFilterCriterion.() -> Unit> {
return@map { value = "${ResourceType.Patient.name}/$it" }
}
}
val carePlans = mutableListOf<CarePlan>()
patientIdsReferenceParamFilterCriteria.forEach {
fhirEngine
.search<CarePlan> { filter(CarePlan.SUBJECT, *it.toTypedArray()) }
.map { it.resource }
.let { carePlans.addAll(it) }
}

carePlans
.groupBy { it.subject.reference }
.values
.filter { it.size > 1 }
.forEach {
onPurgeInActiveCarePlanWithTasks(it)
onPurgeMultipleActiveCarePlanWithTasks(it)
}
query<CarePlan>(ResourceType.CarePlan) {
filter(
CarePlan.STATUS,
{ value = of(CarePlan.CarePlanStatus.REVOKED.toCode()) },
{ value = of(CarePlan.CarePlanStatus.ENTEREDINERROR.toCode()) },
{ value = of(CarePlan.CarePlanStatus.COMPLETED.toCode()) },
{ value = of(CarePlan.CarePlanStatus.UNKNOWN.toCode()) },
operation = Operation.OR,
)
}
.collect { carePlans ->
onPurgeCarePlanWithAssociatedTask(
carePlans.filter {
it.status != CarePlan.CarePlanStatus.ACTIVE &&
it.status != CarePlan.CarePlanStatus.ONHOLD &&
it.status != CarePlan.CarePlanStatus.DRAFT
},
)
}
}

private suspend fun onPurgeCarePlanWithAssociatedTask(
carePlans: List<CarePlan>,
isError: Boolean = false,
) {
if (isError) {
// TODO: Should we also update the task statuses?
carePlans.setStatusEnteredInError()
} else {
carePlans
.asSequence()
.flatMap { it.activity }
.mapNotNull { it.outcomeReference.firstOrNull() }
.map { it.reference.substringAfter("/") }
.toSet()
.map { Task().apply { id = it } }
.toList()
.purge()
carePlans.purge()
}
}

private suspend fun Iterable<CarePlan>.setStatusEnteredInError() {
val updateCarePlans = this.map { it.setStatus(CarePlan.CarePlanStatus.ENTEREDINERROR) }
fhirEngine.update(*updateCarePlans.toTypedArray())
private suspend fun onPurgeCarePlanWithAssociatedTask(carePlans: List<CarePlan>) {
carePlans
.asSequence()
.flatMap { it.activity }
.mapNotNull { it.outcomeReference.firstOrNull() }
.map { it.reference.substringAfter("/") }
.toSet()
.map { Task().apply { id = it } }
.toList()
.purge()
carePlans.purge()
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ import java.util.Date
import kotlin.math.max
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import org.hl7.fhir.r4.model.Appointment
import org.hl7.fhir.r4.model.CarePlan
import org.hl7.fhir.r4.model.CodeType
import org.hl7.fhir.r4.model.Coding
Expand Down Expand Up @@ -315,7 +314,9 @@ abstract class TracingRegisterDao(
val tasks = validTasks(patient)

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

var dueData = carePlans.firstOrNull()?.period?.end

if (dueData == null) {
tasks.minOfOrNull { task -> task.authoredOn }?.let { date -> dueData = date }
Expand All @@ -338,7 +339,7 @@ abstract class TracingRegisterDao(
showIdentifierInProfile = true,
healthStatus = patient.extractHealthStatusFromMeta(metaCodingSystemTag),
tasks = tasks,
services = patient.activeCarePlans(),
services = carePlans,
conditions = defaultRepository.activePatientConditions(patient.logicalId),
guardians = patient.guardians(),
practitioners = patient.practitioners(),
Expand All @@ -351,21 +352,6 @@ abstract class TracingRegisterDao(
}
}

private suspend fun getDueDate(patient: Patient): Date? {
val appointments =
fhirEngine
.fetch<Appointment> {
filter(Appointment.STATUS, { value = of(Appointment.AppointmentStatus.BOOKED.toCode()) })
filter(Appointment.ACTOR, { value = patient.referenceValue() })
sort(Appointment.DATE, Order.ASCENDING)
count = 1
from = 0
}
.map { it.resource }
val appointment = appointments.firstOrNull()
return appointment?.start
}

suspend fun Patient.activeCarePlans() =
defaultRepository
.searchResourceFor<CarePlan>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ class TracingRepository @Inject constructor(val fhirEngine: FhirEngine) {
}
}
} catch (e: Exception) {
e.printStackTrace()
Timber.e(e)
}
}

Expand Down Expand Up @@ -318,7 +318,7 @@ class TracingRepository @Inject constructor(val fhirEngine: FhirEngine) {
encounters.add(resource)
}
} catch (e: Exception) {
e.printStackTrace()
Timber.e(e)
}
}

Expand Down
Loading
Loading