Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release/3.0.0.2 #3725

Merged
merged 9 commits into from
Jul 11, 2024
18 changes: 14 additions & 4 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
# Release notes - Android App for DHIS2 - 3.0.0.1
# Release notes - Android App for DHIS2 - 3.0.0.2

### Bug

[ANDROAPP-6194](https://dhis2.atlassian.net/browse/ANDROAPP-6194) Unable to search outside the program
[ANDROAPP-5869](https://dhis2.atlassian.net/browse/ANDROAPP-5869) IllegalArgumentException: Key "" was already used. If you are using LazyColumn/Row please make sure you provide a unique ke...

[ANDROAPP-6195](https://dhis2.atlassian.net/browse/ANDROAPP-6195) Missing terms in transifex
[ANDROAPP-6093](https://dhis2.atlassian.net/browse/ANDROAPP-6093) App crash when a catCombo is override

[ANDROAPP-6210](https://dhis2.atlassian.net/browse/ANDROAPP-6210) UninitializedPropertyAccessException on breaking the glass
[ANDROAPP-6104](https://dhis2.atlassian.net/browse/ANDROAPP-6104) Future date can be entered in Date of birth search field

[ANDROAPP-6124](https://dhis2.atlassian.net/browse/ANDROAPP-6124) Phone number input value type lacks of support for international symbols like \+, \(, \)

[ANDROAPP-6225](https://dhis2.atlassian.net/browse/ANDROAPP-6225) RuntimeException: Unable to start activity ComponentInfo\{com.dhis2/org.dhis2.usescases.searchTrackEntity.SearchTEAc...

[ANDROAPP-6271](https://dhis2.atlassian.net/browse/ANDROAPP-6271) IllegalArgumentException: The provided start date year \(2026\) is out of the years range of 2024..2024.

[ANDROAPP-6285](https://dhis2.atlassian.net/browse/ANDROAPP-6285) Maps - App crashes when navigating from home to maps but not from analytics to maps

[ANDROAPP-6305](https://dhis2.atlassian.net/browse/ANDROAPP-6305) Value of a TEA of valuetype AGE/DATE is not fully validated \(it is possible to add wrong dates\), and it breaks the analytics.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import org.dhis2.data.forms.dataentry.tablefields.spinner.SpinnerViewModel
import org.dhis2.form.model.ValueStoreResult.ERROR_UPDATING_VALUE
import org.dhis2.form.model.ValueStoreResult.VALUE_CHANGED
import org.dhis2.form.model.ValueStoreResult.VALUE_HAS_NOT_CHANGED
import org.dhis2.usescases.datasets.dataSetTable.dataSetSection.TableDataToTableModelMapper.Companion.INDICATORS_TABLE_ID
import org.hisp.dhis.android.core.arch.helpers.Result
import org.hisp.dhis.android.core.common.ValueType
import org.hisp.dhis.android.core.dataelement.DataElement
Expand Down Expand Up @@ -109,10 +110,12 @@ class DataValuePresenter(
val updatedTableModel = mapper(tableData)

val updatedTables = screenState.value.tables.map { tableModel ->
if (tableModel.id == catComboUid) {
updatedTableModel.copy(overwrittenValues = tableModel.overwrittenValues)
} else {
indicatorTables() ?: tableModel
when (tableModel.id) {
catComboUid -> updatedTableModel.copy(
overwrittenValues = tableModel.overwrittenValues,
)
INDICATORS_TABLE_ID -> indicatorTables() ?: tableModel
else -> tableModel
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import io.reactivex.Single
import org.dhis2.bindings.decimalFormat
import org.dhis2.commons.bindings.dataValueConflicts
import org.dhis2.commons.data.tuples.Pair
import org.dhis2.commons.date.DateUtils
import org.dhis2.composetable.model.TableCell
import org.dhis2.data.dhislogic.AUTH_DATAVALUE_ADD
import org.dhis2.data.forms.dataentry.tablefields.FieldViewModel
import org.dhis2.data.forms.dataentry.tablefields.FieldViewModelFactoryImpl
import org.dhis2.data.forms.dataentry.tablefields.spinner.SpinnerViewModel
import org.dhis2.usescases.datasets.dataSetTable.DataSetTableModel
import org.dhis2.utils.DateUtils
import org.hisp.dhis.android.core.D2
import org.hisp.dhis.android.core.arch.helpers.UidsHelper
import org.hisp.dhis.android.core.arch.repositories.scope.RepositoryScope
Expand Down Expand Up @@ -61,6 +61,7 @@ class DataValueRepository(
?.categoryComboUid()
}?.distinct()
}

else -> {
val dataElementsSectionUid = d2.dataSetModule().sections().withDataElements()
.byDataSetUid().eq(dataSetUid)
Expand Down Expand Up @@ -157,29 +158,16 @@ class DataValueRepository(
dataElement: DataElement,
override: List<DataSetElement>?,
): DataElement {
return override
?.firstOrNull {
it.dataElement().uid() == dataElement.uid() && it.categoryCombo() != null
}?.let {
DataElement.builder()
.uid(dataElement.uid())
.code(dataElement.code())
.name(dataElement.name())
.displayName(dataElement.displayName())
.shortName(dataElement.shortName())
.displayShortName(dataElement.displayShortName())
.description(dataElement.description())
.displayDescription(dataElement.displayDescription())
.valueType(dataElement.valueType())
.zeroIsSignificant(dataElement.zeroIsSignificant())
.aggregationType(dataElement.aggregationType())
.formName(dataElement.formName())
.domainType(dataElement.domainType())
.displayFormName(dataElement.displayFormName())
.optionSet(dataElement.optionSet())
.categoryCombo(it.categoryCombo()).build()
}
?: dataElement
val dataSetElement = override?.firstOrNull {
it.dataElement().uid() == dataElement.uid() && it.categoryCombo() != null
}
return if (dataSetElement != null) {
dataElement.toBuilder()
.categoryCombo(dataSetElement.categoryCombo())
.build()
} else {
dataElement
}
}

private fun getDataValues(): Flowable<List<DataSetTableModel>> {
Expand Down Expand Up @@ -290,6 +278,7 @@ class DataValueRepository(
.get()
.map { section -> section.greyedFields() }
.toFlowable()

else -> Flowable.just(ArrayList())
}

Expand Down Expand Up @@ -319,45 +308,38 @@ class DataValueRepository(

private fun getDataElements(categoryCombo: CategoryCombo): Flowable<List<DataElement>> {
return if (sectionUid != "NO_SECTION") {
val listDataElements =
d2.dataSetModule().sections().withDataElements().byDataSetUid().eq(dataSetUid)
.uid(sectionUid).blockingGet()?.dataElements()
val dataElementsOverride: MutableList<DataElement> =
ArrayList()
val dataSetElements =
d2.dataSetModule().dataSets().withDataSetElements().uid(dataSetUid).blockingGet()
?.dataSetElements()
listDataElements
?.map { transformDataElement(it, dataSetElements) }
?.filter { it.categoryComboUid() == categoryCombo.uid() }
?.forEach { dataElementsOverride.add(it) }
val dataElementsInSection = d2.dataSetModule().sections().withDataElements()
.byDataSetUid().eq(dataSetUid)
.uid(sectionUid)
.blockingGet()
?.dataElements()

val dataSetElements = d2.dataSetModule().dataSets().withDataSetElements()
.uid(dataSetUid)
.blockingGet()
?.dataSetElements()

Flowable.just(
dataElementsOverride,
dataElementsInSection
?.map { transformDataElement(it, dataSetElements) }
?.filter { it.categoryComboUid() == categoryCombo.uid() },
)
} else {
val dataElementUids: MutableList<String> =
ArrayList()
val dataSetElements =
d2.dataSetModule().dataSets().withDataSetElements().byUid().eq(dataSetUid).one()
.blockingGet()?.dataSetElements()
for (dataSetElement in dataSetElements!!) {
if (dataSetElement.categoryCombo() != null &&
categoryCombo.uid() == dataSetElement.categoryCombo()!!.uid()
) {
dataElementUids.add(dataSetElement.dataElement().uid())
} else {
val uid = d2.dataElementModule().dataElements()
.uid(dataSetElement.dataElement().uid()).blockingGet()?.categoryComboUid()
if (categoryCombo.uid() == uid) {
dataElementUids.add(dataSetElement.dataElement().uid())
}
}
}
d2.dataElementModule().dataElements()
.byUid().`in`(dataElementUids)
val dataSetElementsInDataset =
d2.dataSetModule().dataSets().withDataSetElements()
.uid(dataSetUid)
.blockingGet()
?.dataSetElements()

val dataElements = d2.dataElementModule().dataElements()
.byUid().`in`(dataSetElementsInDataset?.map { it.dataElement().uid() })
.orderByName(RepositoryScope.OrderByDirection.ASC)
.get().toFlowable()
.blockingGet()

Flowable.just(
dataElements.map { transformDataElement(it, dataSetElementsInDataset) }
.filter { it.categoryComboUid() == categoryCombo.uid() },
)
}
}

Expand Down Expand Up @@ -403,6 +385,7 @@ class DataValueRepository(
.blockingIsEmpty()
hasDataValueAuthority && canWriteCatOption && canWriteOrgUnit
}

else -> Flowable.just(false)
}
}
Expand Down Expand Up @@ -490,35 +473,13 @@ class DataValueRepository(
}

fun getDataTableModel(categoryCombo: CategoryCombo): Observable<DataTableModel> {
return Flowable.zip<
List<DataElement>,
Map<String, List<List<Pair<CategoryOption, Category>>>>,
List<DataSetTableModel>,
List<DataElementOperand>,
List<DataElementOperand>,
DataTableModel,
>(
return Flowable.zip(
getDataElements(categoryCombo),
getCatOptions(categoryCombo.uid()),
getDataValues(),
getGreyFields(),
getCompulsoryDataElements(),
) { dataElements: List<DataElement>,
optionsWithCategory: Map<
String,
List<
List<
Pair<
CategoryOption,
Category,
>,
>,
>,
>,
dataValues: List<DataSetTableModel>,
disabledDataElements: List<DataElementOperand>,
compulsoryCells: List<DataElementOperand>,
->
) { dataElements, optionsWithCategory, dataValues, disabledDataElements, compulsoryCells ->
var options: List<List<String>> = ArrayList()
for ((_, value) in optionsWithCategory) {
options = getCatOptionCombos(value, 0, ArrayList(), null)
Expand Down Expand Up @@ -663,7 +624,7 @@ class DataValueRepository(
.byDataElementUid().eq(dataElement.uid())
.byCategoryOptionComboUid().eq(categoryOptionCombo.uid())
.blockingGet()
?.find { it.dataElement() == dataElement.uid() }
.find { it.dataElement() == dataElement.uid() }
?.syncState()

val conflictInField =
Expand All @@ -672,6 +633,7 @@ class DataValueRepository(
State.ERROR,
State.WARNING,
-> true

else -> false
}
}?.filter {
Expand All @@ -685,17 +647,21 @@ class DataValueRepository(
conflictInField != null &&
error != null ->
conflictInField + listOf(error)

valueStateSyncState == State.ERROR && conflictInField != null ->
conflictInField

error != null ->
listOf(error)

else -> null
}

val warningList = when {
valueStateSyncState == State.WARNING &&
conflictInField != null ->
conflictInField

else ->
null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,14 @@ class TableDataToTableModelMapper(val mapFieldValueToUser: MapFieldValueToUser)
}

return TableModel(
id = "indicators",
id = INDICATORS_TABLE_ID,
title = mapFieldValueToUser.resources.getString(R.string.dashboard_indicators),
tableHeaderModel = tableHeader,
tableRows = tableRows,
)
}

companion object {
const val INDICATORS_TABLE_ID = "indicators"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,30 +44,27 @@ class SearchRepositoryImplKt(
searchParametersModel: SearchParametersModel,
isOnline: Boolean,
): TrackedEntitySearchCollectionRepository {
var allowCache = false

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

if (searchRepositoryJava.fetchedTeiUIDs.isNotEmpty() && searchParametersModel.selectedProgram == null) {
trackedEntityInstanceQuery =
trackedEntityInstanceQuery.excludeUids().`in`(searchRepositoryJava.fetchedTeiUIDs.toList())
val allowCache = !(
searchParametersModel != searchRepositoryJava.savedSearchParameters ||
!FilterManager.getInstance().sameFilters(searchRepositoryJava.savedFilters)
)

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

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

return pagerFlow
}

override suspend fun searchParameters(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,8 @@ private void initSearchParameters() {
uid,
selectedOrgUnit,
ValueType.ORGANISATION_UNIT,
null
null,
true
)
);
return Unit.INSTANCE;
Expand Down Expand Up @@ -397,7 +398,6 @@ private void configureBottomNavigation() {
this,
() -> {
presenter.trackSearchMapVisualization();
viewModel.setMapScreen();
showMap();
showSearchAndFilterButtons();
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ class SearchTEMap : FragmentGlobalAbstract(), MapboxMap.OnMapClickListener {
(context as SearchTEActivity).searchComponent.plus(
SearchTEMapModule(),
).inject(this)
viewModel.setMapScreen()
}

override fun onCreateView(
Expand Down
Loading
Loading