Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
32 changes: 1 addition & 31 deletions .github/workflows/rampcapture-release-google-play.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,6 @@ jobs:

next_fork_number=$((last_fork_number + 1))

if (( next_fork_number > 99 )); then
echo "::error::Fork number $next_fork_number exceeds 2 digits (max 99). Bump the base vCode in gradle/libs.versions.toml and reset fork numbering." >&2
exit 1
fi

release_tag="RAMPcapture-DHIS2-v${{ steps.read-version.outputs.vName }}-fork-${next_fork_number}"

run_number=${{ github.run_number }}
Expand Down Expand Up @@ -127,30 +122,6 @@ jobs:
permissions:
contents: write
steps:
- name: Delete previous draft release
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail

# Keep only one RAMPcapture draft release at a time: remove any
# leftover draft from a prior run before creating the new one.
# Draft releases don't create an actual git tag until published,
# so there's no tag to clean up here. Published (non-draft)
# releases are left untouched.
while IFS=$'\t' read -r tag_name is_draft; do
[[ "$tag_name" =~ ^RAMPcapture-DHIS2-v[0-9]+(\.[0-9]+)*-fork-[0-9]+$ ]] || continue
[[ "$is_draft" == "true" ]] || continue

echo "Deleting previous draft release: $tag_name"
gh release delete "$tag_name" \
--repo "${{ github.repository }}" \
--yes
done < <(
gh api --paginate "repos/${{ github.repository }}/releases?per_page=100" \
--jq '.[] | [.tag_name, .draft] | @tsv'
)

- name: Create GitHub release
env:
GH_TOKEN: ${{ github.token }}
Expand All @@ -171,8 +142,7 @@ jobs:
--repo "${{ github.repository }}" \
--target "${{ github.sha }}" \
--title "${{ needs.build-aab.outputs.releaseTag }}" \
--notes "$release_notes" \
--draft
--notes "$release_notes"

deploy-google-play:
needs: build-aab
Expand Down
48 changes: 37 additions & 11 deletions README.md

Large diffs are not rendered by default.

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

import org.dhis2.bindings.userFriendlyValue
import org.dhis2.commons.simprints.ramp.model.DetailedEnrollmentListingSettings
import org.dhis2.simprints.ramp.model.DetailedEnrollment
import org.dhis2.tracker.search.model.DomainEnrollment
import org.dhis2.tracker.search.model.DomainProgram
import org.hisp.dhis.android.core.D2
import org.hisp.dhis.android.core.event.Event
import java.util.Date

internal class DetailedEnrollmentRepository(
private val d2: D2,
) {
fun get(
enrollments: List<DomainEnrollment>?,
programs: List<DomainProgram>?,
settings: DetailedEnrollmentListingSettings,
excludedProgramUid: String? = null,
): List<DetailedEnrollment> {
val includedEnrollments = enrollments?.filter { it.program != excludedProgramUid }.orEmpty()
if (includedEnrollments.isEmpty() || programs.isNullOrEmpty()) return emptyList()

val programNames = programs.associate { it.uid to it.displayName }
val siteNames = getSiteNames(includedEnrollments)
val eventDetails = getEventDetails(includedEnrollments, settings)

return includedEnrollments.mapNotNull { enrollment ->
programNames[enrollment.program]
?.takeIf { it.isNotBlank() }
?.let { programName ->
val details = eventDetails[enrollment.uid]
DetailedEnrollment(
programName = programName,
admitted = details?.admitted,
discharge = details?.discharge,
outcome = details?.outcome,
site = siteNames[enrollment.orgUnit],
)
}
}
}

private fun getSiteNames(enrollments: List<DomainEnrollment>): Map<String, String> {
val orgUnitIds = enrollments.mapNotNull { it.orgUnit }.distinct()
if (orgUnitIds.isEmpty()) return emptyMap()

return d2
.organisationUnitModule()
.organisationUnits()
.byUid()
.`in`(orgUnitIds)
.blockingGet()
.mapNotNull { orgUnit ->
val uid = orgUnit.uid()
val name = orgUnit.displayName()
if (uid.isNullOrBlank() || name.isNullOrBlank()) null else uid to name
}.toMap()
}

private fun getEventDetails(
enrollments: List<DomainEnrollment>,
settings: DetailedEnrollmentListingSettings,
): Map<String, EnrollmentEventDetails> {
if (
settings.admissionProgramStageIds.isEmpty() &&
settings.dischargeProgramStageIds.isEmpty()
) {
return emptyMap()
}

val enrollmentIds = enrollments.map { it.uid }.distinct()
if (enrollmentIds.isEmpty()) return emptyMap()

return d2
.eventModule()
.events()
.withTrackedEntityDataValues()
.byEnrollmentUid()
.`in`(enrollmentIds)
.byDeleted()
.isFalse
.blockingGet()
.groupBy { it.enrollment() }
.mapNotNull { (enrollmentId, events) ->
enrollmentId
?.takeIf { it.isNotBlank() }
?.let { it to events.toEnrollmentEventDetails(settings) }
}.toMap()
}

private fun List<Event>.toEnrollmentEventDetails(
settings: DetailedEnrollmentListingSettings,
): EnrollmentEventDetails {
val admitted =
filter { it.programStage() in settings.admissionProgramStageIds }
.mapNotNull { it.eventDate() }
.minOrNull()
val dischargeEvents = filter { it.programStage() in settings.dischargeProgramStageIds }
val discharge = dischargeEvents.mapNotNull { it.eventDate() }.maxOrNull()

return EnrollmentEventDetails(
admitted = admitted,
discharge = discharge,
outcome = dischargeEvents.latestOutcome(settings.dischargeOutcomeDataElementIds),
)
}

private fun List<Event>.latestOutcome(dischargeOutcomeDataElementIds: Set<String>): String? =
mapNotNull { event ->
event
.trackedEntityDataValues()
.orEmpty()
.firstOrNull { dataValue ->
dataValue.dataElement() in dischargeOutcomeDataElementIds &&
!dataValue.value().isNullOrBlank()
}?.let { dataValue -> event to dataValue }
}.maxWithOrNull(
compareBy(
{ (event) -> event.eventDate() ?: event.dueDate() ?: event.created() ?: Date(0) },
{ (event) -> event.uid().orEmpty() },
),
)?.second
?.userFriendlyValue(d2)
?.takeIf { it.isNotBlank() }

private data class EnrollmentEventDetails(
val admitted: Date?,
val discharge: Date?,
val outcome: String?,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.dhis2.simprints.ramp.model

import java.util.Date

data class DetailedEnrollment(
val programName: String,
val admitted: Date?,
val discharge: Date?,
val outcome: String?,
val site: String?,
)
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ private fun VisitHeaderRow(
) {
Row(modifier = Modifier.heightIntrinsicRow()) {
HeaderCell(
text = stringResource(R.string.simprints_ramp_history_table_visit),
text = stringResource(R.string.simprints_ramp_visit),
width = RowHeaderWidth,
textAlign = TextAlign.Start,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class EventPageConfigurator(

override fun displayDataEntry(): Boolean = isPortrait

override fun displayAnalytics(): Boolean = eventCaptureRepository.hasAnalytics() && !displayTableView()
override fun displayAnalytics(): Boolean = false

override fun displayRelationships(): Boolean = eventCaptureRepository.hasRelationships()

Expand Down
4 changes: 4 additions & 0 deletions app/src/main/java/org/dhis2/usescases/main/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,8 @@ class MainActivity : ActivityGlobalAbstract() {
}

private fun showPeriodRequest(periodRequest: FilterManager.PeriodRequest) {
if (supportFragmentManager.isStateSaved) return

if (periodRequest == FilterManager.PeriodRequest.FROM_TO) {
FilterPeriodsDialog
.newPeriodsFilter(filterType = Filters.PERIOD, isFromToFilter = true)
Expand All @@ -327,6 +329,8 @@ class MainActivity : ActivityGlobalAbstract() {
}

private fun openOrgUnitTreeSelector() {
if (supportFragmentManager.isStateSaved) return

OUTreeFragment
.Builder()
.withPreselectedOrgUnits(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ public List<String> trackedEntityTypeFields() {

@Override
public boolean filtersApplyOnGlobalSearch() {
if (FilterManager.getInstance().getTransferredFilter()) return false;
return FilterManager.getInstance().getTotalFilters() == 0 ||
!FilterManager.getInstance().getOrgUnitFilters().isEmpty() ||
!FilterManager.getInstance().getStateFilters().isEmpty();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.withContext
import org.dhis2.commons.filters.FilterManager
import org.dhis2.commons.filters.sorting.SortingItem
import org.dhis2.commons.simprints.ramp.model.DetailedEnrollmentListingSettings
import org.dhis2.commons.simprints.ramp.repository.RampDatastoreRepository
import org.dhis2.commons.viewmodel.DispatcherProvider
import org.dhis2.data.search.SearchParametersModel
import org.dhis2.maps.model.MapItemModel
import org.dhis2.mobile.commons.customintents.CustomIntentRepository
import org.dhis2.mobile.commons.model.CustomIntentActionTypeModel
import org.dhis2.simprints.ramp.data.DetailedEnrollmentRepository
import org.dhis2.tracker.input.model.TrackerInputType
import org.dhis2.tracker.input.ui.action.FieldUid
import org.dhis2.tracker.search.model.TrackedEntitySearchItemResult
Expand All @@ -38,6 +41,9 @@ class SearchRepositoryImplKt(
private val eventInfoProvider: EventInfoProvider,
private val customIntentRepository: CustomIntentRepository,
) : SearchRepositoryKt {
private val rampDatastoreRepository = RampDatastoreRepository(d2)
private val detailedEnrollmentRepository = DetailedEnrollmentRepository(d2)
private var detailedEnrollmentListingSettings: DetailedEnrollmentListingSettings? = null
private lateinit var savedSearchParameters: SearchParametersModel

private lateinit var savedFilters: FilterManager
Expand All @@ -50,6 +56,8 @@ class SearchRepositoryImplKt(
queryData: MutableMap<String, List<String>?>?,
programUid: String?,
): Boolean {
detailedEnrollmentListingSettings =
rampDatastoreRepository.detailedEnrollmentListingSettings(programUid)
if (!this::savedSearchParameters.isInitialized) {
savedSearchParameters =
SearchParametersModel(
Expand Down Expand Up @@ -87,7 +95,11 @@ class SearchRepositoryImplKt(
allowCache = true
}

return if (isOnline && FilterManager.getInstance().stateFilters.isEmpty()) {
return if (
isOnline &&
FilterManager.getInstance().stateFilters.isEmpty() &&
!(FilterManager.getInstance().transferredFilter && searchParametersModel.selectedProgram != null)
) {
trackedEntityInstanceQuery
.allowOnlineCache()
.eq(allowCache)
Expand Down Expand Up @@ -219,6 +231,15 @@ class SearchRepositoryImplKt(
): SearchTeiModel {
val searchTeiModel = SearchTeiModel()
searchTeiModel.tei = searchItemResult
detailedEnrollmentListingSettings?.let { listingSettings ->
searchTeiModel.detailedEnrollments =
detailedEnrollmentRepository.get(
enrollments = searchItemResult.enrollments,
programs = searchItemResult.enrolledPrograms,
settings = listingSettings,
excludedProgramUid = searchItemResult.selectedEnrollment?.program,
)
}
searchItemResult.enrolledPrograms?.forEach {
searchTeiModel.addProgramInfo(
it.uid,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ class SearchTEActivity :
FilterManager.getInstance().clearSorting()
FilterManager.getInstance().clearAssignToMe()
FilterManager.getInstance().clearFollowUp()
FilterManager.getInstance().clearTransferred()
presenter.clearOtherFiltersIfWebAppIsConfig()
}
super.onDestroy()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -815,7 +815,7 @@ class SearchTEIViewModel(

fun fetchGlobalResults(): Flow<PagingData<SearchTeiModel>>? {
// get uids to exclude for possible duplicates
return if (searching) {
return if (searching && !filterManager.transferredFilter) {
flow {
val excludeValues = searchRepositoryKt.getExcludeValues()

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.dhis2.usescases.searchTrackEntity;

import org.dhis2.mobile.commons.model.MetadataIconData;
import org.dhis2.simprints.ramp.model.DetailedEnrollment;
import org.dhis2.tracker.search.model.DomainEnrollment;
import org.dhis2.tracker.search.model.TrackedEntitySearchItemAttributeDomain;
import org.dhis2.tracker.search.model.TrackedEntitySearchItemResult;
Expand All @@ -9,6 +10,7 @@

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;

public class SearchTeiModel{

Expand All @@ -22,6 +24,7 @@ public class SearchTeiModel{
private boolean openedAttributeList = false;
private String sortingKey;
private String sortingValue;
private List<DetailedEnrollment> detailedEnrollments;

@Nullable
public String onlineErrorMessage;
Expand All @@ -34,6 +37,7 @@ public SearchTeiModel() {
this.textAttributeValues = new LinkedHashMap<>();
this.sortingKey = null;
this.sortingValue = null;
this.detailedEnrollments = null;
this.onlineErrorMessage = null;
this.metadataIconDataMap = new HashMap<>();
}
Expand Down Expand Up @@ -140,5 +144,14 @@ public String getSortingValue() {
return sortingValue;
}

@Nullable
public List<DetailedEnrollment> getDetailedEnrollments() {
return detailedEnrollments;
}

public void setDetailedEnrollments(@Nullable List<DetailedEnrollment> detailedEnrollments) {
this.detailedEnrollments = detailedEnrollments;
}


}
Loading