Skip to content
Merged
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
- Bump Gradle to v9.7.0
- Replace legacy edittext-mask dependency with custom DateOfBirthEditText for DOB input masking

### Changes

- Update blood pressure high-range criteria for diabetic patients in Bangladesh to ≥130/80 mmHg

## 2026.08.03

### Internal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import org.simple.clinic.TestClinicApp
import org.simple.clinic.bp.history.adapter.BloodPressureHistoryListItem
import org.simple.clinic.util.scheduler.SchedulersProvider
import org.simple.clinic.TestData
import org.simple.clinic.appconfig.Country
import org.simple.clinic.util.Rules
import org.simple.clinic.util.TestUserClock
import org.simple.clinic.util.TestUtcClock
Expand Down Expand Up @@ -138,11 +139,13 @@ class BloodPressureHistoryListItemPagingSourceTest {
appDatabase = appDatabase,
utcClock = utcClock,
userClock = userClock,
bpEditableDuration = Duration.ofMinutes(10),
schedulersProvider = schedulersProvider,
country = TestData.country(),
dateFormatter = dateFormatter,
timeFormatter = timeFormatter,
source = appDatabase.bloodPressureDao().allBloodPressuresPagingSource(patientUuid)
bpEditableDuration = Duration.ofMinutes(10),
source = appDatabase.bloodPressureDao().allBloodPressuresPagingSource(patientUuid),
hasDiabetes = false
)

val pager = TestPager(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import dagger.assisted.AssistedInject
import kotlinx.coroutines.rx2.asCoroutineDispatcher
import kotlinx.coroutines.withContext
import org.simple.clinic.AppDatabase
import org.simple.clinic.appconfig.Country
import org.simple.clinic.bp.history.adapter.BloodPressureHistoryListItem
import org.simple.clinic.bp.history.adapter.BloodPressureHistoryListItem.BloodPressureHistoryItem
import org.simple.clinic.util.INVALID
Expand All @@ -29,17 +30,20 @@ class BloodPressureHistoryListItemPagingSource @AssistedInject constructor(
private val utcClock: UtcClock,
private val userClock: UserClock,
private val schedulersProvider: SchedulersProvider,
private val country: Country,
@Named("full_date") private val dateFormatter: DateTimeFormatter,
@Named("time_for_measurement_history") private val timeFormatter: DateTimeFormatter,
@Assisted private val bpEditableDuration: Duration,
@Assisted private val source: PagingSource<Int, BloodPressureMeasurement>
@Assisted private val source: PagingSource<Int, BloodPressureMeasurement>,
@Assisted private val hasDiabetes: Boolean
) : PagingSource<Int, BloodPressureHistoryListItem>() {

@AssistedFactory
interface Factory {
fun create(
bpEditableDuration: Duration,
source: PagingSource<Int, BloodPressureMeasurement>
source: PagingSource<Int, BloodPressureMeasurement>,
hasDiabetes: Boolean
): BloodPressureHistoryListItemPagingSource
}

Expand Down Expand Up @@ -75,7 +79,9 @@ class BloodPressureHistoryListItemPagingSource @AssistedInject constructor(
}
}

private fun convertToBloodPressureHistoryListItems(measurements: List<BloodPressureMeasurement>): List<BloodPressureHistoryListItem> {
private fun convertToBloodPressureHistoryListItems(
measurements: List<BloodPressureMeasurement>,
): List<BloodPressureHistoryListItem> {
val measurementsByDate = measurements.groupBy { it.recordedAt.toLocalDateAtZone(userClock.zone) }

return measurementsByDate.mapValues { (_, measurementsList) ->
Expand All @@ -91,10 +97,16 @@ class BloodPressureHistoryListItemPagingSource @AssistedInject constructor(
null
}

val isHighBp = BloodPressureLevel.isHigh(
measurement = measurement,
country = country,
hasDiabetes = hasDiabetes
)

BloodPressureHistoryItem(
measurement = measurement,
isBpEditable = isBpEditable,
isBpHigh = measurement.level.isHigh,
isBpHigh = isHighBp,
bpDate = dateFormatter.format(recordedAt),
bpTime = bpTime
)
Expand Down
14 changes: 14 additions & 0 deletions app/src/main/java/org/simple/clinic/bp/BloodPressureLevel.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.simple.clinic.bp

import org.simple.clinic.R
import org.simple.clinic.appconfig.Country
import java.util.Optional

enum class BloodPressureLevel(private val urgency: Int, val displayTextRes: Optional<Int>) {
Expand All @@ -23,8 +24,21 @@ enum class BloodPressureLevel(private val urgency: Int, val displayTextRes: Opti
MODERATELY_HIGH, VERY_HIGH, EXTREMELY_HIGH -> true
}


companion object {

fun isHigh(
measurement: BloodPressureMeasurement,
country: Country,
hasDiabetes: Boolean
): Boolean {
return when (compute(measurement)) {
LOW, NORMAL -> false
MILDLY_HIGH -> country.isoCountryCode == Country.BANGLADESH && hasDiabetes
MODERATELY_HIGH, VERY_HIGH, EXTREMELY_HIGH -> true
}
}

fun compute(measurement: BloodPressureMeasurement): BloodPressureLevel {
val systolicLevel = computeSystolic(measurement)
val diastolicLevel = computeDiastolic(measurement)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,27 @@ import dagger.assisted.AssistedInject
import io.reactivex.ObservableTransformer
import io.reactivex.Scheduler
import kotlinx.coroutines.CoroutineScope
import org.simple.clinic.appconfig.Country
import org.simple.clinic.bp.BloodPressureHistoryListItemPagingSource
import org.simple.clinic.bp.BloodPressureRepository
import org.simple.clinic.medicalhistory.Answer
import org.simple.clinic.medicalhistory.MedicalHistoryRepository
import org.simple.clinic.patient.PatientRepository
import org.simple.clinic.summary.PatientSummaryConfig
import org.simple.clinic.util.PagerFactory
import org.simple.clinic.util.extractIfPresent
import org.simple.clinic.util.scheduler.SchedulersProvider
import org.simple.clinic.uuid.UuidGenerator

class BloodPressureHistoryScreenEffectHandler @AssistedInject constructor(
private val bloodPressureRepository: BloodPressureRepository,
private val medicalHistoryRepository: MedicalHistoryRepository,
private val patientRepository: PatientRepository,
private val schedulersProvider: SchedulersProvider,
private val pagerFactory: PagerFactory,
private val pagingSourceFactory: BloodPressureHistoryListItemPagingSource.Factory,
private val patientSummaryConfig: PatientSummaryConfig,
private val uuidGenerator: UuidGenerator,
@Assisted private val viewEffectsConsumer: Consumer<BloodPressureHistoryViewEffect>,
@Assisted private val pagingCacheScope: () -> CoroutineScope
) {
Expand All @@ -44,22 +50,36 @@ class BloodPressureHistoryScreenEffectHandler @AssistedInject constructor(
.build()
}

private fun loadBloodPressureHistory(): ObservableTransformer<LoadBloodPressureHistory, BloodPressureHistoryScreenEvent> {
private fun loadBloodPressureHistory():
ObservableTransformer<LoadBloodPressureHistory, BloodPressureHistoryScreenEvent> {

return ObservableTransformer { effects ->
effects
.observeOn(schedulersProvider.io())
.flatMap {
val pagingSource = bloodPressureRepository.allBloodPressuresPagingSource(it.patientUuid)
.flatMap { effect ->
val pagingSource =
bloodPressureRepository.allBloodPressuresPagingSource(
effect.patientUuid
)

medicalHistoryRepository
.historyForPatientOrDefault(
defaultHistoryUuid = uuidGenerator.v4(),
patientUuid = effect.patientUuid
)
.flatMap { history ->

pagerFactory.createPager(
sourceFactory = {
pagingSourceFactory.create(
bpEditableDuration = patientSummaryConfig.bpEditableDuration,
source = pagingSource,
pagerFactory.createPager(
sourceFactory = {
pagingSourceFactory.create(
bpEditableDuration = patientSummaryConfig.bpEditableDuration,
source = pagingSource,
hasDiabetes = history.diagnosedWithDiabetes == Answer.Yes
)
},
cacheScope = pagingCacheScope.invoke(),
)
},
cacheScope = pagingCacheScope.invoke(),
)
}
}
.map(::BloodPressuresHistoryLoaded)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ data class LoadBloodPressures(
val numberOfBpsToDisplay: Int
) : BloodPressureSummaryViewEffect()

data class LoadMedicalHistory(
val patientUuid: UUID,
) : BloodPressureSummaryViewEffect()

data class LoadBloodPressuresCount(val patientUuid: UUID) : BloodPressureSummaryViewEffect()

data class OpenBloodPressureEntrySheet(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@ import io.reactivex.ObservableTransformer
import io.reactivex.Scheduler
import org.simple.clinic.bp.BloodPressureRepository
import org.simple.clinic.facility.Facility
import org.simple.clinic.medicalhistory.MedicalHistoryRepository
import org.simple.clinic.util.scheduler.SchedulersProvider
import org.simple.clinic.uuid.UuidGenerator

class BloodPressureSummaryViewEffectHandler @AssistedInject constructor(
private val bloodPressureRepository: BloodPressureRepository,
private val medicalHistoryRepository: MedicalHistoryRepository,
private val schedulersProvider: SchedulersProvider,
private val facility: Lazy<Facility>,
@Assisted private val uiActions: BloodPressureSummaryViewUiActions
@Assisted private val uiActions: BloodPressureSummaryViewUiActions,
private val uuidGenerator: UuidGenerator,
) {

@AssistedFactory
Expand All @@ -32,6 +36,7 @@ class BloodPressureSummaryViewEffectHandler @AssistedInject constructor(
.addConsumer(OpenBloodPressureEntrySheet::class.java, { uiActions.openBloodPressureEntrySheet(it.patientUuid, it.currentFacility) }, schedulersProvider.ui())
.addConsumer(OpenBloodPressureUpdateSheet::class.java, { uiActions.openBloodPressureUpdateSheet(it.id) }, schedulersProvider.ui())
.addConsumer(ShowBloodPressureHistoryScreen::class.java, { uiActions.showBloodPressureHistoryScreen(it.patientUuid) }, schedulersProvider.ui())
.addTransformer(LoadMedicalHistory::class.java, loadMedicalHistory(schedulersProvider.io()))
.build()
}

Expand Down Expand Up @@ -71,4 +76,20 @@ class BloodPressureSummaryViewEffectHandler @AssistedInject constructor(
.map(::CurrentFacilityLoaded)
}
}

private fun loadMedicalHistory(
scheduler: Scheduler
): ObservableTransformer<LoadMedicalHistory, BloodPressureSummaryViewEvent> {
return ObservableTransformer { effects ->
effects
.observeOn(scheduler)
.flatMap {
medicalHistoryRepository.historyForPatientOrDefault(
defaultHistoryUuid = uuidGenerator.v4(),
patientUuid = it.patientUuid
)
}
.map(::MedicalHistoryLoaded)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package org.simple.clinic.summary.bloodpressures

import org.simple.clinic.bp.BloodPressureMeasurement
import org.simple.clinic.facility.Facility
import org.simple.clinic.medicalhistory.MedicalHistory
import org.simple.clinic.widgets.UiEvent
import java.util.UUID

Expand All @@ -13,6 +14,8 @@ data class BloodPressuresCountLoaded(val count: Int) : BloodPressureSummaryViewE

data class CurrentFacilityLoaded(val facility: Facility) : BloodPressureSummaryViewEvent()

data class MedicalHistoryLoaded(val medicalHistory: MedicalHistory) : BloodPressureSummaryViewEvent()

data object AddNewBloodPressureClicked : BloodPressureSummaryViewEvent() {
override val analyticsName = "Patient Summary:Add New BP Clicked"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ class BloodPressureSummaryViewInit(
private val config: BloodPressureSummaryViewConfig
) : Init<BloodPressureSummaryViewModel, BloodPressureSummaryViewEffect> {
override fun init(model: BloodPressureSummaryViewModel): First<BloodPressureSummaryViewModel, BloodPressureSummaryViewEffect> {
val effects = mutableSetOf<BloodPressureSummaryViewEffect>()

effects.add(LoadBloodPressuresCount(model.patientUuid))
val effects = mutableSetOf(
LoadBloodPressuresCount(model.patientUuid),
LoadMedicalHistory(model.patientUuid)
)

if (!model.hasLoadedFacility) {
effects.add(LoadCurrentFacility)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import android.os.Parcelable
import kotlinx.parcelize.Parcelize
import org.simple.clinic.bp.BloodPressureMeasurement
import org.simple.clinic.facility.Facility
import org.simple.clinic.medicalhistory.Answer
import org.simple.clinic.medicalhistory.MedicalHistory
import org.simple.clinic.summary.PatientSummaryChildModel
import java.util.UUID

Expand All @@ -12,10 +14,18 @@ data class BloodPressureSummaryViewModel(
val patientUuid: UUID,
val latestBloodPressuresToDisplay: List<BloodPressureMeasurement>?,
val totalRecordedBloodPressureCount: Int?,
val facility: Facility?
val facility: Facility?,
val diagnosedWithDiabetes: Answer?,
) : Parcelable, PatientSummaryChildModel {
companion object {
fun create(patientUuid: UUID) = BloodPressureSummaryViewModel(patientUuid, null, null, null)
fun create(patientUuid: UUID) =
BloodPressureSummaryViewModel(
patientUuid,
null,
null,
null,
null
)
}

val hasLoadedCountOfBloodSugars: Boolean
Expand All @@ -28,7 +38,7 @@ data class BloodPressureSummaryViewModel(
get() = facility!!.config.diabetesManagementEnabled

override fun readyToRender(): Boolean {
return hasLoadedCountOfBloodSugars && latestBloodPressuresToDisplay != null
return hasLoadedCountOfBloodSugars && latestBloodPressuresToDisplay != null && diagnosedWithDiabetes != null
}

fun bloodPressuresLoaded(bloodPressures: List<BloodPressureMeasurement>): BloodPressureSummaryViewModel =
Expand All @@ -39,4 +49,8 @@ data class BloodPressureSummaryViewModel(

fun currentFacilityLoaded(facility: Facility): BloodPressureSummaryViewModel =
copy(facility = facility)

fun medicalHistoryLoaded(medicalHistory: MedicalHistory): BloodPressureSummaryViewModel =
copy(diagnosedWithDiabetes = medicalHistory.diagnosedWithDiabetes)

}
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package org.simple.clinic.summary.bloodpressures

import org.simple.clinic.bp.BloodPressureMeasurement
import org.simple.clinic.medicalhistory.Answer

interface BloodPressureSummaryViewUi {
fun showNoBloodPressuresView()
fun showBloodPressures(bloodPressures: List<BloodPressureMeasurement>)
fun showBloodPressures(
bloodPressures: List<BloodPressureMeasurement>,
diagnosedWithDiabetes: Answer?,
)
fun showSeeAllButton()
fun hideSeeAllButton()
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class BloodPressureSummaryViewUiRenderer(
when {
model.latestBloodPressuresToDisplay == null -> return
model.latestBloodPressuresToDisplay.isEmpty() -> ui.showNoBloodPressuresView()
else -> ui.showBloodPressures(model.latestBloodPressuresToDisplay)
else -> ui.showBloodPressures(model.latestBloodPressuresToDisplay, model.diagnosedWithDiabetes)
}

if (model.hasLoadedFacility && model.hasLoadedCountOfBloodSugars) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class BloodPressureSummaryViewUpdate(
is AddNewBloodPressureClicked -> dispatch(OpenBloodPressureEntrySheet(model.patientUuid, model.facility!!))
is BloodPressureClicked -> dispatch(OpenBloodPressureUpdateSheet(event.id))
is SeeAllClicked -> dispatch(ShowBloodPressureHistoryScreen(model.patientUuid))
is MedicalHistoryLoaded -> next(model.medicalHistoryLoaded(event.medicalHistory))
}
}

Expand Down
Loading
Loading