From 9e2d5ee5c870460c09b900c9449b686b8e7f2a85 Mon Sep 17 00:00:00 2001 From: Christopher Seven Phiri Date: Wed, 31 Jul 2024 08:21:52 +0200 Subject: [PATCH 1/6] Show on UI if task is missing --- .../data/local/register/dao/HivRegisterDao.kt | 69 ++++++++++++------- .../engine/domain/model/CarePlanTask.kt | 24 +++++++ .../engine/domain/model/ProfileData.kt | 3 +- .../profile/PatientProfileViewModel.kt | 2 +- .../components/ProfileActionableItem.kt | 37 +++++++++- .../ui/shared/models/PatientProfileRowItem.kt | 1 + .../util/mappers/ProfileViewDataMapper.kt | 17 ++--- 7 files changed, 116 insertions(+), 37 deletions(-) create mode 100644 android/engine/src/main/java/org/smartregister/fhircore/engine/domain/model/CarePlanTask.kt diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/dao/HivRegisterDao.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/dao/HivRegisterDao.kt index 46f20e9b93..df3019d9fd 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/dao/HivRegisterDao.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/dao/HivRegisterDao.kt @@ -48,6 +48,7 @@ import org.smartregister.fhircore.engine.configuration.ConfigurationRegistry import org.smartregister.fhircore.engine.data.domain.Guardian import org.smartregister.fhircore.engine.data.domain.PregnancyStatus import org.smartregister.fhircore.engine.data.local.DefaultRepository +import org.smartregister.fhircore.engine.domain.model.CarePlanTask import org.smartregister.fhircore.engine.domain.model.HealthStatus import org.smartregister.fhircore.engine.domain.model.ProfileData import org.smartregister.fhircore.engine.domain.model.RegisterData @@ -245,7 +246,7 @@ constructor( fhirEngine.withTransaction { val patient = defaultRepository.loadResource(resourceId)!! val carePlan = patient.activeCarePlans(fhirEngine).firstOrNull() - + val (exists, activities) = fetchCarePlanActivities(carePlan) profileData = ProfileData.HivProfileData( logicalId = patient.logicalId, @@ -265,7 +266,8 @@ constructor( showIdentifierInProfile = true, currentCarePlan = carePlan, healthStatus = patient.extractHealthStatusFromMeta(patientTypeMetaTagCodingSystem), - tasks = fetchCarePlanActivities(carePlan), + tasks = activities, + hasMissingTasks = exists, conditions = defaultRepository.activePatientConditions(patient.logicalId), otherPatients = patient.otherChildren(), guardians = patient.guardians(), @@ -512,33 +514,48 @@ constructor( private suspend fun fetchCarePlanActivities( carePlan: CarePlan?, - ): List { - if (carePlan == null) return emptyList() - val activityOnList = mutableMapOf() - val tasksToFetch = mutableListOf() - for (planActivity in carePlan.activity) { - if (!planActivity.shouldShowOnProfile()) { - continue - } - val taskId = planActivity.outcomeReference.firstOrNull()?.extractId() - if (taskId != null) { - tasksToFetch.add(taskId) - activityOnList[taskId] = planActivity + ): Pair> { + if (carePlan == null) return Pair(false, emptyList()) + + val activityOnList = mutableMapOf() + val tasksToFetch = + carePlan.activity.mapNotNull { planActivity -> + if (planActivity.shouldShowOnProfile()) { + planActivity.outcomeReference.firstOrNull()?.extractId()?.also { taskId -> + activityOnList[taskId] = CarePlanTask(planActivity, false) + } + } else { + null + } } - } - if (tasksToFetch.isNotEmpty()) { - val tasks = fhirEngine.getResourcesByIds(tasksToFetch) - tasks.forEach { task -> - val planActivity: CarePlan.CarePlanActivityComponent? = activityOnList[task.logicalId] - if (planActivity != null) { - planActivity.detail?.status = task.taskStatusToCarePlanActivityStatus() - activityOnList[task.logicalId] = planActivity + + var hasMissingTask = false + val items: List = + if (tasksToFetch.isNotEmpty()) { + val tasks = fhirEngine.getResourcesByIds(tasksToFetch).associateBy { it.logicalId } + activityOnList.map { (taskId, carePlanTask) -> + tasks[taskId]?.let { task -> + val updatedTask = + carePlanTask.task.apply { detail?.status = task.taskStatusToCarePlanActivityStatus() } + carePlanTask.copy(task = updatedTask, taskExists = true) + } + ?: run { + if (carePlanTask.task.detail?.status != CarePlan.CarePlanActivityStatus.SCHEDULED) { + hasMissingTask = true + } + carePlanTask.copy(taskExists = false) + } } + } else { + activityOnList.values.toList() } - } - return activityOnList.values.sortedWith( - compareBy(nullsLast()) { it.detail?.code?.text?.toBigIntegerOrNull() }, - ) + + val sortedItems = + items.sortedWith( + compareBy(nullsLast()) { it.task.detail?.code?.text?.toBigIntegerOrNull() }, + ) + + return Pair(hasMissingTask, sortedItems) } object ResourceValue { diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/domain/model/CarePlanTask.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/domain/model/CarePlanTask.kt new file mode 100644 index 0000000000..7f6d27e6d6 --- /dev/null +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/domain/model/CarePlanTask.kt @@ -0,0 +1,24 @@ +/* + * 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.domain.model + +import org.hl7.fhir.r4.model.CarePlan + +data class CarePlanTask( + val task: CarePlan.CarePlanActivityComponent, + val taskExists: Boolean, +) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/domain/model/ProfileData.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/domain/model/ProfileData.kt index 474acee2b4..cb3846ab5a 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/domain/model/ProfileData.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/domain/model/ProfileData.kt @@ -96,7 +96,8 @@ sealed class ProfileData(open val logicalId: String, open val name: String) { val addressDistrict: String = "", val addressTracingCatchment: String = "", val addressPhysicalLocator: String = "", - val tasks: List = listOf(), + val hasMissingTasks: Boolean = false, + val tasks: List = listOf(), val chwAssigned: Reference, val healthStatus: HealthStatus, val phoneContacts: List = listOf(), diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/PatientProfileViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/PatientProfileViewModel.kt index 746034a5b5..b4f903e216 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/PatientProfileViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/PatientProfileViewModel.kt @@ -173,7 +173,7 @@ constructor( hivPatientProfileData.copy( tasks = hivPatientProfileData.tasks.filter { - it.isGuardianVisit(applicationConfiguration.taskFilterTagViaMetaCodingSystem) + it.task.isGuardianVisit(applicationConfiguration.taskFilterTagViaMetaCodingSystem) }, ) _patientProfileViewDataFlow.value = diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/components/ProfileActionableItem.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/components/ProfileActionableItem.kt index 4d8e82d2e9..fe0833fcb2 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/components/ProfileActionableItem.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/components/ProfileActionableItem.kt @@ -30,9 +30,11 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.ButtonDefaults import androidx.compose.material.Divider import androidx.compose.material.Icon +import androidx.compose.material.MaterialTheme import androidx.compose.material.OutlinedButton import androidx.compose.material.Text import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Error import androidx.compose.material.icons.outlined.ChevronRight import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -69,7 +71,26 @@ fun ProfileActionableItem( verticalAlignment = Alignment.CenterVertically, ) { if (patientProfileRowItem.title.isEmpty() && patientProfileRowItem.subtitle.isEmpty()) { - ActionButton(patientProfileRowItem, modifier = modifier.fillMaxWidth(1f), onActionClick) + Row(verticalAlignment = Alignment.CenterVertically) { + if (!patientProfileRowItem.taskExists) { + Box( + modifier + .padding(end = 8.dp) + .clip(RoundedCornerShape(6.dp)) + .background(MaterialTheme.colors.error) + .padding(8.dp), + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(2.dp) + ) { + Icon(Icons.Filled.Error, contentDescription = "", tint = MaterialTheme.colors.onError) + Text(text = "Missing Task", color = MaterialTheme.colors.onError) + } + } + } + ActionButton(patientProfileRowItem, modifier = modifier.fillMaxWidth(1f), onActionClick) + } } else { Row(verticalAlignment = Alignment.CenterVertically) { if ( @@ -258,6 +279,20 @@ fun ProfileActionableItemForVisitPreview() { ), onActionClick = { _, _ -> }, ) + Divider() + ProfileActionableItem( + PatientProfileRowItem( + id = "3", + title = "", + titleIcon = R.drawable.ic_pregnant, + subtitle = "", + profileViewSection = PatientProfileViewSection.TASKS, + actionButtonColor = OverdueColor, + actionButtonText = "Malaria medicine", + taskExists = false, + ), + onActionClick = { _, _ -> }, + ) } } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/models/PatientProfileRowItem.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/models/PatientProfileRowItem.kt index ddb91c3d19..345e2c0cf1 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/models/PatientProfileRowItem.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/models/PatientProfileRowItem.kt @@ -38,4 +38,5 @@ data class PatientProfileRowItem( val actionButtonText: String? = null, val showAngleRightIcon: Boolean = false, val showDot: Boolean = false, + val taskExists: Boolean = true, ) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/util/mappers/ProfileViewDataMapper.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/util/mappers/ProfileViewDataMapper.kt index 8fe46ea4ea..f549957aaf 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/util/mappers/ProfileViewDataMapper.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/util/mappers/ProfileViewDataMapper.kt @@ -97,23 +97,24 @@ class ProfileViewDataMapper @Inject constructor(@ApplicationContext val context: tasks = inputModel.tasks.map { PatientProfileRowItem( - id = it.outcomeReference.first().extractId(), - actionFormId = if (it.canBeCompleted()) it.getQuestionnaire() else null, + id = it.task.outcomeReference.first().extractId(), + actionFormId = if (it.task.canBeCompleted()) it.task.getQuestionnaire() else null, title = "", // it.description, subtitle = "", // context.getString(R.string.due_on, // it.executionPeriod.start.makeItReadable()), + taskExists = it.taskExists, profileViewSection = PatientProfileViewSection.TASKS, actionButtonIcon = - if (it.detail.status == CarePlan.CarePlanActivityStatus.COMPLETED) { + if (it.task.detail.status == CarePlan.CarePlanActivityStatus.COMPLETED) { Icons.Filled.Check } else Icons.Filled.Add, actionIconColor = - if (it.detail.status == CarePlan.CarePlanActivityStatus.COMPLETED) { + if (it.task.detail.status == CarePlan.CarePlanActivityStatus.COMPLETED) { SuccessColor - } else it.detail.status.retrieveColorCode(), - actionButtonColor = it.detail.status.retrieveColorCode(), - actionButtonText = it.getQuestionnaireName(), - subtitleStatus = it.detail.status.name, + } else it.task.detail.status.retrieveColorCode(), + actionButtonColor = it.task.detail.status.retrieveColorCode(), + actionButtonText = it.task.getQuestionnaireName(), + subtitleStatus = it.task.detail.status.name, ) }, practitioners = inputModel.practitioners, From 19bfd677ec5aaade139a636560bd08b4a89496da Mon Sep 17 00:00:00 2001 From: Christopher Seven Phiri Date: Wed, 31 Jul 2024 10:24:11 +0200 Subject: [PATCH 2/6] create patient fix screen --- .../data/local/register/dao/HivRegisterDao.kt | 1 + .../quest/navigation/MainNavigationScreen.kt | 7 + .../fhircore/quest/ui/main/AppMainScreen.kt | 12 +- .../quest/ui/patient/fix/FixPatientScreen.kt | 138 ++++++++++++++++++ .../quest/ui/patient/fix/FixPatientState.kt | 23 +++ .../ui/patient/fix/FixPatientViewModel.kt | 69 +++++++++ .../ui/patient/profile/PatientProfileEvent.kt | 2 + .../patient/profile/PatientProfileScreen.kt | 5 + .../profile/PatientProfileViewModel.kt | 14 ++ .../components/ProfileActionableItem.kt | 2 +- .../profile/components/ProfileErrorCard.kt | 65 +++++++++ .../quest/ui/shared/models/ProfileViewData.kt | 1 + .../util/mappers/ProfileViewDataMapper.kt | 1 + android/quest/src/main/res/values/strings.xml | 1 + 14 files changed, 338 insertions(+), 3 deletions(-) create mode 100644 android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/fix/FixPatientScreen.kt create mode 100644 android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/fix/FixPatientState.kt create mode 100644 android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/fix/FixPatientViewModel.kt create mode 100644 android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/components/ProfileErrorCard.kt diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/dao/HivRegisterDao.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/dao/HivRegisterDao.kt index df3019d9fd..8141495ec1 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/dao/HivRegisterDao.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/local/register/dao/HivRegisterDao.kt @@ -534,6 +534,7 @@ constructor( if (tasksToFetch.isNotEmpty()) { val tasks = fhirEngine.getResourcesByIds(tasksToFetch).associateBy { it.logicalId } activityOnList.map { (taskId, carePlanTask) -> + println(taskId) tasks[taskId]?.let { task -> val updatedTask = carePlanTask.task.apply { detail?.status = task.taskStatusToCarePlanActivityStatus() } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/navigation/MainNavigationScreen.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/navigation/MainNavigationScreen.kt index ec637ff748..bf55e32e40 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/navigation/MainNavigationScreen.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/navigation/MainNavigationScreen.kt @@ -77,6 +77,12 @@ sealed class MainNavigationScreen( route = "patientProfileRoute", ) + data object FixPatientProfile : + MainNavigationScreen( + titleResource = R.string.fix_patient, + route = "fixPatientProfileRoute", + ) + data object TracingProfile : MainNavigationScreen( titleResource = org.smartregister.fhircore.engine.R.string.profile, @@ -106,6 +112,7 @@ sealed class MainNavigationScreen( Reports, Settings, PatientProfile, + FixPatientProfile, PatientGuardians, FamilyProfile, ViewChildContacts, 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 ee0dac75ca..58fb260181 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 @@ -25,9 +25,7 @@ import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.Scaffold import androidx.compose.material.rememberScaffoldState import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel @@ -49,6 +47,8 @@ import org.smartregister.fhircore.quest.ui.counters.CountersScreen import org.smartregister.fhircore.quest.ui.family.profile.FamilyProfileScreen import org.smartregister.fhircore.quest.ui.insights.InsightsScreen import org.smartregister.fhircore.quest.ui.main.components.AppDrawer +import org.smartregister.fhircore.quest.ui.patient.fix.FixPatientScreen +import org.smartregister.fhircore.quest.ui.patient.fix.FixPatientViewModel import org.smartregister.fhircore.quest.ui.patient.profile.PatientProfileScreen import org.smartregister.fhircore.quest.ui.patient.profile.childcontact.ChildContactsProfileScreen import org.smartregister.fhircore.quest.ui.patient.profile.guardians.GuardianRelatedPersonProfileScreen @@ -199,6 +199,14 @@ private fun AppMainNavigationGraph( ) { PatientProfileScreen(navController = navController, appMainViewModel = appMainViewModel) } + MainNavigationScreen.FixPatientProfile -> + composable( + route = + "${it.route}${NavigationArg.routePathsOf(includeCommonArgs = true, NavigationArg.PATIENT_ID, FixPatientViewModel.NAVIGATION_ARG_START)}", + arguments = commonNavArgs.plus(patientIdNavArgument()), + ) { + FixPatientScreen(navController = navController, appMainViewModel = appMainViewModel) + } MainNavigationScreen.TracingProfile -> composable( route = diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/fix/FixPatientScreen.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/fix/FixPatientScreen.kt new file mode 100644 index 0000000000..33852e2244 --- /dev/null +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/fix/FixPatientScreen.kt @@ -0,0 +1,138 @@ +/* + * 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.quest.ui.patient.fix + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Button +import androidx.compose.material.CircularProgressIndicator +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Scaffold +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.navigation.NavHostController +import org.smartregister.fhircore.engine.domain.util.DataLoadState +import org.smartregister.fhircore.quest.R +import org.smartregister.fhircore.quest.ui.main.AppMainViewModel + +@Composable +fun FixPatientScreen( + viewModel: FixPatientViewModel = hiltViewModel(), + navController: NavHostController, + appMainViewModel: AppMainViewModel, +) { + val state by viewModel.screenState.collectAsState() + val fixState by viewModel.fixState.collectAsState() + + Scaffold( + topBar = { + Column( + modifier = Modifier.fillMaxWidth().background(MaterialTheme.colors.primary), + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.padding(vertical = 8.dp), + ) { + IconButton(onClick = { navController.popBackStack() }) { + Icon( + Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = "Back", + tint = Color.White, + ) + } + Text( + text = stringResource(id = R.string.fix_patient), + fontSize = 20.sp, + color = Color.White, + modifier = Modifier.weight(1f), + ) + } + } + }, + ) { innerPadding -> + Box(modifier = Modifier.padding(innerPadding)) { + if (state is FixPatientState.ActionStart) { + PatientFixActionStartContainer(fixState, { navController.navigateUp() }) { + viewModel.startFix() + } + } else { + PatientFixAllActionContainer() + } + } + } +} + +@Composable fun PatientFixAllActionContainer() {} + +@Composable +fun PatientFixActionStartContainer( + state: DataLoadState, + close: () -> Unit, + retry: () -> Unit +) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + modifier = Modifier.fillMaxSize(), + ) { + when (state) { + is DataLoadState.Error -> + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + ) { + Text(text = "Something went wrong while fetching data..") + Button(onClick = retry) { Text(text = "Retry") } + } + is DataLoadState.Success -> { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + ) { + Text(text = "Completed!") + Button(onClick = close) { Text(text = "Close") } + } + } + else -> { + Text( + text = "Fixing Patient", + style = MaterialTheme.typography.h4.copy(color = Color.Gray), + ) + CircularProgressIndicator() + } + } + } +} diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/fix/FixPatientState.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/fix/FixPatientState.kt new file mode 100644 index 0000000000..1a6ed58479 --- /dev/null +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/fix/FixPatientState.kt @@ -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.quest.ui.patient.fix + +sealed class FixPatientState { + data object AllActions : FixPatientState() + + data object ActionStart : FixPatientState() +} diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/fix/FixPatientViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/fix/FixPatientViewModel.kt new file mode 100644 index 0000000000..bdd2056a27 --- /dev/null +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/fix/FixPatientViewModel.kt @@ -0,0 +1,69 @@ +/* + * 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.quest.ui.patient.fix + +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.launch +import org.smartregister.fhircore.engine.domain.util.DataLoadState +import org.smartregister.fhircore.quest.navigation.NavigationArg +import timber.log.Timber + +@HiltViewModel +class FixPatientViewModel @Inject constructor(savedStateHandle: SavedStateHandle) : ViewModel() { + val patientId = savedStateHandle.get(NavigationArg.PATIENT_ID) ?: "" + private val startState = + savedStateHandle.get(NAVIGATION_ARG_START)?.let { FixStartState.valueOf(it) } + ?: FixStartState.All + val screenState = MutableStateFlow(FixPatientState.AllActions) + val fixState = MutableStateFlow>(DataLoadState.Idle) + + init { + viewModelScope.launch { + if (startState == FixStartState.StartFix) { + screenState.emit(FixPatientState.ActionStart) + startFix() + } else { + screenState.emit(FixPatientState.AllActions) + } + } + } + + fun startFix() { + viewModelScope.launch { + try { + fixState.emit(DataLoadState.Loading) + } catch (e: Exception) { + Timber.e(e) + fixState.value = DataLoadState.Error(e) + } + } + } + + companion object { + const val NAVIGATION_ARG_START = "fix_start_state" + } +} + +enum class FixStartState { + All, + StartFix, +} diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/PatientProfileEvent.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/PatientProfileEvent.kt index 332e589cd7..4ac3319296 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/PatientProfileEvent.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/PatientProfileEvent.kt @@ -41,4 +41,6 @@ sealed class PatientProfileEvent { data class OpenChildProfile(val patientId: String, val navController: NavHostController) : PatientProfileEvent() + + data class OpenPatientFixer(val navController: NavHostController) : PatientProfileEvent() } 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 b6dfed7b60..65184bdd17 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 @@ -77,6 +77,7 @@ import org.smartregister.fhircore.quest.ui.main.AppMainViewModel import org.smartregister.fhircore.quest.ui.patient.profile.components.PersonalData import org.smartregister.fhircore.quest.ui.patient.profile.components.ProfileActionableItem import org.smartregister.fhircore.quest.ui.patient.profile.components.ProfileCard +import org.smartregister.fhircore.quest.ui.patient.profile.components.ProfileErrorCard import org.smartregister.fhircore.quest.ui.shared.models.PatientProfileViewSection @Composable @@ -204,6 +205,10 @@ fun PatientProfileScreen( PersonalData(profileViewData) } + ProfileErrorCard(profileViewData) { + patientProfileViewModel.onEvent(PatientProfileEvent.OpenPatientFixer(navController)) + } + // Patient tasks: List of tasks for the patients if (profileViewData.tasks.isNotEmpty()) { val appointmentDate = profileViewData.currentCarePlan?.period?.end diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/PatientProfileViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/PatientProfileViewModel.kt index b4f903e216..3a30568fbd 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/PatientProfileViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/PatientProfileViewModel.kt @@ -60,6 +60,8 @@ import org.smartregister.fhircore.quest.navigation.NavigationArg import org.smartregister.fhircore.quest.navigation.OverflowMenuFactory import org.smartregister.fhircore.quest.navigation.OverflowMenuHost import org.smartregister.fhircore.quest.ui.family.remove.member.RemoveFamilyMemberQuestionnaireActivity +import org.smartregister.fhircore.quest.ui.patient.fix.FixPatientViewModel +import org.smartregister.fhircore.quest.ui.patient.fix.FixStartState import org.smartregister.fhircore.quest.ui.patient.profile.childcontact.ChildContactPagingSource import org.smartregister.fhircore.quest.ui.patient.profile.tranfer.TransferOutActivity import org.smartregister.fhircore.quest.ui.shared.models.ProfileViewData @@ -356,6 +358,18 @@ constructor( route = MainNavigationScreen.PatientProfile.route + urlParams, ) } + is PatientProfileEvent.OpenPatientFixer -> { + val urlParams = + NavigationArg.bindArgumentsOf( + Pair(NavigationArg.FEATURE, AppFeature.PatientManagement.name), + Pair(NavigationArg.HEALTH_MODULE, healthModule), + Pair(NavigationArg.PATIENT_ID, patientId), + Pair(FixPatientViewModel.NAVIGATION_ARG_START, FixStartState.StartFix.name), + ) + event.navController.navigate( + route = MainNavigationScreen.FixPatientProfile.route + urlParams, + ) + } } } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/components/ProfileActionableItem.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/components/ProfileActionableItem.kt index fe0833fcb2..e4750f150d 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/components/ProfileActionableItem.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/components/ProfileActionableItem.kt @@ -82,7 +82,7 @@ fun ProfileActionableItem( ) { Row( verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(2.dp) + horizontalArrangement = Arrangement.spacedBy(2.dp), ) { Icon(Icons.Filled.Error, contentDescription = "", tint = MaterialTheme.colors.onError) Text(text = "Missing Task", color = MaterialTheme.colors.onError) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/components/ProfileErrorCard.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/components/ProfileErrorCard.kt new file mode 100644 index 0000000000..c74fc7d113 --- /dev/null +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/components/ProfileErrorCard.kt @@ -0,0 +1,65 @@ +/* + * 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.quest.ui.patient.profile.components + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Button +import androidx.compose.material.Card +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import org.smartregister.fhircore.quest.ui.shared.models.ProfileViewData + +@Composable +fun ProfileErrorCard( + profileViewData: ProfileViewData.PatientProfileViewData, + navigate: () -> Unit, +) { + if (profileViewData.hasMissingTasks) { + Card( + backgroundColor = MaterialTheme.colors.error, + modifier = Modifier.fillMaxWidth().padding(8.dp), + ) { + Column(verticalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.padding(8.dp)) { + Text(text = "Error", style = MaterialTheme.typography.h6) + Text(text = "Seems like this patient has some errors") + Button(onClick = navigate) { Text(text = "Fix patient") } + } + } + } +} + +@Preview +@Composable +private fun ProfileErrorCardPreview() { + Box(Modifier.padding(16.dp)) { + ProfileErrorCard( + profileViewData = + ProfileViewData.PatientProfileViewData( + hasMissingTasks = true, + ), + navigate = {}, + ) + } +} diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/models/ProfileViewData.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/models/ProfileViewData.kt index 4ac5b33442..38433300a0 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/models/ProfileViewData.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/models/ProfileViewData.kt @@ -46,6 +46,7 @@ sealed class ProfileViewData( val age: String = "", val dob: String = "", val showListsHighlights: Boolean = true, + val hasMissingTasks: Boolean = false, val tasks: List = emptyList(), val forms: List = emptyList(), val medicalHistoryData: List = emptyList(), diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/util/mappers/ProfileViewDataMapper.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/util/mappers/ProfileViewDataMapper.kt index f549957aaf..ebec1ca9a1 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/util/mappers/ProfileViewDataMapper.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/util/mappers/ProfileViewDataMapper.kt @@ -94,6 +94,7 @@ class ProfileViewDataMapper @Inject constructor(@ApplicationContext val context: guardians = inputModel.guardians, currentCarePlan = inputModel.currentCarePlan, visitNumber = inputModel.currentCarePlan?.extractVisitNumber(), + hasMissingTasks = inputModel.hasMissingTasks, tasks = inputModel.tasks.map { PatientProfileRowItem( diff --git a/android/quest/src/main/res/values/strings.xml b/android/quest/src/main/res/values/strings.xml index caa8f98b81..4788da6523 100644 --- a/android/quest/src/main/res/values/strings.xml +++ b/android/quest/src/main/res/values/strings.xml @@ -146,4 +146,5 @@ Appointments Insights RAM Available + Fix Patient From 68cedc961879a4200343db613df4192d64d2d157 Mon Sep 17 00:00:00 2001 From: Christopher Seven Phiri Date: Wed, 7 Aug 2024 11:22:11 +0200 Subject: [PATCH 3/6] Can fetch Task From server and update --- android/engine/build.gradle.kts | 2 + .../fhir/resource/ResourceFixerService.kt | 211 ++++++++++++++++++ .../fhircore/engine/di/NetworkModule.kt | 15 ++ .../util/extension/CarePlanExtension.kt | 13 ++ .../fhircore/quest/ui/main/AppMainScreen.kt | 2 +- .../quest/ui/patient/fix/FixPatientScreen.kt | 9 +- .../ui/patient/fix/FixPatientViewModel.kt | 18 +- .../profile/PatientProfileViewModel.kt | 25 ++- 8 files changed, 282 insertions(+), 13 deletions(-) create mode 100644 android/engine/src/main/java/org/smartregister/fhircore/engine/data/remote/fhir/resource/ResourceFixerService.kt diff --git a/android/engine/build.gradle.kts b/android/engine/build.gradle.kts index 19b9ade052..532a607070 100644 --- a/android/engine/build.gradle.kts +++ b/android/engine/build.gradle.kts @@ -212,6 +212,8 @@ dependencies { api("com.squareup.retrofit2:retrofit-mock:$retrofitVersion") api("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0") + implementation("ca.uhn.hapi.fhir:hapi-fhir-client-okhttp:${Dependencies.Versions.hapiFhir}") + val okhttpVersion = "4.12.0" api("com.squareup.okhttp3:okhttp:$okhttpVersion") api("com.squareup.okhttp3:logging-interceptor:$okhttpVersion") diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/remote/fhir/resource/ResourceFixerService.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/remote/fhir/resource/ResourceFixerService.kt new file mode 100644 index 0000000000..36e3a53770 --- /dev/null +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/remote/fhir/resource/ResourceFixerService.kt @@ -0,0 +1,211 @@ +/* + * 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.data.remote.fhir.resource + +import ca.uhn.fhir.rest.client.api.IGenericClient +import com.google.android.fhir.FhirEngine +import com.google.android.fhir.datacapture.extensions.logicalId +import com.google.android.fhir.get +import java.util.Date +import javax.inject.Inject +import org.hl7.fhir.r4.model.Bundle +import org.hl7.fhir.r4.model.CarePlan +import org.hl7.fhir.r4.model.Coding +import org.hl7.fhir.r4.model.Meta +import org.hl7.fhir.r4.model.Period +import org.hl7.fhir.r4.model.Reference +import org.hl7.fhir.r4.model.Resource +import org.hl7.fhir.r4.model.ResourceType +import org.hl7.fhir.r4.model.Task +import org.smartregister.fhircore.engine.util.SystemConstants.CARE_PLAN_REFERENCE_SYSTEM +import org.smartregister.fhircore.engine.util.SystemConstants.QUESTIONNAIRE_REFERENCE_SYSTEM +import org.smartregister.fhircore.engine.util.SystemConstants.RESOURCE_CREATED_ON_TAG_SYSTEM +import org.smartregister.fhircore.engine.util.extension.extractId +import org.smartregister.fhircore.engine.util.extension.generateCreatedOn +import org.smartregister.fhircore.engine.util.extension.getResourcesByIds +import org.smartregister.fhircore.engine.util.extension.shouldShowOnProfile +import org.smartregister.fhircore.engine.util.extension.toTaskStatus +import timber.log.Timber + +class ResourceFixerService +@Inject +constructor( + private val fhirClient: IGenericClient, + private val fhirEngine: FhirEngine, +) { + suspend fun fixCurrentCarePlan(patientId: String, carePlanId: String) { + val carePlan: CarePlan = fhirEngine.get(carePlanId) + val tasks = getMissingTasks(carePlan) + if (tasks.isEmpty()) { + return + } + Timber.i("Found missing tasks: ${tasks.size}") + handleMissingTasks(patientId, carePlan, tasks) + return + } + + private suspend fun getMissingTasks(carePlan: CarePlan): List { + val activityOnList = mutableMapOf() + val missingTasks = mutableListOf() + val tasksToFetch = + carePlan.activity.mapNotNull { planActivity -> + if (planActivity.shouldShowOnProfile()) { + planActivity.outcomeReference.firstOrNull()?.extractId()?.also { taskId -> + activityOnList[taskId] = planActivity + } + } else { + null + } + } + val tasks = fhirEngine.getResourcesByIds(tasksToFetch).associateBy { it.logicalId } + activityOnList.forEach { (taskId, activity) -> + if (activity.detail?.status != CarePlan.CarePlanActivityStatus.SCHEDULED) { + if (!tasks.containsKey(taskId)) { + missingTasks.add( + TaskHolder( + taskId = taskId, + carePlanActivityStatus = activity.detail.status, + taskDescription = activity.detail.description, + questId = activity.detail.code.coding.firstOrNull()?.code, + ), + ) + } + } + } + return missingTasks + } + + private suspend fun handleMissingTasks( + patientId: String, + carePlan: CarePlan, + tasks: List, + ) { + val bundle = Bundle() + bundle.setType(Bundle.BundleType.TRANSACTION) + bundle.entry.addAll( + tasks.map { task -> + Bundle.BundleEntryComponent().apply { + request = + Bundle.BundleEntryRequestComponent().apply { + method = Bundle.HTTPVerb.GET + url = "${ResourceType.Task.name}/${task.taskId}" + } + } + }, + ) + + val tasksToRecreate = mutableListOf() + val existingTasks = mutableListOf() + + val resBundle = fhirClient.transaction().withBundle(bundle).execute() + for ((index, entry) in resBundle.entry.withIndex()) { + if (entry.hasResource()) { + existingTasks.add(entry.resource as Task) + } else { + tasksToRecreate.add(tasks[index]) + } + } + + val resourceToSave = mutableListOf() + resourceToSave.addAll(existingTasks) + if (tasksToRecreate.isNotEmpty()) { + resourceToSave.addAll( + tasksToRecreate.map { + createGenericTask( + taskId = it.taskId, + patientId = patientId, + taskDescription = it.taskDescription, + questId = it.questId ?: "", + carePlan = carePlan, + taskStatus = it.carePlanActivityStatus.toTaskStatus(), + ) + }, + ) + } + Timber.e("Task on server: ${existingTasks.size}, Tasks to recreate: ${tasksToRecreate.size}") + fhirEngine.create(*resourceToSave.toTypedArray()) + } + + private fun createGenericTask( + taskId: String, + patientId: String, + taskDescription: String, + questId: String, + carePlan: CarePlan, + taskStatus: Task.TaskStatus, + ): Task { + val patientReference = Reference().apply { reference = "Patient/$patientId" } + + val questRef = Reference().apply { reference = questId } + + val period = + Period().apply { + start = Date() + end = Date() + } + + val task = + Task().apply { + id = taskId + status = taskStatus + intent = Task.TaskIntent.PLAN + priority = Task.TaskPriority.ROUTINE + description = taskDescription + authoredOn = Date() + lastModified = Date() + `for` = patientReference + executionPeriod = period + requester = carePlan.author + owner = carePlan.author + reasonReference = questRef + } + + val meta = + Meta().apply { + tag = + carePlan.meta.tag + .filter { + it.system != RESOURCE_CREATED_ON_TAG_SYSTEM && it.system != CARE_PLAN_REFERENCE_SYSTEM + } + .toMutableList() + tag.add( + Coding().apply { + system = CARE_PLAN_REFERENCE_SYSTEM + code = "CarePlan/${carePlan.id}" + display = carePlan.title + }, + ) + tag.add( + Coding().apply { + system = QUESTIONNAIRE_REFERENCE_SYSTEM + code = questId + }, + ) + } + + task.meta = meta + task.generateCreatedOn() + return task + } +} + +private data class TaskHolder( + val taskId: String, + val taskDescription: String, + val questId: String?, + val carePlanActivityStatus: CarePlan.CarePlanActivityStatus, +) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/di/NetworkModule.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/di/NetworkModule.kt index 13712c20c2..20e5208fb7 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/di/NetworkModule.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/di/NetworkModule.kt @@ -17,7 +17,9 @@ package org.smartregister.fhircore.engine.di import ca.uhn.fhir.context.FhirContext +import ca.uhn.fhir.okhttp.client.OkHttpRestfulClientFactory import ca.uhn.fhir.parser.IParser +import ca.uhn.fhir.rest.client.api.IGenericClient import com.google.gson.Gson import com.google.gson.GsonBuilder import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory @@ -166,6 +168,19 @@ class NetworkModule { fun provideFhirResourceService(@RegularRetrofit retrofit: Retrofit): FhirResourceService = retrofit.create(FhirResourceService::class.java) + @Provides + fun providesGenericFhirClient( + @WithAuthorizationOkHttpClientQualifier okHttpClient: OkHttpClient, + configService: ConfigService, + ): IGenericClient { + val factory = OkHttpRestfulClientFactory() + val ctx = FhirContext.forR4() + factory.fhirContext = ctx + factory.setHttpClient(okHttpClient) + ctx.restfulClientFactory = factory + return ctx.newRestfulGenericClient(configService.provideAuthConfiguration().fhirServerBaseUrl) + } + companion object { const val TIMEOUT_DURATION = 120L const val AUTHORIZATION = "Authorization" diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/CarePlanExtension.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/CarePlanExtension.kt index 8d54de656c..b9ad510b23 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/CarePlanExtension.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/CarePlanExtension.kt @@ -94,3 +94,16 @@ fun CarePlan.CarePlanStatus.toCoding() = Coding(this.system, this.toCode(), this fun CarePlan.isLastTask(task: Task) = this.activity.last()?.outcomeReference?.last()?.extractId() == task.logicalId + +fun CarePlan.CarePlanActivityStatus.toTaskStatus(): Task.TaskStatus { + return when (this) { + CarePlan.CarePlanActivityStatus.STOPPED -> Task.TaskStatus.FAILED + CarePlan.CarePlanActivityStatus.CANCELLED -> Task.TaskStatus.CANCELLED + CarePlan.CarePlanActivityStatus.NOTSTARTED -> Task.TaskStatus.READY + CarePlan.CarePlanActivityStatus.COMPLETED -> Task.TaskStatus.COMPLETED + CarePlan.CarePlanActivityStatus.ONHOLD -> Task.TaskStatus.ONHOLD + CarePlan.CarePlanActivityStatus.INPROGRESS -> Task.TaskStatus.INPROGRESS + CarePlan.CarePlanActivityStatus.ENTEREDINERROR -> Task.TaskStatus.ENTEREDINERROR + else -> Task.TaskStatus.NULL + } +} 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 58fb260181..e78a8310fb 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 @@ -202,7 +202,7 @@ private fun AppMainNavigationGraph( MainNavigationScreen.FixPatientProfile -> composable( route = - "${it.route}${NavigationArg.routePathsOf(includeCommonArgs = true, NavigationArg.PATIENT_ID, FixPatientViewModel.NAVIGATION_ARG_START)}", + "${it.route}${NavigationArg.routePathsOf(includeCommonArgs = true, NavigationArg.PATIENT_ID, FixPatientViewModel.NAVIGATION_ARG_START, FixPatientViewModel.NAVIGATION_ARG_CARE_PLAN)}", arguments = commonNavArgs.plus(patientIdNavArgument()), ) { FixPatientScreen(navController = navController, appMainViewModel = appMainViewModel) diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/fix/FixPatientScreen.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/fix/FixPatientScreen.kt index 33852e2244..651a8d0095 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/fix/FixPatientScreen.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/fix/FixPatientScreen.kt @@ -34,6 +34,7 @@ import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment @@ -57,6 +58,12 @@ fun FixPatientScreen( val state by viewModel.screenState.collectAsState() val fixState by viewModel.fixState.collectAsState() + LaunchedEffect(fixState) { + if (fixState is DataLoadState.Success || fixState is DataLoadState.Error) { + appMainViewModel.onTaskComplete(System.currentTimeMillis().toString()) + } + } + Scaffold( topBar = { Column( @@ -101,7 +108,7 @@ fun FixPatientScreen( fun PatientFixActionStartContainer( state: DataLoadState, close: () -> Unit, - retry: () -> Unit + retry: () -> Unit, ) { Column( horizontalAlignment = Alignment.CenterHorizontally, diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/fix/FixPatientViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/fix/FixPatientViewModel.kt index bdd2056a27..748339a7bd 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/fix/FixPatientViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/fix/FixPatientViewModel.kt @@ -21,15 +21,23 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch +import org.smartregister.fhircore.engine.data.remote.fhir.resource.ResourceFixerService import org.smartregister.fhircore.engine.domain.util.DataLoadState import org.smartregister.fhircore.quest.navigation.NavigationArg import timber.log.Timber @HiltViewModel -class FixPatientViewModel @Inject constructor(savedStateHandle: SavedStateHandle) : ViewModel() { +class FixPatientViewModel +@Inject +constructor( + savedStateHandle: SavedStateHandle, + private val resourceFixerService: ResourceFixerService, +) : ViewModel() { val patientId = savedStateHandle.get(NavigationArg.PATIENT_ID) ?: "" + private val carePlanId = savedStateHandle.get(NAVIGATION_ARG_CARE_PLAN) private val startState = savedStateHandle.get(NAVIGATION_ARG_START)?.let { FixStartState.valueOf(it) } ?: FixStartState.All @@ -48,9 +56,14 @@ class FixPatientViewModel @Inject constructor(savedStateHandle: SavedStateHandle } fun startFix() { - viewModelScope.launch { + viewModelScope.launch(Dispatchers.IO) { try { fixState.emit(DataLoadState.Loading) + if (carePlanId == null) { + return@launch + } + resourceFixerService.fixCurrentCarePlan(patientId, carePlanId) + fixState.emit(DataLoadState.Success(true)) } catch (e: Exception) { Timber.e(e) fixState.value = DataLoadState.Error(e) @@ -60,6 +73,7 @@ class FixPatientViewModel @Inject constructor(savedStateHandle: SavedStateHandle companion object { const val NAVIGATION_ARG_START = "fix_start_state" + const val NAVIGATION_ARG_CARE_PLAN = "fix_care_plan" } } diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/PatientProfileViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/PatientProfileViewModel.kt index 3a30568fbd..82350c62a4 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/PatientProfileViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/patient/profile/PatientProfileViewModel.kt @@ -29,6 +29,7 @@ import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.PagingData import androidx.paging.cachedIn +import com.google.android.fhir.datacapture.extensions.logicalId import com.google.android.fhir.sync.SyncJobStatus import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject @@ -359,16 +360,22 @@ constructor( ) } is PatientProfileEvent.OpenPatientFixer -> { - val urlParams = - NavigationArg.bindArgumentsOf( - Pair(NavigationArg.FEATURE, AppFeature.PatientManagement.name), - Pair(NavigationArg.HEALTH_MODULE, healthModule), - Pair(NavigationArg.PATIENT_ID, patientId), - Pair(FixPatientViewModel.NAVIGATION_ARG_START, FixStartState.StartFix.name), + if (patientProfileData != null && patientProfileData is ProfileData.HivProfileData) { + val urlParams = + NavigationArg.bindArgumentsOf( + Pair(NavigationArg.FEATURE, AppFeature.PatientManagement.name), + Pair(NavigationArg.HEALTH_MODULE, healthModule), + Pair(NavigationArg.PATIENT_ID, patientId), + Pair(FixPatientViewModel.NAVIGATION_ARG_START, FixStartState.StartFix.name), + Pair( + FixPatientViewModel.NAVIGATION_ARG_CARE_PLAN, + (patientProfileData as ProfileData.HivProfileData).currentCarePlan?.logicalId, + ), + ) + event.navController.navigate( + route = MainNavigationScreen.FixPatientProfile.route + urlParams, ) - event.navController.navigate( - route = MainNavigationScreen.FixPatientProfile.route + urlParams, - ) + } } } } From fe7e4b2aa03addce8dc42a049436feabb0be5041 Mon Sep 17 00:00:00 2001 From: Christopher Seven Phiri Date: Wed, 7 Aug 2024 12:01:35 +0200 Subject: [PATCH 4/6] Update ResourceFixerService.kt --- .../fhir/resource/ResourceFixerService.kt | 50 +++++++------------ 1 file changed, 17 insertions(+), 33 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/remote/fhir/resource/ResourceFixerService.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/remote/fhir/resource/ResourceFixerService.kt index 36e3a53770..6593e39a0a 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/remote/fhir/resource/ResourceFixerService.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/remote/fhir/resource/ResourceFixerService.kt @@ -24,6 +24,7 @@ import java.util.Date import javax.inject.Inject import org.hl7.fhir.r4.model.Bundle import org.hl7.fhir.r4.model.CarePlan +import org.hl7.fhir.r4.model.CarePlan.CarePlanActivityComponent import org.hl7.fhir.r4.model.Coding import org.hl7.fhir.r4.model.Meta import org.hl7.fhir.r4.model.Period @@ -58,9 +59,9 @@ constructor( return } - private suspend fun getMissingTasks(carePlan: CarePlan): List { - val activityOnList = mutableMapOf() - val missingTasks = mutableListOf() + private suspend fun getMissingTasks(carePlan: CarePlan): List { + val activityOnList = mutableMapOf() + val missingTasks = mutableListOf() val tasksToFetch = carePlan.activity.mapNotNull { planActivity -> if (planActivity.shouldShowOnProfile()) { @@ -75,14 +76,7 @@ constructor( activityOnList.forEach { (taskId, activity) -> if (activity.detail?.status != CarePlan.CarePlanActivityStatus.SCHEDULED) { if (!tasks.containsKey(taskId)) { - missingTasks.add( - TaskHolder( - taskId = taskId, - carePlanActivityStatus = activity.detail.status, - taskDescription = activity.detail.description, - questId = activity.detail.code.coding.firstOrNull()?.code, - ), - ) + missingTasks.add(activity) } } } @@ -92,23 +86,24 @@ constructor( private suspend fun handleMissingTasks( patientId: String, carePlan: CarePlan, - tasks: List, + tasks: List, ) { val bundle = Bundle() bundle.setType(Bundle.BundleType.TRANSACTION) bundle.entry.addAll( tasks.map { task -> + val taskId = task.outcomeReference.firstOrNull()?.extractId() Bundle.BundleEntryComponent().apply { request = Bundle.BundleEntryRequestComponent().apply { method = Bundle.HTTPVerb.GET - url = "${ResourceType.Task.name}/${task.taskId}" + url = "${ResourceType.Task.name}/$taskId" } } }, ) - val tasksToRecreate = mutableListOf() + val tasksToRecreate = mutableListOf() val existingTasks = mutableListOf() val resBundle = fhirClient.transaction().withBundle(bundle).execute() @@ -122,34 +117,30 @@ constructor( val resourceToSave = mutableListOf() resourceToSave.addAll(existingTasks) + if (tasksToRecreate.isNotEmpty()) { resourceToSave.addAll( tasksToRecreate.map { createGenericTask( - taskId = it.taskId, patientId = patientId, - taskDescription = it.taskDescription, - questId = it.questId ?: "", + activity = it, carePlan = carePlan, - taskStatus = it.carePlanActivityStatus.toTaskStatus(), ) }, ) } + Timber.e("Task on server: ${existingTasks.size}, Tasks to recreate: ${tasksToRecreate.size}") fhirEngine.create(*resourceToSave.toTypedArray()) } private fun createGenericTask( - taskId: String, patientId: String, - taskDescription: String, - questId: String, + activity: CarePlanActivityComponent, carePlan: CarePlan, - taskStatus: Task.TaskStatus, ): Task { + val questId = activity.detail.code.coding.firstOrNull()?.code val patientReference = Reference().apply { reference = "Patient/$patientId" } - val questRef = Reference().apply { reference = questId } val period = @@ -160,11 +151,11 @@ constructor( val task = Task().apply { - id = taskId - status = taskStatus + id = activity.outcomeReference.firstOrNull()?.extractId() + status = activity.detail.status.toTaskStatus() intent = Task.TaskIntent.PLAN priority = Task.TaskPriority.ROUTINE - description = taskDescription + description = activity.detail.description authoredOn = Date() lastModified = Date() `for` = patientReference @@ -202,10 +193,3 @@ constructor( return task } } - -private data class TaskHolder( - val taskId: String, - val taskDescription: String, - val questId: String?, - val carePlanActivityStatus: CarePlan.CarePlanActivityStatus, -) From ffc9a58b32493e391ceb2d127e4eef8e08e71d6c Mon Sep 17 00:00:00 2001 From: Christopher Seven Phiri Date: Mon, 12 Aug 2024 08:09:29 +0200 Subject: [PATCH 5/6] Add preference --- .../fhir/resource/ResourceFixerService.kt | 81 +++++++++++-------- .../engine/ui/settings/SettingsScreen.kt | 80 ++++++++++-------- .../engine/ui/settings/views/DevMenu.kt | 16 ++-- .../engine/util/SharedPreferenceKey.kt | 1 + .../engine/src/main/res/values/strings.xml | 1 + 5 files changed, 107 insertions(+), 72 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/remote/fhir/resource/ResourceFixerService.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/remote/fhir/resource/ResourceFixerService.kt index 6593e39a0a..4631dd1df3 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/data/remote/fhir/resource/ResourceFixerService.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/data/remote/fhir/resource/ResourceFixerService.kt @@ -32,6 +32,8 @@ import org.hl7.fhir.r4.model.Reference import org.hl7.fhir.r4.model.Resource import org.hl7.fhir.r4.model.ResourceType import org.hl7.fhir.r4.model.Task +import org.smartregister.fhircore.engine.util.SharedPreferenceKey +import org.smartregister.fhircore.engine.util.SharedPreferencesHelper import org.smartregister.fhircore.engine.util.SystemConstants.CARE_PLAN_REFERENCE_SYSTEM import org.smartregister.fhircore.engine.util.SystemConstants.QUESTIONNAIRE_REFERENCE_SYSTEM import org.smartregister.fhircore.engine.util.SystemConstants.RESOURCE_CREATED_ON_TAG_SYSTEM @@ -47,6 +49,7 @@ class ResourceFixerService constructor( private val fhirClient: IGenericClient, private val fhirEngine: FhirEngine, + private val sharedPreferences: SharedPreferencesHelper, ) { suspend fun fixCurrentCarePlan(patientId: String, carePlanId: String) { val carePlan: CarePlan = fhirEngine.get(carePlanId) @@ -55,7 +58,8 @@ constructor( return } Timber.i("Found missing tasks: ${tasks.size}") - handleMissingTasks(patientId, carePlan, tasks) + val isOffline = sharedPreferences.read(SharedPreferenceKey.PATIENT_FIX_TYPE.name, false) + handleMissingTasks(patientId, carePlan, tasks, recreateAll = isOffline) return } @@ -87,40 +91,13 @@ constructor( patientId: String, carePlan: CarePlan, tasks: List, + recreateAll: Boolean, ) { - val bundle = Bundle() - bundle.setType(Bundle.BundleType.TRANSACTION) - bundle.entry.addAll( - tasks.map { task -> - val taskId = task.outcomeReference.firstOrNull()?.extractId() - Bundle.BundleEntryComponent().apply { - request = - Bundle.BundleEntryRequestComponent().apply { - method = Bundle.HTTPVerb.GET - url = "${ResourceType.Task.name}/$taskId" - } - } - }, - ) - - val tasksToRecreate = mutableListOf() - val existingTasks = mutableListOf() - - val resBundle = fhirClient.transaction().withBundle(bundle).execute() - for ((index, entry) in resBundle.entry.withIndex()) { - if (entry.hasResource()) { - existingTasks.add(entry.resource as Task) - } else { - tasksToRecreate.add(tasks[index]) - } - } - val resourceToSave = mutableListOf() - resourceToSave.addAll(existingTasks) - if (tasksToRecreate.isNotEmpty()) { + if (recreateAll) { resourceToSave.addAll( - tasksToRecreate.map { + tasks.map { createGenericTask( patientId = patientId, activity = it, @@ -128,9 +105,49 @@ constructor( ) }, ) + Timber.e("Recreating tasks: ${resourceToSave.size}") + } else { + val bundle = Bundle() + bundle.setType(Bundle.BundleType.TRANSACTION) + bundle.entry.addAll( + tasks.map { task -> + val taskId = task.outcomeReference.firstOrNull()?.extractId() + Bundle.BundleEntryComponent().apply { + request = + Bundle.BundleEntryRequestComponent().apply { + method = Bundle.HTTPVerb.GET + url = "${ResourceType.Task.name}/$taskId" + } + } + }, + ) + + val tasksToRecreate = mutableListOf() + val existingTasks = mutableListOf() + + val resBundle = fhirClient.transaction().withBundle(bundle).execute() + for ((index, entry) in resBundle.entry.withIndex()) { + if (entry.hasResource()) { + existingTasks.add(entry.resource as Task) + } else { + tasksToRecreate.add(tasks[index]) + } + } + resourceToSave.addAll(existingTasks) + if (tasksToRecreate.isNotEmpty()) { + resourceToSave.addAll( + tasksToRecreate.map { + createGenericTask( + patientId = patientId, + activity = it, + carePlan = carePlan, + ) + }, + ) + } + Timber.e("Task on server: ${existingTasks.size}, Tasks to recreate: ${tasksToRecreate.size}") } - Timber.e("Task on server: ${existingTasks.size}, Tasks to recreate: ${tasksToRecreate.size}") fhirEngine.create(*resourceToSave.toTypedArray()) } diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/settings/SettingsScreen.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/settings/SettingsScreen.kt index 39bf3e3ddf..f4e6b3f8da 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/settings/SettingsScreen.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/settings/SettingsScreen.kt @@ -29,13 +29,11 @@ import androidx.compose.material.Scaffold import androidx.compose.material.Text import androidx.compose.material.TopAppBar import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.rounded.Logout import androidx.compose.material.icons.filled.ArrowBack -import androidx.compose.material.icons.outlined.Upload -import androidx.compose.material.icons.rounded.BugReport import androidx.compose.material.icons.rounded.CleaningServices -import androidx.compose.material.icons.rounded.Logout import androidx.compose.material.icons.rounded.Report -import androidx.compose.material.icons.rounded.Sync +import androidx.compose.material.icons.rounded.Task import androidx.compose.material.icons.rounded.Upload import androidx.compose.material.rememberModalBottomSheetState import androidx.compose.runtime.Composable @@ -53,6 +51,7 @@ import me.zhanghai.compose.preference.PreferenceCategory import me.zhanghai.compose.preference.ProvidePreferenceLocals import me.zhanghai.compose.preference.getPreferenceFlow import me.zhanghai.compose.preference.listPreference +import me.zhanghai.compose.preference.switchPreference import org.smartregister.fhircore.engine.R import org.smartregister.fhircore.engine.configuration.preferences.SyncUploadStrategy import org.smartregister.fhircore.engine.ui.settings.views.DevMenu @@ -117,12 +116,12 @@ fun SettingsScreen( // clickListener = settingsViewModel::fetchPractitionerDetails, // modifier = modifier, // ) - UserProfileRow( - icon = Icons.Rounded.Sync, - text = stringResource(id = R.string.sync), - clickListener = settingsViewModel::runSync, - modifier = modifier, - ) + // UserProfileRow( + // icon = Icons.Rounded.Sync, + // text = stringResource(id = R.string.sync), + // clickListener = settingsViewModel::runSync, + // modifier = modifier, + // ) UserProfileRow( icon = Icons.Rounded.Report, text = stringResource(R.string.reports), @@ -130,37 +129,17 @@ fun SettingsScreen( modifier = modifier, ) UserProfileRow( - icon = Icons.Rounded.BugReport, - text = stringResource(R.string.dev_menu), + icon = Icons.Rounded.Task, + text = stringResource(R.string.background_taks), clickListener = { scope.launch { devMenuSheetState.show() } }, modifier = modifier, ) - UserProfileRow( - icon = Icons.Rounded.Logout, - text = stringResource(id = R.string.logout), - clickListener = { settingsViewModel.logoutUser(context) }, - modifier = modifier, - ) - - val timestamp = settingsViewModel.sharedPreferences.read(LAST_PURGE_KEY.name, 0L) - val simpleDateFormat = - SimpleDateFormat(SYNC_TIMESTAMP_OUTPUT_FORMAT, Locale.getDefault()) - val text = context.resources.getString(R.string.last_purge) - val dateFormat = simpleDateFormat.format(timestamp) - - if (timestamp > 0L) { - UserProfileRow( - icon = Icons.Rounded.CleaningServices, - text = "$text: $dateFormat", - clickable = false, - modifier = modifier, - ) - } } item { Divider(color = DividerColor, modifier = Modifier.padding(vertical = 10.dp)) PreferenceCategory(title = { Text(text = "Preferences") }) } + listPreference( key = SharedPreferenceKey.SYNC_UPLOAD_STRATEGY.name, defaultValue = SyncUploadStrategy.Default.name, @@ -179,6 +158,41 @@ fun SettingsScreen( }, values = SyncUploadStrategy.entries.map { it.name }, ) + + switchPreference( + key = SharedPreferenceKey.PATIENT_FIX_TYPE.name, + defaultValue = false, + title = { Text(text = "Fix patients offline") }, + ) + + item { + Divider(color = DividerColor, modifier = Modifier.padding(vertical = 10.dp)) + PreferenceCategory(title = { Text(text = "Others") }) + } + item { + UserProfileRow( + icon = Icons.AutoMirrored.Rounded.Logout, + text = stringResource(id = R.string.logout), + clickListener = { settingsViewModel.logoutUser(context) }, + modifier = modifier, + ) + } + item { + val timestamp = settingsViewModel.sharedPreferences.read(LAST_PURGE_KEY.name, 0L) + val simpleDateFormat = + SimpleDateFormat(SYNC_TIMESTAMP_OUTPUT_FORMAT, Locale.getDefault()) + val text = context.resources.getString(R.string.last_purge) + val dateFormat = simpleDateFormat.format(timestamp) + + if (timestamp > 0L) { + UserProfileRow( + icon = Icons.Rounded.CleaningServices, + text = "$text: $dateFormat", + clickable = false, + modifier = modifier, + ) + } + } } } } diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/settings/views/DevMenu.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/settings/views/DevMenu.kt index 4d6726c851..7770238bbf 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/settings/views/DevMenu.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/settings/views/DevMenu.kt @@ -31,8 +31,10 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.work.WorkInfo +import org.smartregister.fhircore.engine.R import org.smartregister.fhircore.engine.domain.util.DataLoadState import org.smartregister.fhircore.engine.ui.settings.DevViewModel import org.smartregister.fhircore.engine.util.annotation.ExcludeFromJacocoGeneratedReport @@ -45,12 +47,12 @@ fun DevMenu(viewModel: DevViewModel) { val appointmentList by viewModel.observeMissedAppointment(context).collectAsState(listOf()) val interruptedList by viewModel.observeInterrupted(context).collectAsState(listOf()) val resourcePurger by viewModel.observeResourcePurgerWorker(context).collectAsState(listOf()) - val cleanState by viewModel.cleanCorruptedState.collectAsState() +// val cleanState by viewModel.cleanCorruptedState.collectAsState() Column( modifier = Modifier.padding(16.dp).padding(vertical = 20.dp).fillMaxWidth(), ) { - SectionTitle(text = "Developer Options") + SectionTitle(text = stringResource(id = R.string.background_taks)) UserProfileRow( iconAlt = { WorkerStateIcon(states = missedTasks) }, text = "Run missed task worker", @@ -71,11 +73,11 @@ fun DevMenu(viewModel: DevViewModel) { text = "Run Resource Purger Worker", clickListener = @ExcludeFromJacocoGeneratedReport { viewModel.resourcePurger(context) }, ) - UserProfileRow( - iconAlt = { LoadableStateIcon(cleanState) }, - text = "Run Clean corrupted resources", - clickListener = @ExcludeFromJacocoGeneratedReport { viewModel.clearCorruptedEvents() }, - ) +// UserProfileRow( +// iconAlt = { LoadableStateIcon(cleanState) }, +// text = "Run Clean corrupted resources", +// clickListener = @ExcludeFromJacocoGeneratedReport { viewModel.clearCorruptedEvents() }, +// ) } } diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/SharedPreferenceKey.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/SharedPreferenceKey.kt index d9aa196d1a..2783f1da73 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/SharedPreferenceKey.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/SharedPreferenceKey.kt @@ -31,4 +31,5 @@ enum class SharedPreferenceKey { LAST_PURGE_KEY, USER_CLAIM_INFO, SYNC_UPLOAD_STRATEGY, + PATIENT_FIX_TYPE, } diff --git a/android/engine/src/main/res/values/strings.xml b/android/engine/src/main/res/values/strings.xml index caaa7e898d..a36eeb485c 100644 --- a/android/engine/src/main/res/values/strings.xml +++ b/android/engine/src/main/res/values/strings.xml @@ -169,4 +169,5 @@ Ok Opening form... Visit + Backgroung Tasks From 23b23734f3f51c7b94215c41faf4c3c973e947db Mon Sep 17 00:00:00 2001 From: Christopher Seven Phiri Date: Wed, 14 Aug 2024 09:00:37 +0200 Subject: [PATCH 6/6] Update DevMenu.kt --- .../fhircore/engine/ui/settings/views/DevMenu.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/settings/views/DevMenu.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/settings/views/DevMenu.kt index 7770238bbf..0ccb06ef5f 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/settings/views/DevMenu.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/ui/settings/views/DevMenu.kt @@ -47,7 +47,7 @@ fun DevMenu(viewModel: DevViewModel) { val appointmentList by viewModel.observeMissedAppointment(context).collectAsState(listOf()) val interruptedList by viewModel.observeInterrupted(context).collectAsState(listOf()) val resourcePurger by viewModel.observeResourcePurgerWorker(context).collectAsState(listOf()) -// val cleanState by viewModel.cleanCorruptedState.collectAsState() + // val cleanState by viewModel.cleanCorruptedState.collectAsState() Column( modifier = Modifier.padding(16.dp).padding(vertical = 20.dp).fillMaxWidth(), @@ -73,11 +73,11 @@ fun DevMenu(viewModel: DevViewModel) { text = "Run Resource Purger Worker", clickListener = @ExcludeFromJacocoGeneratedReport { viewModel.resourcePurger(context) }, ) -// UserProfileRow( -// iconAlt = { LoadableStateIcon(cleanState) }, -// text = "Run Clean corrupted resources", -// clickListener = @ExcludeFromJacocoGeneratedReport { viewModel.clearCorruptedEvents() }, -// ) + // UserProfileRow( + // iconAlt = { LoadableStateIcon(cleanState) }, + // text = "Run Clean corrupted resources", + // clickListener = @ExcludeFromJacocoGeneratedReport { viewModel.clearCorruptedEvents() }, + // ) } }