diff --git a/.github/workflows/ci-mwcore.yml b/.github/workflows/ci-mwcore.yml index 0ce7d7cf73..6f54ff6e94 100644 --- a/.github/workflows/ci-mwcore.yml +++ b/.github/workflows/ci-mwcore.yml @@ -12,6 +12,41 @@ env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} jobs: + dataclerk: + runs-on: macos-latest + steps: + - name: Cancel Previous workflow runs + uses: styfle/cancel-workflow-action@0.9.1 + with: + access_token: ${{ github.token }} + + - name: Checkout 🛎️ + uses: actions/checkout@v2 + with: + fetch-depth: 2 + + - name: Set up JDK 17 + uses: actions/setup-java@v1 + with: + java-version: 17 + + - name: Decode google-services.json + run: echo $ENCODED_GOOGLE_SERVICES_JSON | base64 -d > android/quest/google-services.json + env: + ENCODED_GOOGLE_SERVICES_JSON: ${{ secrets.MWCORE_GOOGLE_SERVICES_JSON }} + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + working-directory: android + + - name: Spotless check engine module + run: ./gradlew :dataclerk:spotlessCheck + working-directory: android + + - name: Builds + run: ./gradlew :dataclerk:assembleDebug + working-directory: android + engine-tests: runs-on: macos-latest steps: @@ -43,6 +78,10 @@ jobs: run: ./gradlew :engine:spotlessCheck working-directory: android + - name: Builds + run: ./gradlew :engine:assembleDebug + working-directory: android + # - name: Run Engine unit tests with Gradle # run: ./gradlew :engine:clean && ./gradlew :engine:jacocoTestReport -x :engine:testReleaseUnitTest --stacktrace # working-directory: android @@ -83,6 +122,10 @@ jobs: run: ./gradlew :quest:spotlessCheck working-directory: android + - name: Builds + run: ./gradlew :quest:assembleMwcoreDevDebug + working-directory: android + # - name: Run Quest unit tests with Gradle # run: ./gradlew :quest:clean && ./gradlew :quest:jacocoTestReportMwcoreDebug -x :quest:testMwcoreReleaseUnitTest --stacktrace # working-directory: android 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 2df12e0ced..0b09d2843b 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,36 @@ 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 = @@ -186,7 +196,8 @@ constructor( .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)) @@ -197,7 +208,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 aa25131f95..0de8d613ce 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 40e1464f61..421746141c 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 9f16e2128e..fd3065bb13 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 @@ -55,8 +55,7 @@ import org.smartregister.fhircore.engine.domain.model.ProfileData 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.DispatcherProvider -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 +93,10 @@ abstract class TracingRegisterDao( 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 +127,7 @@ abstract class TracingRegisterDao( page: Int = -1, ): List>> { filters as TracingRegisterFilter + val filterFilter = applicationConfiguration().patientTypeFilterTagViaMetaCodingSystem val patients: List = fhirEngine .search { @@ -139,12 +142,12 @@ abstract class TracingRegisterDao( 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() } @@ -159,8 +162,11 @@ abstract class TracingRegisterDao( 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 } @@ -310,8 +316,9 @@ abstract class TracingRegisterDao( .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 } } @@ -367,7 +374,7 @@ abstract class TracingRegisterDao( 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 -> @@ -377,6 +384,7 @@ abstract class TracingRegisterDao( 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) { @@ -439,6 +447,7 @@ abstract class TracingRegisterDao( 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 676ec5408c..9367aba907 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( @@ -241,13 +243,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(), ) @@ -264,61 +267,24 @@ 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 = list.orderedBy.coding @@ -327,7 +293,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/build.gradle.kts b/android/quest/build.gradle.kts index 532508db7b..ec77be4c54 100644 --- a/android/quest/build.gradle.kts +++ b/android/quest/build.gradle.kts @@ -145,8 +145,8 @@ android { dimension = "apps" applicationIdSuffix = ".mwcore" versionNameSuffix = "-mwcore" - versionCode = 32 - versionName = "0.1.22" + versionCode = 33 + versionName = "0.1.23" } create("mwcoreDev") { dimension = "apps" 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 35396624f8..52a3497b35 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 5e712431e1..b0c3555f5b 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,10 +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 b5655e67f1..31bf881041 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 @@ -217,6 +217,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 46365be067..e414472d21 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 @@ -197,7 +197,7 @@ private fun AppMainNavigationGraph( "${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( 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 df795532a1..a6cb9fd48a 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()) @@ -193,7 +193,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 f04eb4e7a0..fa0b9efe3e 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 @@ -86,7 +86,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/ProfileViewData.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/shared/models/ProfileViewData.kt index 00adcc2967..6ceec6cfb9 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 @@ -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 99b9d9412b..e664cb81e1 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 @@ -85,7 +87,12 @@ fun TracingProfileScreen( navController: NavHostController, modifier: Modifier = Modifier, viewModel: TracingProfileViewModel = hiltViewModel(), + appViewModel: AppMainViewModel = hiltViewModel(), ) { + val taskId by appViewModel.completedTaskId.collectAsState() + + LaunchedEffect(taskId) { taskId?.let { viewModel.fetchTracingData() } } + TracingProfilePage( navController, modifier = modifier, @@ -108,6 +115,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( @@ -166,15 +179,18 @@ 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(), ) { @@ -438,10 +454,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 b4ff08ec6f..b0c72a460c 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 { @@ -104,7 +89,7 @@ constructor( is SyncJobStatus.Finished, is SyncJobStatus.Failed, -> { isSyncing.value = false - fetchTracingData() + // fetchTracingData() } is SyncJobStatus.Started -> { isSyncing.value = true @@ -124,7 +109,6 @@ constructor( fun fetchTracingData() { viewModelScope.launch { registerRepository.loadPatientProfileData(appFeatureName, healthModule, patientId)?.let { - patientProfileData = it _patientProfileViewDataFlow.value = profileViewDataMapper.transformInputToOutputModel(it) as ProfileViewData.TracingProfileData @@ -173,12 +157,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, + backReference = "notify", ) } } @@ -201,6 +186,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 }