From 91c25a5a331a75fec01aec0d8261695714ff69fb Mon Sep 17 00:00:00 2001 From: Christopher Seven Phiri Date: Thu, 29 Feb 2024 16:39:34 +0200 Subject: [PATCH 1/2] Update build.gradle --- android/quest/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/quest/build.gradle b/android/quest/build.gradle index 2a1d6a88aa..9e19bb8140 100644 --- a/android/quest/build.gradle +++ b/android/quest/build.gradle @@ -173,8 +173,8 @@ android { dimension "apps" applicationIdSuffix ".mwcore" versionNameSuffix "-mwcore" - versionCode 32 - versionName "0.1.22" + versionCode 33 + versionName "0.1.23" } mwcoreDev { dimension "apps" From 133a40f47bcac664b78fcedce2f456c5f02e985e Mon Sep 17 00:00:00 2001 From: Christopher Seven Phiri Date: Fri, 1 Mar 2024 10:30:46 +0200 Subject: [PATCH 2/2] Update tracing and appointments logic (#30) * update tracing and appointments logic * Update AppointmentRegisterDao.kt --- .../register/dao/AppointmentRegisterDao.kt | 71 ++++++++-------- .../register/dao/HomeTracingRegisterDao.kt | 7 +- .../register/dao/PhoneTracingRegisterDao.kt | 8 +- .../local/register/dao/TracingRegisterDao.kt | 26 ++++-- .../data/local/tracing/TracingRepository.kt | 82 ++++++------------- .../util/helper/TransformSupportServices.kt | 2 + .../fhircore/quest/ui/Components.kt | 4 +- .../quest/ui/StandardRegisterScreen.kt | 4 - .../fhircore/quest/ui/main/AppMainActivity.kt | 5 ++ .../fhircore/quest/ui/main/AppMainScreen.kt | 2 +- .../quest/ui/main/AppMainViewModel.kt | 4 +- .../patient/profile/PatientProfileScreen.kt | 2 +- .../shared/models/PatientProfileViewData.kt | 2 +- .../tracing/profile/TracingProfileScreen.kt | 27 ++++-- .../profile/TracingProfileViewModel.kt | 28 ++----- .../quest/util/extensions/TracingExtension.kt | 10 ++- 16 files changed, 141 insertions(+), 143 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/dao/AppointmentRegisterDao.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/dao/AppointmentRegisterDao.kt index 52ce4a89a2..5b6e3d841f 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/dao/AppointmentRegisterDao.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/dao/AppointmentRegisterDao.kt @@ -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 @@ -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 @@ -65,7 +69,10 @@ constructor( ) : RegisterDao { private val currentPractitioner by lazy { - sharedPreferencesHelper.read(key = LOGGED_IN_PRACTITIONER, decodeWithGson = true) + sharedPreferencesHelper.read( + key = SharedPreferenceKey.PRACTITIONER_ID.name, + defaultValue = null + ) } private fun Appointment.patientRef() = @@ -85,7 +92,6 @@ constructor( return fhirEngine .search { filter(Appointment.STATUS, { value = of(Appointment.AppointmentStatus.BOOKED.toCode()) }) - filter(Appointment.DATE, { value = of(DateTimeType.today()) }) } .map { it.resource } .count { @@ -116,6 +122,7 @@ constructor( page: Int = -1 ): List { filters as AppointmentRegisterFilter + val patientTypeFilterTag = applicationConfiguration().patientTypeFilterTagViaMetaCodingSystem val searchResults = fhirEngine.search { if (!loadAll) count = PaginationConstant.DEFAULT_PAGE_SIZE @@ -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, alternativeCoding).map< - // Coding, TokenParamFilterCriterion.() -> Unit> { c -> { value = of(c) } } - // } - // - // has(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, alternativeCoding).map< + Coding, TokenParamFilterCriterion.() -> Unit> { c -> { value = of(c) } } + } + + has(Appointment.PATIENT) { + filter(TokenClientParam("_tag"), *paramQueries.toTypedArray(), operation = Operation.OR) + } + } filters.reasonCode?.let { val codeableConcept = @@ -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)) @@ -195,7 +201,8 @@ constructor( it.hasStart() && patientAssignmentFilter && patientCategoryFilter && - appointmentReasonFilter + appointmentReasonFilter && + it.patientRef() != null } } diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/dao/HomeTracingRegisterDao.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/dao/HomeTracingRegisterDao.kt index b664baa7ed..2a5ba67e9a 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/dao/HomeTracingRegisterDao.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/dao/HomeTracingRegisterDao.kt @@ -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") + } } diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/dao/PhoneTracingRegisterDao.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/dao/PhoneTracingRegisterDao.kt index 2374aa8881..e1217a1c4c 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/dao/PhoneTracingRegisterDao.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/dao/PhoneTracingRegisterDao.kt @@ -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") + } } diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/dao/TracingRegisterDao.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/dao/TracingRegisterDao.kt index 82f03b252a..dde220f1e6 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/dao/TracingRegisterDao.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/dao/TracingRegisterDao.kt @@ -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 @@ -94,7 +94,10 @@ constructor( patient.extractOfficialIdentifier() ?: HivRegisterDao.ResourceValue.BLANK private val currentPractitioner by lazy { - sharedPreferencesHelper.read(key = LOGGED_IN_PRACTITIONER, decodeWithGson = true) + sharedPreferencesHelper.read( + key = SharedPreferenceKey.PRACTITIONER_ID.name, + defaultValue = null + ) } private val filtersForValidTask: BaseSearch.() -> Unit = { @@ -125,6 +128,7 @@ constructor( page: Int = -1 ): List>> { filters as TracingRegisterFilter + val filterFilter = applicationConfiguration().patientTypeFilterTagViaMetaCodingSystem val patients: List = fhirEngine .search { @@ -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() } @@ -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 } @@ -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 } } @@ -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 -> @@ -364,6 +372,7 @@ constructor( suspend fun Patient.practitioners(): List { 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) { @@ -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, diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/tracing/TracingRepository.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/tracing/TracingRepository.kt index 117126d82f..d028ee7344 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/tracing/TracingRepository.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/tracing/TracingRepository.kt @@ -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 @@ -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( @@ -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): 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() ) @@ -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(resourceId) } - .onSuccess { enc -> - lastAttempt = maxOf(lastAttempt, enc, nullsFirst(compareBy { it.period?.start?.time })) - } - .onFailure { it.printStackTrace() } - } - - val obs = - lastAttempt?.let { - fhirEngine.search { - 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 = @@ -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 diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/helper/TransformSupportServices.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/helper/TransformSupportServices.kt index 9d3af17422..a07aef06e9 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/helper/TransformSupportServices.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/helper/TransformSupportServices.kt @@ -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 @@ -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) } } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/Components.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/Components.kt index 2de1707c93..f5eabbee56 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/Components.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/Components.kt @@ -75,9 +75,7 @@ fun LocalExposedDropdownMenuBox( value = selectedItem.text(), onValueChange = { /* No-op */}, readOnly = true, - trailingIcon = { - TrailingIcon(expanded = isExpanded, onIconClick = { isExpanded = !isExpanded }) - }, + trailingIcon = { TrailingIcon(expanded = isExpanded) }, colors = ExposedDropdownMenuDefaults.outlinedTextFieldColors(textColor = Color.DarkGray), maxLines = 1, singleLine = true, diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/StandardRegisterScreen.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/StandardRegisterScreen.kt index 759d520726..c5b85c6a34 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/StandardRegisterScreen.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/StandardRegisterScreen.kt @@ -44,7 +44,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -69,11 +68,8 @@ fun PageRegisterScreen( registerViewModel: StandardRegisterViewModel, filterNavClickAction: () -> Unit ) { - - val context = LocalContext.current val searchTextState = registerViewModel.searchText.collectAsState() val searchText by remember { searchTextState } - val registerConfigs = remember { registerViewModel.registerViewConfiguration } val pagingItems: LazyPagingItems = registerViewModel.paginatedRegisterData.collectAsState().value.collectAsLazyPagingItems() diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/main/AppMainActivity.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/main/AppMainActivity.kt index f74ac82a0e..bfe3868f70 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/main/AppMainActivity.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/main/AppMainActivity.kt @@ -213,6 +213,11 @@ open class AppMainActivity : BaseMultiLanguageActivity(), OnSyncListener { data.getStringExtra(QuestionnaireActivity.QUESTIONNAIRE_ARG_FORM) ) } + it == "notify" -> { + appMainViewModel.onTaskComplete( + data.getStringExtra(QuestionnaireActivity.QUESTIONNAIRE_ARG_FORM) + ) + } } } } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/main/AppMainScreen.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/main/AppMainScreen.kt index 4049699411..787342f414 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/main/AppMainScreen.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/main/AppMainScreen.kt @@ -196,7 +196,7 @@ private fun AppMainNavigationGraph( route = "${it.route}${NavigationArg.routePathsOf(includeCommonArgs = true, NavigationArg.PATIENT_ID, NavigationArg.FAMILY_ID)}", arguments = commonNavArgs.plus(patientIdNavArgument()) - ) { TracingProfileScreen(navController = navController) } + ) { TracingProfileScreen(navController = navController, appViewModel = appMainViewModel) } MainNavigationScreen.PatientGuardians -> composable( route = diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/main/AppMainViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/main/AppMainViewModel.kt index 19a2657bcc..9ed4c50b3a 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/main/AppMainViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/main/AppMainViewModel.kt @@ -72,7 +72,7 @@ constructor( val appMainUiState: MutableState = mutableStateOf(appMainUiStateOf()) val refreshDataState: MutableState = mutableStateOf(0) - val taskId: MutableStateFlow = MutableStateFlow(null) + val completedTaskId: MutableStateFlow = MutableStateFlow(null) private val simpleDateFormat = SimpleDateFormat(SYNC_TIMESTAMP_OUTPUT_FORMAT, Locale.getDefault()) @@ -194,7 +194,7 @@ constructor( } fun onTaskComplete(id: String?) { - viewModelScope.launch { id?.let { taskId.emit(it) } } + viewModelScope.launch { id?.let { completedTaskId.emit(it) } } } companion object { diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/PatientProfileScreen.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/PatientProfileScreen.kt index ffc6be7cc4..4225dc80b8 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/PatientProfileScreen.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/PatientProfileScreen.kt @@ -87,7 +87,7 @@ fun PatientProfileScreen( val profileViewData by remember { profileViewDataState } var showOverflowMenu by remember { mutableStateOf(false) } val viewState = patientProfileViewModel.patientProfileUiState.value - val taskId by appMainViewModel.taskId.collectAsState() + val taskId by appMainViewModel.completedTaskId.collectAsState() val syncing by remember { patientProfileViewModel.isSyncing } val tasksId = profileViewData.tasks.map { it.actionFormId } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/models/PatientProfileViewData.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/models/PatientProfileViewData.kt index fbcacd61df..a2d1aaed2f 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/models/PatientProfileViewData.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/models/PatientProfileViewData.kt @@ -124,7 +124,7 @@ sealed class ProfileViewData( val hasFinishedAttempts: Boolean = currentAttempt.run { isHomeTracing?.let { - val maxAttempts = if (it) 3 else 2 + val maxAttempts = if (it) 2 else 3 if (this != null) { return@run this.numberOfAttempts >= maxAttempts } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/tracing/profile/TracingProfileScreen.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/tracing/profile/TracingProfileScreen.kt index 6de83493ca..e68d1ca467 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/tracing/profile/TracingProfileScreen.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/tracing/profile/TracingProfileScreen.kt @@ -47,6 +47,7 @@ import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.outlined.MoreVert import androidx.compose.material.icons.outlined.Refresh import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -76,6 +77,7 @@ import org.smartregister.fhircore.engine.util.annotation.ExcludeFromJacocoGenera import org.smartregister.fhircore.engine.util.extension.asDdMmYyyy import org.smartregister.fhircore.engine.util.extension.safeSubList import org.smartregister.fhircore.quest.R as R2 +import org.smartregister.fhircore.quest.ui.main.AppMainViewModel import org.smartregister.fhircore.quest.ui.shared.models.ProfileViewData import org.smartregister.fhircore.quest.ui.tracing.components.InfoBoxItem import org.smartregister.fhircore.quest.ui.tracing.components.OutlineCard @@ -84,8 +86,12 @@ import org.smartregister.fhircore.quest.ui.tracing.components.OutlineCard fun TracingProfileScreen( navController: NavHostController, modifier: Modifier = Modifier, - viewModel: TracingProfileViewModel = hiltViewModel() + viewModel: TracingProfileViewModel = hiltViewModel(), + appViewModel: AppMainViewModel = hiltViewModel() ) { + val taskId by appViewModel.completedTaskId.collectAsState() + + LaunchedEffect(taskId) { taskId?.let { viewModel.fetchTracingData() } } TracingProfilePage( navController, @@ -110,6 +116,12 @@ fun TracingProfilePage( val viewState = tracingProfileViewModel.patientTracingProfileUiState.value val syncing by remember { tracingProfileViewModel.isSyncing } + LaunchedEffect(profileViewData) { + if (profileViewData.logicalId.isNotBlank() && profileViewData.hasFinishedAttempts) { + onBackPress() + } + } + Scaffold( topBar = { TopAppBar( @@ -165,15 +177,17 @@ fun TracingProfilePage( }, bottomBar = { Box(contentAlignment = Alignment.Center, modifier = modifier.fillMaxWidth()) { + val hasFinishedAttempts = profileViewData.hasFinishedAttempts Button( colors = ButtonDefaults.buttonColors( backgroundColor = LoginButtonColor, LoginFieldBackgroundColor ), - enabled = !profileViewData.hasFinishedAttempts, + enabled = !hasFinishedAttempts, onClick = { - tracingProfileViewModel.onEvent(TracingProfileEvent.LoadOutComesForm(context)) + if (!hasFinishedAttempts) + tracingProfileViewModel.onEvent(TracingProfileEvent.LoadOutComesForm(context)) }, modifier = modifier.fillMaxWidth() ) { @@ -431,10 +445,9 @@ private fun TracingGuardianAddress( modifier = modifier.fillMaxWidth(), ) { Column(modifier = modifier.padding(horizontal = 4.dp)) { - TracingReasonItem( - title = stringResource(R2.string.guardian_relation), - value = guardian.relationshipFirstRep.codingFirstRep.display - ) + guardian.relationshipFirstRep?.codingFirstRep?.display?.let { + TracingReasonItem(title = stringResource(R2.string.guardian_relation), value = it) + } TracingReasonItem( title = stringResource(R2.string.guardian_phone_number, i + 1), value = guardian.telecomFirstRep.value diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/tracing/profile/TracingProfileViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/tracing/profile/TracingProfileViewModel.kt index d44fb15597..bbf15c4e94 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/tracing/profile/TracingProfileViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/tracing/profile/TracingProfileViewModel.kt @@ -21,8 +21,6 @@ import android.content.Intent import android.net.Uri import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -36,10 +34,7 @@ import kotlinx.coroutines.launch import org.hl7.fhir.r4.model.ResourceType import org.smartregister.fhircore.engine.appfeature.model.HealthModule import org.smartregister.fhircore.engine.configuration.ConfigurationRegistry -import org.smartregister.fhircore.engine.configuration.app.AppConfigClassification -import org.smartregister.fhircore.engine.configuration.app.ApplicationConfiguration import org.smartregister.fhircore.engine.data.local.register.AppRegisterRepository -import org.smartregister.fhircore.engine.domain.model.ProfileData import org.smartregister.fhircore.engine.sync.OnSyncListener import org.smartregister.fhircore.engine.sync.SyncBroadcaster import org.smartregister.fhircore.engine.ui.questionnaire.QuestionnaireActivity @@ -61,7 +56,7 @@ class TracingProfileViewModel constructor( savedStateHandle: SavedStateHandle, private val syncBroadcaster: SyncBroadcaster, - private val overflowMenuFactory: OverflowMenuFactory, + overflowMenuFactory: OverflowMenuFactory, val registerRepository: AppRegisterRepository, val configurationRegistry: ConfigurationRegistry, val profileViewDataMapper: ProfileViewDataMapper, @@ -73,7 +68,6 @@ constructor( savedStateHandle.get(NavigationArg.HEALTH_MODULE) ?: HealthModule.DEFAULT val patientId = savedStateHandle.get(NavigationArg.PATIENT_ID) ?: "" val familyId = savedStateHandle.get(NavigationArg.FAMILY_ID) - var patientTracingProfileUiState: MutableState = mutableStateOf( TracingProfileUiState( @@ -85,15 +79,6 @@ constructor( val patientProfileViewData: StateFlow get() = _patientProfileViewDataFlow.asStateFlow() - var patientProfileData: ProfileData? = null - - val applicationConfiguration: ApplicationConfiguration - get() = configurationRegistry.retrieveConfiguration(AppConfigClassification.APPLICATION) - - private val _showTracingOutcomes = MutableLiveData() - val showTracingOutcomes: LiveData - get() = _showTracingOutcomes - val isSyncing = mutableStateOf(false) init { @@ -103,7 +88,7 @@ constructor( when (state) { is SyncJobStatus.Finished, is SyncJobStatus.Failed -> { isSyncing.value = false - fetchTracingData() + // fetchTracingData() } is SyncJobStatus.Started -> { isSyncing.value = true @@ -123,7 +108,6 @@ constructor( fun fetchTracingData() { viewModelScope.launch { registerRepository.loadPatientProfileData(appFeatureName, healthModule, patientId)?.let { - patientProfileData = it _patientProfileViewDataFlow.value = profileViewDataMapper.transformInputToOutputModel(it) as ProfileViewData.TracingProfileData @@ -172,12 +156,13 @@ constructor( ) is TracingProfileEvent.LoadOutComesForm -> { profile.isHomeTracing?.let { isHomeTracing -> - QuestionnaireActivity.launchQuestionnaire( - event.context, + QuestionnaireActivity.launchQuestionnaireForResult( + event.context as Activity, if (isHomeTracing) "home-tracing-outcome" else "phone-tracing-outcome", clientIdentifier = patientId, questionnaireType = QuestionnaireType.EDIT, - populationResources = profile.populationResources + populationResources = profile.populationResources, + backReference = "notify" ) } } @@ -200,6 +185,7 @@ constructor( } fun reSync() { + fetchTracingData() syncBroadcaster.runSync() } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/util/extensions/TracingExtension.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/util/extensions/TracingExtension.kt index 1bc8217a5e..cefabbbc79 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/util/extensions/TracingExtension.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/util/extensions/TracingExtension.kt @@ -17,11 +17,17 @@ package org.smartregister.fhircore.quest.util.extensions import org.hl7.fhir.r4.model.Task +import org.smartregister.fhircore.engine.data.local.register.dao.HomeTracingRegisterDao +import org.smartregister.fhircore.engine.data.local.register.dao.PhoneTracingRegisterDao fun Task.isHomeTracingTask(): Boolean { - return this.meta.tag.firstOrNull { it.`is`("https://d-tree.org", "home-tracing") } !== null + return this.meta.tag.firstOrNull { + it.`is`(HomeTracingRegisterDao.taskCode.system, HomeTracingRegisterDao.taskCode.code) + } !== null } fun Task.isPhoneTracingTask(): Boolean { - return this.meta.tag.firstOrNull { it.`is`("https://d-tree.org", "phone-tracing") } !== null + return this.meta.tag.firstOrNull { + it.`is`(PhoneTracingRegisterDao.taskCode.system, PhoneTracingRegisterDao.taskCode.code) + } !== null }