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
13 changes: 12 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@

### Internal

- Bump AGP to v9.4.0
- Bump Lint to v32.4.0

## 2026.09.03

### Internal

- Bump AndroidX Benchmark to v1.5.0-rc02
- Bump Sentry to v8.54.0
- Bump Sentry Android to v6.20.0
Expand All @@ -23,12 +30,16 @@
- Update GH Actions `setup-jdk` to v6
- Replaced deprecated Gradle Kotlin DSL by project property delegates with explicit project.property(...) access to improve Gradle 10 compatibility.


### Changes

- Enabled CDSS nudges for all countries, previously limited to Sri Lanka and Ethiopia.
- Applied the lower blood pressure threshold of 130/80 for diabetic patients in Sri Lanka.

### Fixes

- Fixed a crash when loading statin information for a patient.
- Fixed a crash when loading the patient's assigned facility.

## 2026.08.31

### Internal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ data class UpdateCVDRisk(
val newRiskRange: CVDRiskRange
) : PatientSummaryEffect()

data class LoadStatinInfo(val patientUuid: UUID) : PatientSummaryEffect()
data class LoadStatinInfo(
val patient: Patient
) : PatientSummaryEffect()

data class LoadCVDRiskInfo(val patientUuid: UUID) : PatientSummaryEffect()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -307,22 +307,21 @@ class PatientSummaryEffectHandler @AssistedInject constructor(
effects
.observeOn(schedulersProvider.io())
.map { effect ->
val patientUuid = effect.patientUuid
val patient = patientRepository.patientImmediate(patientUuid)
val patient = effect.patient
val medicalHistory = medicalHistoryRepository.historyForPatientOrDefaultImmediate(
defaultHistoryUuid = uuidGenerator.v4(),
patientUuid = patientUuid
patientUuid = patient.uuid
)
val patientAttribute = patientAttributeRepository.getPatientAttributeImmediate(patientUuid)
val riskRange = cvdRiskRepository.getCVDRiskImmediate(patientUuid)?.riskScore
val patientAttribute = patientAttributeRepository.getPatientAttributeImmediate(patient.uuid)
val riskRange = cvdRiskRepository.getCVDRiskImmediate(patient.uuid)?.riskScore
val canPrescribeStatin = if (country.isoCountryCode == Country.SRI_LANKA) {
riskRange?.canPrescribeStatinInSriLanka ?: false
} else {
riskRange?.canPrescribeStatin ?: false || medicalHistory.diagnosedWithDiabetes == MedicalHistoryAnswer.Yes
}

StatinInfoLoaded(
age = patient!!.ageDetails.estimateAge(userClock),
age = patient.ageDetails.estimateAge(userClock),
medicalHistory = medicalHistory,
canPrescribeStatin = canPrescribeStatin,
riskRange = riskRange,
Expand Down Expand Up @@ -523,12 +522,6 @@ class PatientSummaryEffectHandler @AssistedInject constructor(
.flatMap(Function { facilityRepository.facility(it) })
}

private fun getAssignedFacility(assignedFacilityId: UUID?): Optional<Facility> {
return Optional
.ofNullable(assignedFacilityId)
.flatMap { facilityRepository.facility(it) }
}

private fun mapPatientProfileToSummaryProfile(
patientProfile: PatientProfile,
facility: Optional<Facility>
Expand Down Expand Up @@ -564,7 +557,7 @@ class PatientSummaryEffectHandler @AssistedInject constructor(
missingPhoneReminderRepository
.markReminderAsShownFor(effect.patientUuid)
.subscribeOn(scheduler)
.andThen(Observable.empty<PatientSummaryEvent>())
.andThen(Observable.empty())
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ class PatientSummaryUpdate(
is HypertensionNotNowClicked -> hypertensionNotNowClicked(event.continueToDiabetesDiagnosisWarning)
is StatinPrescriptionCheckInfoLoaded -> statinPrescriptionCheckInfoLoaded(event, model)
is CVDRiskCalculated -> saveOrUpdateCVDRisk(event, model)
is CVDRiskUpdated -> dispatch(LoadStatinInfo(model.patientUuid))
is CVDRiskUpdated -> dispatch(LoadStatinInfo(model.patientSummaryProfile!!.patient))
is StatinInfoLoaded -> statinInfoLoaded(event, model)
is AddTobaccoUseClicked -> dispatch(ShowTobaccoStatusDialog)
is TobaccoUseAnswered -> dispatch(UpdateTobaccoUse(model.patientUuid, event.isSmoker, event.isUsingSmokelessTobacco))
Expand Down Expand Up @@ -237,7 +237,7 @@ class PatientSummaryUpdate(
}

isEligibleForLabBasedCvdRisk -> {
dispatch(LoadStatinInfo(model.patientUuid))
dispatch(LoadStatinInfo(model.patientSummaryProfile!!.patient))
}

else -> {
Expand Down Expand Up @@ -294,7 +294,7 @@ class PatientSummaryUpdate(
}

isEligibleForNonLabBasedCvdRisk -> {
dispatch(LoadStatinInfo(model.patientUuid))
dispatch(LoadStatinInfo(model.patientSummaryProfile!!.patient))
}

else -> {
Expand All @@ -314,7 +314,7 @@ class PatientSummaryUpdate(
model: PatientSummaryModel
): Next<PatientSummaryModel, PatientSummaryEffect> {
return when {
event.newRiskRange == null -> dispatch(LoadStatinInfo(model.patientUuid))
event.newRiskRange == null -> dispatch(LoadStatinInfo(model.patientSummaryProfile!!.patient))
event.oldRisk != null -> dispatch(UpdateCVDRisk(event.oldRisk, event.newRiskRange))
else -> dispatch(SaveCVDRisk(model.patientUuid, event.newRiskRange))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class AssignedFacilityEffectHandler @AssistedInject constructor(
return ObservableTransformer { effects ->
effects
.observeOn(schedulersProvider.io())
.map { (patientUuid, assignedFacilityId) ->
.doOnNext { (patientUuid, assignedFacilityId) ->
patientRepository.updateAssignedFacilityId(patientUuid, assignedFacilityId)
}
.map { AssignedFacilityChanged }
Expand All @@ -47,8 +47,11 @@ class AssignedFacilityEffectHandler @AssistedInject constructor(
return ObservableTransformer { effects ->
effects
.observeOn(schedulersProvider.io())
.map { patientRepository.patientImmediate(it.patientUuid) }
.map(::getAssignedFacility)
.map { effect ->
patientRepository.patientImmediate(effect.patientUuid)
?.let(::getAssignedFacility)
?: Optional.empty()
}
.map(::AssignedFacilityLoaded)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -988,16 +988,17 @@ class PatientSummaryEffectHandlerTest {
@Test
fun `when load statin info effect is received, then load statin info`() {
//given
val bmiReading = BMIReading(height = 177f, weight = 53f)

whenever(patientRepository.patientImmediate(patientUuid)) doReturn TestData.patient(
val patient = TestData.patient(
uuid = patientUuid,
patientAgeDetails = PatientAgeDetails(
ageValue = 55,
ageUpdatedAt = Instant.parse("2018-01-01T00:00:00Z"),
dateOfBirth = null,
)
)
val bmiReading = BMIReading(height = 177f, weight = 53f)

whenever(patientRepository.patientImmediate(patientUuid)) doReturn patient
val medicalHistory = TestData.medicalHistory(isSmoking = Yes)

whenever(medicalHistoryRepository.historyForPatientOrDefaultImmediate(
Expand All @@ -1012,7 +1013,7 @@ class PatientSummaryEffectHandlerTest {
TestData.cvdRisk(riskScore = CVDRiskRange(27, 27))

//when
testCase.dispatch(LoadStatinInfo(patientUuid))
testCase.dispatch(LoadStatinInfo(patient))

//then
testCase.assertOutgoingEvents(StatinInfoLoaded(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2463,7 +2463,7 @@ class PatientSummaryUpdateTest {
))
.then(assertThatNext(
hasNoModel(),
hasEffects(LoadStatinInfo(patientUuid))
hasEffects(LoadStatinInfo(model.patientSummaryProfile!!.patient))
))
}

Expand Down Expand Up @@ -2496,14 +2496,15 @@ class PatientSummaryUpdateTest {

@Test
fun `when cvd risk score is calculated and both range and old cvd risk are null, then load statin info`() {
val model = defaultModel.patientSummaryProfileLoaded(patientSummaryProfile)
updateSpec
.given(defaultModel)
.given(model)
.whenEvent(CVDRiskCalculated(
oldRisk = null,
newRiskRange = null
))
.then(assertThatNext(
hasEffects(LoadStatinInfo(defaultModel.patientUuid))
hasEffects(LoadStatinInfo(model.patientSummaryProfile!!.patient))
))
}

Expand Down Expand Up @@ -2995,7 +2996,7 @@ class PatientSummaryUpdateTest {
))
.then(assertThatNext(
hasNoModel(),
hasEffects(LoadStatinInfo(patientUuid))
hasEffects(LoadStatinInfo(model.patientSummaryProfile!!.patient))
))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,22 @@ class AssignedFacilityEffectHandlerTest {
verify(uiActions).notifyAssignedFacilityChanged()
verifyNoMoreInteractions(uiActions)
}

@Test
fun `when load assigned facility effect is received and patient is not found, then assigned facility is empty`() {
// given
val patientUuid = UUID.fromString("17bb9690-9a17-4d90-a45f-8bfdcc4153e4")

whenever(patientRepository.patientImmediate(patientUuid)) doReturn null

// when
effectHandlerTestCase.dispatch(LoadAssignedFacility(patientUuid))

// then
effectHandlerTestCase.assertOutgoingEvents(
AssignedFacilityLoaded(Optional.empty())
)

verifyNoInteractions(uiActions)
}
}
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[versions]
agp = "9.3.2"
agp = "9.4.0"

androidx-cameraView = "1.6.2"
androidx-camera = "1.6.2"
Expand All @@ -21,7 +21,7 @@ ksp = "2.3.11"

ktlint = "0.36.0"

lint = "32.3.2"
lint = "32.4.0"

mobius = "2.1.2"

Expand Down
Loading