Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 32 additions & 12 deletions README.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ package org.dhis2.simprints
import android.app.Activity.RESULT_OK
import android.content.Intent
import androidx.lifecycle.ViewModel
import kotlin.concurrent.atomics.AtomicReference
import kotlin.concurrent.atomics.ExperimentalAtomicApi
import org.dhis2.commons.simprints.repository.SimprintsD2Repository
import org.dhis2.commons.simprints.repository.SimprintsSessionRepository
import org.dhis2.commons.simprints.usecases.SimprintsResolvePendingEnrollmentActionUseCase
import kotlin.concurrent.atomics.AtomicReference
import kotlin.concurrent.atomics.ExperimentalAtomicApi

@OptIn(ExperimentalAtomicApi::class)
class SimprintsEnrollmentViewModel(
Expand All @@ -25,14 +25,7 @@ class SimprintsEnrollmentViewModel(
private val pendingAction =
AtomicReference<SimprintsResolvePendingEnrollmentActionUseCase.PendingEnrollmentAction?>(null)

suspend fun onFinishRequested(
isNewEnrollment: Boolean,
enrollmentUid: String,
): Intent? {
if (!isNewEnrollment) {
return null
}

suspend fun onFinishRequested(enrollmentUid: String): Intent? {
val resolvedAction =
sessionRepository.pendingEnrollmentSessionId()?.let {
resolvePendingEnrollmentAction(
Expand All @@ -45,39 +38,77 @@ class SimprintsEnrollmentViewModel(
return resolvedAction.callout.launchIntent
}

suspend fun onAutoEnrollLastRequested(enrollmentUid: String): Intent? {
val sessionId = sessionRepository.get()?.takeIf(String::isNotBlank) ?: return null
val resolvedAction =
resolvePendingEnrollmentAction(
enrollmentUid = enrollmentUid,
sessionId = sessionId,
) ?: return null

pendingAction.store(resolvedAction)
sessionRepository.markPendingEnrollmentFromPossibleDuplicates()
return resolvedAction.callout.launchIntent
}

suspend fun onRegisterLastResult(
resultCode: Int,
data: Intent?,
teiUid: String?,
enrollmentUid: String?,
): RegisterLastResult {
val resolvedAction = pendingAction.exchange(null) ?: return RegisterLastResult.NONE
val resolvedAction =
pendingAction.exchange(null)
?: restorePendingAction(enrollmentUid)
?: return RegisterLastResult.NONE
if (resultCode != RESULT_OK) {
sessionRepository.clearPendingEnrollment()
return RegisterLastResult.ERROR
}

val saved =
if (resultCode == RESULT_OK && teiUid != null) {
val value =
resultMapper.map(
responseData = resolvedAction.callout.responseData,
data = data,
) ?: return RegisterLastResult.ERROR
return try {
val resolvedTeiUid =
teiUid ?: enrollmentUid?.let { simprintsD2Repository.getEnrollmentContext(it)?.teiUid }
if (resolvedTeiUid == null) {
sessionRepository.clearPendingEnrollment()
return RegisterLastResult.ERROR
}
val value =
resultMapper.map(
responseData = resolvedAction.callout.responseData,
data = data,
)
if (value == null) {
sessionRepository.clearPendingEnrollment()
RegisterLastResult.ERROR
} else {
simprintsD2Repository.saveTrackedEntityAttributeValue(
teiUid = teiUid,
teiUid = resolvedTeiUid,
attributeUid = resolvedAction.fieldUid,
value = value,
)
true
} else {
false
sessionRepository.clear()
RegisterLastResult.CONTINUE_FINISH
}

if (saved) {
sessionRepository.clear()
return RegisterLastResult.CONTINUE_FINISH
} catch (e: Exception) {
sessionRepository.clearPendingEnrollment()
throw e
}

return RegisterLastResult.ERROR
}

fun onRegisterLastLaunchFailed() {
pendingAction.store(null)
sessionRepository.clearPendingEnrollment()
}

private suspend fun restorePendingAction(
enrollmentUid: String?,
): SimprintsResolvePendingEnrollmentActionUseCase.PendingEnrollmentAction? {
val resolvedEnrollmentUid = enrollmentUid?.takeIf(String::isNotBlank) ?: return null
val sessionId = sessionRepository.pendingEnrollmentSessionId()?.takeIf(String::isNotBlank) ?: return null
return resolvePendingEnrollmentAction(
enrollmentUid = resolvedEnrollmentUid,
sessionId = sessionId,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package org.dhis2.simprints

import androidx.paging.PagingData
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import org.dhis2.commons.filters.sorting.SortingItem
import org.dhis2.commons.simprints.usecases.SimprintsOrderSearchResultsByIdentifyResponseUseCase
import org.dhis2.commons.simprints.utils.SimprintsSearchUtils
import org.dhis2.data.search.SearchParametersModel
import org.dhis2.form.model.FieldUiModel
import org.dhis2.usescases.searchTrackEntity.SearchRepository
import org.dhis2.usescases.searchTrackEntity.SearchRepositoryKt
import org.dhis2.usescases.searchTrackEntity.SearchTeiModel

class SimprintsLoadBiometricSearchResultsUseCase(
private val searchRepository: SearchRepository,
private val searchRepositoryKt: SearchRepositoryKt,
private val orderSearchResultsByIdentifyResponse: SimprintsOrderSearchResultsByIdentifyResponseUseCase,
) {
suspend operator fun invoke(
searchItems: List<FieldUiModel>,
searchParametersModel: SearchParametersModel,
isOnline: Boolean,
offlineOnly: Boolean,
sortingItem: SortingItem?,
): Flow<PagingData<SearchTeiModel>>? {
val trackedEntities =
orderSearchResultsByIdentifyResponse(
searchFields = searchItems.toSearchFields(),
queryData = searchParametersModel.queryData.orEmpty(),
searchTrackedEntities = {
searchRepositoryKt.searchTrackedEntitiesImmediate(
searchParametersModel = searchParametersModel,
isOnline = isOnline,
)
},
) ?: return null

return flowOf(
PagingData.from(
trackedEntities.map { searchItem ->
searchRepository.transform(
searchItem,
searchParametersModel.selectedProgram,
offlineOnly,
sortingItem,
)
},
),
)
}

private fun List<FieldUiModel>.toSearchFields(): List<SimprintsSearchUtils.SearchField> =
map { field ->
SimprintsSearchUtils.SearchField(
uid = field.uid,
value = field.value,
customIntent = field.customIntent,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package org.dhis2.simprints

import org.dhis2.commons.filters.sorting.SortingItem
import org.dhis2.data.search.SearchParametersModel
import org.dhis2.usescases.searchTrackEntity.SearchRepository
import org.dhis2.usescases.searchTrackEntity.SearchRepositoryKt
import org.dhis2.usescases.searchTrackEntity.SearchTeiModel

class SimprintsLoadPossibleDuplicatesSearchResultsUseCase(
private val searchRepository: SearchRepository,
private val searchRepositoryKt: SearchRepositoryKt,
) {
suspend operator fun invoke(
searchParametersModel: SearchParametersModel,
isOnline: Boolean,
offlineOnly: Boolean,
sortingItem: SortingItem?,
): List<SearchTeiModel>? {
val simprintsQueryData = searchParametersModel.queryData.orEmpty()
val simprintsQueryEntry =
simprintsQueryData.entries.firstOrNull { (_, values) ->
(values?.size ?: 0) > 1
} ?: simprintsQueryData.entries.firstOrNull { (_, values) ->
!values.isNullOrEmpty()
} ?: return null

val simprintsQueryValues =
simprintsQueryEntry
.value
.orEmpty()
.filter(String::isNotBlank)
.distinct()
.takeIf(List<String>::isNotEmpty)
?: return null

val searchItems =
buildList {
simprintsQueryValues.forEach { guidValue ->
addAll(
searchRepositoryKt.searchTrackedEntitiesImmediate(
searchParametersModel =
searchParametersModel.copy(
queryData =
simprintsQueryData.toMutableMap().apply {
put(simprintsQueryEntry.key, listOf(guidValue))
},
),
isOnline = isOnline,
),
)
}
}.distinctBy { it.uid() }

return searchItems.map { searchItem ->
searchRepository.transform(
searchItem,
searchParametersModel.selectedProgram,
offlineOnly,
sortingItem,
)
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package org.dhis2.simprints

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
import org.dhis2.commons.filters.FilterManager
import org.dhis2.commons.network.NetworkUtils
import org.dhis2.data.search.SearchParametersModel
import org.dhis2.usescases.searchTrackEntity.SearchRepository
import org.dhis2.usescases.searchTrackEntity.SearchRepositoryKt

class SimprintsResolveSingleBiometricSearchNavigationUseCase(
private val searchRepository: SearchRepository,
private val searchRepositoryKt: SearchRepositoryKt,
private val networkUtils: NetworkUtils,
private val filterManager: FilterManager,
private val ioDispatcher: CoroutineDispatcher,
) {
data class NavigationTarget(
val teiUid: String,
val programUid: String?,
val enrollmentUid: String?,
val isOnline: Boolean,
)

suspend operator fun invoke(
initialProgramUid: String?,
queryData: Map<String, List<String>?>,
value: String?,
): NavigationTarget? =
withContext(ioDispatcher) {
val biometricSearchValues =
value
?.split(",")
?.map(String::trim)
?.filter(String::isNotEmpty)
?: return@withContext null

if (biometricSearchValues.size != 1) {
return@withContext null
}

val searchParametersModel =
SearchParametersModel(
selectedProgram = searchRepository.getProgram(initialProgramUid),
queryData = queryData.toMutableMap(),
)
val isOnline = queryData.isNotEmpty() && networkUtils.isOnline()
val trackedEntity =
searchRepositoryKt
.searchTrackedEntitiesImmediate(
searchParametersModel = searchParametersModel,
isOnline = isOnline,
).singleOrNull() ?: return@withContext null

val searchTeiModel =
searchRepository.transform(
trackedEntity,
searchParametersModel.selectedProgram,
!(isOnline && filterManager.stateFilters.isEmpty()),
filterManager.sortingItem,
)

NavigationTarget(
teiUid = searchTeiModel.uid(),
programUid = searchTeiModel.selectedEnrollment?.program() ?: initialProgramUid,
enrollmentUid = searchTeiModel.selectedEnrollment?.uid(),
isOnline = searchTeiModel.isOnline,
)
}
}
Loading