Skip to content

Commit

Permalink
Merge pull request #3469 from dhis2/ANDROAPP-5536
Browse files Browse the repository at this point in the history
refactor: [ANDROAPP-5536] Search uses paging3
  • Loading branch information
andresmr authored Feb 8, 2024
2 parents 271fff1 + daa4428 commit 74be855
Show file tree
Hide file tree
Showing 12 changed files with 225 additions and 122 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData;
import androidx.paging.PagedList;
import androidx.paging.PagingData;

import org.dhis2.commons.data.EventViewModel;
import org.dhis2.commons.data.SearchTeiModel;
Expand All @@ -15,6 +16,8 @@
import org.hisp.dhis.android.core.program.Program;
import org.hisp.dhis.android.core.settings.AnalyticsDhisVisualizationsGroup;
import org.hisp.dhis.android.core.trackedentity.TrackedEntityType;
import org.hisp.dhis.android.core.trackedentity.search.TrackedEntitySearchCollectionRepository;
import org.hisp.dhis.android.core.trackedentity.search.TrackedEntitySearchItem;
import org.jetbrains.annotations.NotNull;

import java.util.Date;
Expand All @@ -29,9 +32,6 @@ public interface SearchRepository {

Observable<List<Program>> programsWithRegistration(String programTypeId);

@NonNull
LiveData<PagedList<SearchTeiModel>> searchTrackedEntities(SearchParametersModel searchParametersModel, boolean isOnline);

void clearFetchedList();

@NonNull
Expand All @@ -58,6 +58,10 @@ public interface SearchRepository {

TeiDownloadResult download(String teiUid, @Nullable String enrollmentUid, String reason);

SearchTeiModel transform(TrackedEntitySearchItem searchItem, @Nullable Program selectedProgram, boolean offlineOnly, SortingItem sortingItem);

TrackedEntitySearchCollectionRepository getFilteredRepository(SearchParametersModel searchParametersModel);

void setCurrentProgram(@Nullable String currentProgram);
boolean programStagesHaveCoordinates(String programUid);
boolean teTypeAttributesHaveCoordinates(String typeId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Transformations;
import androidx.paging.DataSource;
import androidx.paging.LivePagedListBuilder;
import androidx.paging.PagedList;
import androidx.paging.Pager;
import androidx.paging.PagingData;
import androidx.paging.PagingDataTransforms;
import androidx.paging.PagingLiveData;

import org.dhis2.R;
import org.dhis2.bindings.ExtensionsKt;
Expand Down Expand Up @@ -97,6 +102,11 @@
import io.reactivex.Flowable;
import io.reactivex.Observable;
import io.reactivex.Single;
import kotlin.Unit;
import kotlin.coroutines.Continuation;
import kotlinx.coroutines.ExecutorsKt;
import kotlinx.coroutines.flow.Flow;
import kotlinx.coroutines.flow.FlowCollector;

public class SearchRepositoryImpl implements SearchRepository {

Expand All @@ -105,7 +115,7 @@ public class SearchRepositoryImpl implements SearchRepository {
private final D2 d2;
private final SearchSortingValueSetter sortingValueSetter;
private TrackedEntitySearchCollectionRepository trackedEntityInstanceQuery;
private SearchParametersModel savedSearchParameters;
public SearchParametersModel savedSearchParameters;
private FilterManager savedFilters;
private FilterPresenter filterPresenter;
private DhisPeriodUtils periodUtils;
Expand Down Expand Up @@ -168,40 +178,6 @@ public void clearFetchedList() {
fetchedTeiUids.clear();
}

@NonNull
@Override
public LiveData<PagedList<SearchTeiModel>> searchTrackedEntities(SearchParametersModel searchParametersModel, boolean isOnline) {
boolean allowCache = false;
if (!searchParametersModel.equals(savedSearchParameters) || !FilterManager.getInstance().sameFilters(savedFilters)) {
trackedEntityInstanceQuery = getFilteredRepository(searchParametersModel);
} else {
getFilteredRepository(searchParametersModel);
allowCache = true;
}

if (!fetchedTeiUids.isEmpty() && searchParametersModel.getSelectedProgram() == null) {
trackedEntityInstanceQuery = trackedEntityInstanceQuery.excludeUids().in(new ArrayList<>(fetchedTeiUids));
}

DataSource<TrackedEntitySearchItem, SearchTeiModel> dataSource;

if (isOnline && FilterManager.getInstance().getStateFilters().isEmpty()) {
dataSource = trackedEntityInstanceQuery.allowOnlineCache().eq(allowCache).offlineFirst().getResultDataSource()
.map(result -> transformResult(result, searchParametersModel.getSelectedProgram(), false, FilterManager.getInstance().getSortingItem()));
} else {
dataSource = trackedEntityInstanceQuery.allowOnlineCache().eq(allowCache).offlineOnly().getResultDataSource()
.map(result -> transformResult(result, searchParametersModel.getSelectedProgram(), true, FilterManager.getInstance().getSortingItem()));
}

return new LivePagedListBuilder<>(new DataSource.Factory<TrackedEntitySearchItem, SearchTeiModel>() {
@NonNull
@Override
public DataSource<TrackedEntitySearchItem, SearchTeiModel> create() {
return dataSource;
}
}, 10).build();
}

@NonNull
@Override
public Flowable<List<SearchTeiModel>> searchTeiForMap(SearchParametersModel searchParametersModel, boolean isOnline) {
Expand All @@ -225,7 +201,8 @@ public Flowable<List<SearchTeiModel>> searchTeiForMap(SearchParametersModel sear
.toList().toFlowable();
}

private TrackedEntitySearchCollectionRepository getFilteredRepository(SearchParametersModel searchParametersModel) {
@Override
public TrackedEntitySearchCollectionRepository getFilteredRepository(SearchParametersModel searchParametersModel) {
this.savedSearchParameters = searchParametersModel.copy();
this.savedFilters = FilterManager.getInstance().copy();

Expand All @@ -247,12 +224,12 @@ private TrackedEntitySearchCollectionRepository getFilteredRepository(SearchPara

boolean isUnique = d2.trackedEntityModule().trackedEntityAttributes().uid(dataId).blockingGet().unique();
if (isUnique) {
trackedEntityInstanceQuery = trackedEntityInstanceQuery.byAttribute(dataId).eq(dataValue);
trackedEntityInstanceQuery = trackedEntityInstanceQuery.byFilter(dataId).eq(dataValue);
} else if (dataValue.contains("_os_")) {
dataValue = dataValue.split("_os_")[1];
trackedEntityInstanceQuery = trackedEntityInstanceQuery.byAttribute(dataId).eq(dataValue);
trackedEntityInstanceQuery = trackedEntityInstanceQuery.byFilter(dataId).eq(dataValue);
} else
trackedEntityInstanceQuery = trackedEntityInstanceQuery.byAttribute(dataId).like(dataValue);
trackedEntityInstanceQuery = trackedEntityInstanceQuery.byFilter(dataId).like(dataValue);
}
}

Expand Down Expand Up @@ -725,7 +702,7 @@ public TeiDownloadResult download(String teiUid, @Nullable String enrollmentUid,
return teiDownloader.download(teiUid, enrollmentUid, reason);
}

private SearchTeiModel transformResult(Result<TrackedEntitySearchItem, D2Error> result, @Nullable Program selectedProgram, boolean offlineOnly, SortingItem sortingItem) {
public SearchTeiModel transformResult(Result<TrackedEntitySearchItem, D2Error> result, @Nullable Program selectedProgram, boolean offlineOnly, SortingItem sortingItem) {
try {
return transform(result.getOrThrow(), selectedProgram, offlineOnly, sortingItem);
} catch (Exception e) {
Expand All @@ -736,7 +713,8 @@ private SearchTeiModel transformResult(Result<TrackedEntitySearchItem, D2Error>
}
}

private SearchTeiModel transform(TrackedEntitySearchItem searchItem, @Nullable Program selectedProgram, boolean offlineOnly, SortingItem sortingItem) {
@Override
public SearchTeiModel transform(TrackedEntitySearchItem searchItem, @Nullable Program selectedProgram, boolean offlineOnly, SortingItem sortingItem) {
if (!fetchedTeiUids.contains(searchItem.uid())) {
fetchedTeiUids.add(searchItem.uid());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.dhis2.usescases.searchTrackEntity

import androidx.paging.PagingData
import kotlinx.coroutines.flow.Flow
import org.dhis2.commons.filters.FilterManager
import org.dhis2.data.search.SearchParametersModel
import org.hisp.dhis.android.core.trackedentity.search.TrackedEntitySearchCollectionRepository
import org.hisp.dhis.android.core.trackedentity.search.TrackedEntitySearchItem

class SearchRepositoryImplKt(
private val searchRepositoryJava: SearchRepository,
) : SearchRepositoryKt {

private lateinit var savedSearchParamenters: SearchParametersModel

private lateinit var savedFilters: FilterManager

private lateinit var trackedEntityInstanceQuery: TrackedEntitySearchCollectionRepository

private val fetchedTeiUids = HashSet<String>()

override fun searchTrackedEntities(
searchParametersModel: SearchParametersModel,
isOnline: Boolean,
): Flow<PagingData<TrackedEntitySearchItem>> {
var allowCache = false
savedSearchParamenters = searchParametersModel.copy()
savedFilters = FilterManager.getInstance().copy()

if (searchParametersModel != savedSearchParamenters || !FilterManager.getInstance().sameFilters(savedFilters)) {
trackedEntityInstanceQuery = searchRepositoryJava.getFilteredRepository(searchParametersModel)
} else {
trackedEntityInstanceQuery = searchRepositoryJava.getFilteredRepository(searchParametersModel)
allowCache = true
}

if (fetchedTeiUids.isNotEmpty() && searchParametersModel.selectedProgram == null) {
trackedEntityInstanceQuery =
trackedEntityInstanceQuery.excludeUids().`in`(fetchedTeiUids.toList())
}

val pagerFlow = if (isOnline && FilterManager.getInstance().stateFilters.isNotEmpty()) {
trackedEntityInstanceQuery.allowOnlineCache().eq(allowCache).offlineFirst().getPagingData(10)
} else {
trackedEntityInstanceQuery.allowOnlineCache().eq(allowCache).offlineOnly().getPagingData(10)
}

return pagerFlow
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.dhis2.usescases.searchTrackEntity

import androidx.paging.PagingData
import kotlinx.coroutines.flow.Flow
import org.dhis2.data.search.SearchParametersModel
import org.hisp.dhis.android.core.trackedentity.search.TrackedEntitySearchItem

interface SearchRepositoryKt {

fun searchTrackedEntities(
searchParametersModel: SearchParametersModel,
isOnline: Boolean,
): Flow<PagingData<TrackedEntitySearchItem>>
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.PagedList
import androidx.paging.PagingData
import androidx.paging.cachedIn
import androidx.paging.map
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.dhis2.commons.data.SearchTeiModel
import org.dhis2.commons.filters.FilterManager
import org.dhis2.commons.idlingresource.SearchIdlingResourceSingleton
import org.dhis2.commons.network.NetworkUtils
import org.dhis2.commons.viewmodel.DispatcherProvider
Expand All @@ -29,6 +34,7 @@ class SearchTEIViewModel(
private val initialProgramUid: String?,
initialQuery: MutableMap<String, String>?,
private val searchRepository: SearchRepository,
private val searchRepositoryKt: SearchRepositoryKt,
private val searchNavPageConfigurator: SearchPageConfigurator,
private val mapDataRepository: MapDataRepository,
private val networkUtils: NetworkUtils,
Expand Down Expand Up @@ -226,46 +232,106 @@ class SearchTEIViewModel(
}
}

fun fetchListResults(onPagedListReady: (LiveData<PagedList<SearchTeiModel>>?) -> Unit) {
fun fetchListResults(onPagedListReady: (Flow<PagingData<SearchTeiModel>>?) -> Unit) {
viewModelScope.launch {
val resultPagedList = when {
searching -> loadSearchResults()
displayFrontPageList() -> loadDisplayInListResults()
searching -> loadSearchResults().cachedIn(viewModelScope)
displayFrontPageList() -> loadDisplayInListResults().cachedIn(viewModelScope)
else -> null
}
onPagedListReady(resultPagedList)
}
}

private suspend fun loadSearchResults() = withContext(dispatchers.io()) {
return@withContext searchRepository.searchTrackedEntities(
SearchParametersModel(
selectedProgram = searchRepository.getProgram(initialProgramUid),
queryData = queryData,
),
val searchParametersModel = SearchParametersModel(
selectedProgram = searchRepository.getProgram(initialProgramUid),
queryData = queryData,
)
val getPagingData = searchRepositoryKt.searchTrackedEntities(
searchParametersModel,
searching && networkUtils.isOnline(),
)

return@withContext getPagingData.map { pagingData ->
pagingData.map { item ->
if (
searching && networkUtils.isOnline() &&
FilterManager.getInstance().stateFilters.isEmpty()
) {
searchRepository.transform(
item,
searchParametersModel.selectedProgram,
false,
FilterManager.getInstance().sortingItem,
)
} else {
searchRepository.transform(
item,
searchParametersModel.selectedProgram,
true,
FilterManager.getInstance().sortingItem,
)
}
}
}
}

private suspend fun loadDisplayInListResults() = withContext(dispatchers.io()) {
return@withContext searchRepository.searchTrackedEntities(
SearchParametersModel(
selectedProgram = searchRepository.getProgram(initialProgramUid),
queryData = queryData,
),
val searchParametersModel = SearchParametersModel(
selectedProgram = searchRepository.getProgram(initialProgramUid),
queryData = queryData,
)
val getPagingData = searchRepositoryKt.searchTrackedEntities(
searchParametersModel,
false,
)

return@withContext getPagingData.map { pagingData ->
pagingData.map { item ->
searchRepository.transform(
item,
searchParametersModel.selectedProgram,
true,
FilterManager.getInstance().sortingItem,
)
}
}
}

fun fetchGlobalResults(): LiveData<PagedList<SearchTeiModel>>? {
return if (searching) {
searchRepository.searchTrackedEntities(
SearchParametersModel(
selectedProgram = null,
queryData = queryData,
),
searching && networkUtils.isOnline(),
)
suspend fun fetchGlobalResults() = withContext(dispatchers.io()) {
val searchParametersModel = SearchParametersModel(
selectedProgram = searchRepository.getProgram(initialProgramUid),
queryData = queryData,
)
val getPagingData = searchRepositoryKt.searchTrackedEntities(
searchParametersModel,
searching && networkUtils.isOnline(),
)

return@withContext if (searching) {
getPagingData.map { pagingData ->
pagingData.map { item ->
if (
searching && networkUtils.isOnline() &&
FilterManager.getInstance().stateFilters.isEmpty()
) {
searchRepository.transform(
item,
searchParametersModel.selectedProgram,
false,
FilterManager.getInstance().sortingItem,
)
} else {
searchRepository.transform(
item,
searchParametersModel.selectedProgram,
true,
FilterManager.getInstance().sortingItem,
)
}
}
}
} else {
null
}
Expand Down
Loading

0 comments on commit 74be855

Please sign in to comment.