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
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@

- Bump Coroutines to v1.11.0
- Bump Gradle to v9.5.1
- Bump sqlCipher to v4.16.0
- Bump Material Components to v1.14.0
- Bump KSP to v2.3.8
- Bump asm to v9.10.1
- Bump Sentry Android to v6.8.1
- Bump Compose BOM to v2026.05.01
- Bump Sentry to v8.42.0

### Changes

- Update Titration nudge threshold for diabetic patients in SriLanka from `140/90` to `130/80`

## 2026.05.11

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -453,8 +453,8 @@ class BloodPressureRepositoryAndroidTest {
val newestBpForPatient3 = TestData.bloodPressureMeasurement(
uuid = UUID.fromString("3d948d3b-6d8f-4608-bde0-d8750fef75d4"),
patientUuid = patient3Uuid,
systolic = 150,
diastolic = 90,
systolic = 135,
diastolic = 85,
createdAt = Instant.parse("2000-01-01T00:03:00Z"),
recordedAt = Instant.parse("2000-01-01T00:03:00Z"),
updatedAt = Instant.parse("2000-01-01T00:03:00Z"),
Expand All @@ -475,9 +475,9 @@ class BloodPressureRepositoryAndroidTest {
))

// when
val isNewestBpEntryHighForPatient1 = repository.isNewestBpEntryHigh(patient1Uuid).blockingFirst()
val isNewestBpEntryHighForPatient2 = repository.isNewestBpEntryHigh(patient2Uuid).blockingFirst()
val isNewestBpEntryHighForPatient3 = repository.isNewestBpEntryHigh(patient3Uuid).blockingFirst()
val isNewestBpEntryHighForPatient1 = repository.isNewestBpEntryHigh(patient1Uuid, isDiabeticPatient = false, isSriLankaEnabled = false).blockingFirst()
val isNewestBpEntryHighForPatient2 = repository.isNewestBpEntryHigh(patient2Uuid, isDiabeticPatient = true, isSriLankaEnabled = false).blockingFirst()
val isNewestBpEntryHighForPatient3 = repository.isNewestBpEntryHigh(patient3Uuid, isDiabeticPatient = true, isSriLankaEnabled = true).blockingFirst()

// then
assertThat(isNewestBpEntryHighForPatient1).isTrue()
Expand Down
35 changes: 20 additions & 15 deletions app/src/main/java/org/simple/clinic/bp/BloodPressureMeasurement.kt
Original file line number Diff line number Diff line change
Expand Up @@ -252,20 +252,25 @@ data class BloodPressureMeasurement(
fun purgeBloodPressureMeasurementWhenPatientIsNull(): Int

@Query("""
SELECT
CASE
WHEN (COUNT(BP.uuid) >= 1) THEN 1
ELSE 0
END
FROM BloodPressureMeasurement BP
WHERE
BP.patientUuid = :patientUuid AND
date(BP.recordedAt) == :currentDate AND
(BP.systolic >= 140 OR BP.diastolic >= 90) AND
BP.deletedAt IS NULL
ORDER BY BP.recordedAt DESC
LIMIT 1
""")
fun isNewestBpEntryHigh(patientUuid: UUID, currentDate: LocalDate): Observable<Boolean>
SELECT
CASE
WHEN (COUNT(BP.uuid) >= 1) THEN 1
ELSE 0
END
FROM BloodPressureMeasurement BP
WHERE
BP.patientUuid = :patientUuid AND
date(BP.recordedAt) == :currentDate AND
(BP.systolic >= :systolicThreshold OR BP.diastolic >= :diastolicThreshold) AND
BP.deletedAt IS NULL
ORDER BY BP.recordedAt DESC
LIMIT 1
""")
fun isNewestBpEntryHigh(
patientUuid: UUID,
currentDate: LocalDate,
systolicThreshold: Int,
diastolicThreshold: Int
): Observable<Boolean>
}
}
20 changes: 18 additions & 2 deletions app/src/main/java/org/simple/clinic/bp/BloodPressureRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -166,9 +166,25 @@ class BloodPressureRepository @Inject constructor(
)
}

fun isNewestBpEntryHigh(patientUuid: UUID): Observable<Boolean> {
fun isNewestBpEntryHigh(
patientUuid: UUID,
isDiabeticPatient: Boolean,
isSriLankaEnabled: Boolean
): Observable<Boolean> {

val currentDate = LocalDate.now(utcClock)

return dao.isNewestBpEntryHigh(patientUuid = patientUuid, currentDate = currentDate)
val systolicThreshold =
if (isSriLankaEnabled && isDiabeticPatient) 130 else 140

val diastolicThreshold =
if (isSriLankaEnabled && isDiabeticPatient) 80 else 90

return dao.isNewestBpEntryHigh(
patientUuid = patientUuid,
currentDate = currentDate,
systolicThreshold = systolicThreshold,
diastolicThreshold = diastolicThreshold
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -401,14 +401,38 @@ class PatientSummaryEffectHandler @AssistedInject constructor(
effects
.observeOn(schedulersProvider.io())
.switchMap {

val patientUuid = it.patientUuid
bloodPressureRepository.isNewestBpEntryHigh(patientUuid).map { Pair(patientUuid, it) }

val hasDiabetes =
medicalHistoryRepository.historyForPatientOrDefaultImmediate(
defaultHistoryUuid = uuidGenerator.v4(),
patientUuid = patientUuid
).diagnosedWithDiabetes == MedicalHistoryAnswer.Yes

val isSriLanka =
country.isoCountryCode == Country.SRI_LANKA

bloodPressureRepository.isNewestBpEntryHigh(
patientUuid = patientUuid,
isDiabeticPatient = hasDiabetes,
isSriLankaEnabled = isSriLanka
).map { isNewestBpEntryHigh ->
Pair(patientUuid, isNewestBpEntryHigh)
}
}
.switchMap { (patientUuid, isNewestBpEntryHigh) ->
prescriptionRepository.hasPrescriptionForPatientChangedToday(patientUuid).map { Pair(isNewestBpEntryHigh, it) }
prescriptionRepository
.hasPrescriptionForPatientChangedToday(patientUuid)
.map { hasPrescriptionsChangedToday ->
Pair(isNewestBpEntryHigh, hasPrescriptionsChangedToday)
}
}
.map { (isNewestBpEntryHigh, hasPrescriptionsChangedToday) ->
ClinicalDecisionSupportInfoLoaded(isNewestBpEntryHigh, hasPrescriptionsChangedToday)
ClinicalDecisionSupportInfoLoaded(
isNewestBpEntryHigh,
hasPrescriptionsChangedToday
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,14 @@ class PatientSummaryEffectHandlerTest {
// given
val patientUuid = UUID.fromString("44daeb85-de4c-4807-8b31-6a88bf597cc7")

whenever(bloodPressureRepository.isNewestBpEntryHigh(patientUuid)).doReturn(Observable.just(true))
val medicalHistory = TestData.medicalHistory()

whenever(medicalHistoryRepository.historyForPatientOrDefaultImmediate(
defaultHistoryUuid = uuidGenerator.v4(),
patientUuid = patientUuid
)) doReturn medicalHistory

whenever(bloodPressureRepository.isNewestBpEntryHigh(patientUuid, medicalHistory.diagnosedWithDiabetes == Yes, false)).doReturn(Observable.just(true))
whenever(prescriptionRepository.hasPrescriptionForPatientChangedToday(patientUuid)).doReturn(Observable.just(true))

// when
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ import org.simple.clinic.cvdrisk.calculator.NonLabBasedCVDRiskCalculator
import org.simple.clinic.drugs.DiagnosisWarningPrescriptions
import org.simple.clinic.drugs.PrescriptionRepository
import org.simple.clinic.facility.FacilityRepository
import org.simple.clinic.feature.Feature
import org.simple.clinic.feature.Features
import org.simple.clinic.medicalhistory.Answer
import org.simple.clinic.medicalhistory.MedicalHistoryRepository
import org.simple.clinic.overdue.Appointment.Status.Cancelled
import org.simple.clinic.overdue.AppointmentCancelReason
Expand All @@ -35,11 +34,9 @@ import org.simple.clinic.patient.PatientRepository
import org.simple.clinic.patient.businessid.Identifier
import org.simple.clinic.patient.businessid.Identifier.IdentifierType.BpPassport
import org.simple.clinic.patientattribute.PatientAttributeRepository
import org.simple.clinic.remoteconfig.DefaultValueConfigReader
import org.simple.clinic.summary.OpenIntention.LinkIdWithPatient
import org.simple.clinic.summary.OpenIntention.ViewExistingPatient
import org.simple.clinic.summary.OpenIntention.ViewNewPatient
import org.simple.clinic.util.NoOpRemoteConfigService
import org.simple.clinic.util.RxErrorsRule
import org.simple.clinic.util.TestUserClock
import org.simple.clinic.util.TestUtcClock
Expand Down Expand Up @@ -104,16 +101,17 @@ class PatientSummaryScreenLogicTest {
.atZone(userClock.zone)
.toInstant()

whenever(bpRepository.isNewestBpEntryHigh(patientUuid)) doReturn Observable.just(true)
val medicalHistory = TestData.medicalHistory(uuid = medicalHistoryUuid)
whenever(medicalHistoryRepository.historyForPatientOrDefaultImmediate(
defaultHistoryUuid = uuidGenerator.v4(),
patientUuid = patientUuid
)) doReturn medicalHistory
whenever(bpRepository.isNewestBpEntryHigh(patientUuid, isDiabeticPatient = medicalHistory.diagnosedWithDiabetes == Answer.Yes, isSriLankaEnabled = false)) doReturn Observable.just(true)
whenever(patientRepository.patientProfile(patientUuid)) doReturn Observable.just(Optional.of(patientProfile))
whenever(patientRepository.latestPhoneNumberForPatient(patientUuid)) doReturn Optional.empty()
whenever(appointmentRepository.lastCreatedAppointmentForPatient(patientUuid)) doReturn Optional.empty()
whenever(bpRepository.hasBPRecordedToday(patientUuid, today)) doReturn Observable.just(true)
whenever(facilityRepository.facility(assignedFacilityUuid)) doReturn Optional.of(TestData.facility())
whenever(medicalHistoryRepository.historyForPatientOrDefaultImmediate(
defaultHistoryUuid = uuidGenerator.v4(),
patientUuid = patientUuid
)) doReturn TestData.medicalHistory(uuid = medicalHistoryUuid)
whenever(prescriptionRepository.newestPrescriptionsForPatientImmediate(patientUuid)) doReturn emptyList()
}

Expand Down
14 changes: 7 additions & 7 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ dagger = "2.59.2"
kotlin = "2.3.21"
kotlinx-serialization = "1.11.0"

ksp = "2.3.7"
ksp = "2.3.8"

ktlint = "0.36.0"

Expand All @@ -39,11 +39,11 @@ coroutines = "1.11.0"

compose-compiler = "1.5.13"

androidx-compose-bom = "2026.05.00"
androidx-compose-bom = "2026.05.01"

composeThemeAdapter = "0.36.0"

sqlCipher = "4.15.0"
sqlCipher = "4.16.0"

[libraries]
android-desugaring = "com.android.tools:desugar_jdk_libs:2.1.5"
Expand Down Expand Up @@ -95,7 +95,7 @@ androidx-viewmodel-savedstate = { module = "androidx.lifecycle:lifecycle-viewmod

androidx-lifecycle-livedata-ktx = { module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref = "androidx-lifecycle" }

asm = "org.ow2.asm:asm:9.9.1"
asm = "org.ow2.asm:asm:9.10.1"

chucker = { module = "com.github.chuckerteam.chucker:library", version.ref = "chucker" }
chucker-no-op = { module = "com.github.chuckerteam.chucker:library-no-op", version.ref = "chucker" }
Expand Down Expand Up @@ -142,7 +142,7 @@ logback-classic = "ch.qos.logback:logback-classic:1.2.11"

lottie = "com.airbnb.android:lottie-compose:6.7.1"

material = "com.google.android.material:material:1.13.0"
material = "com.google.android.material:material:1.14.0"

mockito-kotlin = "org.mockito.kotlin:mockito-kotlin:6.3.0"

Expand Down Expand Up @@ -181,7 +181,7 @@ rx-java = "io.reactivex.rxjava2:rxjava:2.2.21"
rx-kotlin = "io.reactivex.rxjava2:rxkotlin:2.4.0"
rx-preferences = "com.f2prateek.rx.preferences2:rx-preferences:2.0.1"

sentry-android = "io.sentry:sentry-android:8.41.0"
sentry-android = "io.sentry:sentry-android:8.42.0"

signaturepad = "com.github.gcacace:signature-pad:1.3.1"

Expand Down Expand Up @@ -231,7 +231,7 @@ kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref =
kotlin-compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
google-services = { id = "com.google.gms.google-services", version = "4.4.4" }
sentry = { id = "io.sentry.android.gradle", version = "6.6.0" }
sentry = { id = "io.sentry.android.gradle", version = "6.8.1" }

[bundles]
androidx-camera = ["androidx-camera-core", "androidx-camera-camera2", "androidx-camera-view", "androidx-camera-lifecycle"]
Expand Down
Loading