From 01898df3474a075f068b39b2ad946db3619f8e3a Mon Sep 17 00:00:00 2001 From: Siddharth Agarwal Date: Fri, 13 Dec 2024 15:03:29 +0530 Subject: [PATCH] Update the model when statin info is loaded --- .../clinic/summary/PatientSummaryModel.kt | 11 +- .../clinic/summary/PatientSummaryScreen.kt | 3 +- .../clinic/summary/PatientSummaryScreenUi.kt | 4 +- .../clinic/summary/PatientSummaryUpdate.kt | 32 ++++-- .../summary/PatientSummaryViewRenderer.kt | 6 +- .../summary/PatientSummaryUpdateTest.kt | 102 +++++++++++++++++- .../summary/PatientSummaryViewRendererTest.kt | 13 ++- 7 files changed, 145 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/org/simple/clinic/summary/PatientSummaryModel.kt b/app/src/main/java/org/simple/clinic/summary/PatientSummaryModel.kt index 82e719bfd35..fbd81136a56 100644 --- a/app/src/main/java/org/simple/clinic/summary/PatientSummaryModel.kt +++ b/app/src/main/java/org/simple/clinic/summary/PatientSummaryModel.kt @@ -2,6 +2,7 @@ package org.simple.clinic.summary import android.os.Parcelable import kotlinx.parcelize.Parcelize +import org.simple.clinic.cvdrisk.StatinInfo import org.simple.clinic.facility.Facility import org.simple.clinic.overdue.Appointment import org.simple.clinic.patient.PatientStatus @@ -27,7 +28,7 @@ data class PatientSummaryModel( val hasPrescribedDrugsChangedToday: Boolean?, val scheduledAppointment: ParcelableOptional?, val hasShownDiagnosisWarningDialog: Boolean, - val canPrescribeStatin: Boolean?, + val statinInfo: StatinInfo?, ) : Parcelable, PatientSummaryChildModel { companion object { @@ -46,7 +47,7 @@ data class PatientSummaryModel( hasPrescribedDrugsChangedToday = null, scheduledAppointment = null, hasShownDiagnosisWarningDialog = false, - canPrescribeStatin = null, + statinInfo = null, ) } } @@ -82,7 +83,7 @@ data class PatientSummaryModel( get() = scheduledAppointment != null && scheduledAppointment.isPresent() val hasStatinInfoLoaded: Boolean - get() = canPrescribeStatin != null + get() = statinInfo != null override fun readyToRender(): Boolean { return hasLoadedPatientSummaryProfile && hasLoadedCurrentFacility && hasPatientRegistrationData != null @@ -128,7 +129,7 @@ data class PatientSummaryModel( return copy(scheduledAppointment = appointment.toOptional().parcelable()) } - fun updateStatinInfo(canPrescribeStatin: Boolean): PatientSummaryModel { - return copy(canPrescribeStatin = canPrescribeStatin) + fun updateStatinInfo(statinInfo: StatinInfo?): PatientSummaryModel { + return copy(statinInfo = statinInfo) } } diff --git a/app/src/main/java/org/simple/clinic/summary/PatientSummaryScreen.kt b/app/src/main/java/org/simple/clinic/summary/PatientSummaryScreen.kt index 2bd60703f74..68f08ccf53f 100644 --- a/app/src/main/java/org/simple/clinic/summary/PatientSummaryScreen.kt +++ b/app/src/main/java/org/simple/clinic/summary/PatientSummaryScreen.kt @@ -41,6 +41,7 @@ import org.simple.clinic.R import org.simple.clinic.ReportAnalyticsEvents import org.simple.clinic.common.ui.theme.SimpleTheme import org.simple.clinic.contactpatient.ContactPatientBottomSheet +import org.simple.clinic.cvdrisk.StatinInfo import org.simple.clinic.databinding.ScreenPatientSummaryBinding import org.simple.clinic.di.injector import org.simple.clinic.editpatient.EditPatientScreen @@ -763,7 +764,7 @@ class PatientSummaryScreen : clinicalDecisionSupportAlertView.visibility = GONE } - override fun showStatinAlert() { + override fun showStatinAlert(statinInfo: StatinInfo) { shouldShowStatinNudge = true } diff --git a/app/src/main/java/org/simple/clinic/summary/PatientSummaryScreenUi.kt b/app/src/main/java/org/simple/clinic/summary/PatientSummaryScreenUi.kt index 090c3c55304..42db607609b 100644 --- a/app/src/main/java/org/simple/clinic/summary/PatientSummaryScreenUi.kt +++ b/app/src/main/java/org/simple/clinic/summary/PatientSummaryScreenUi.kt @@ -1,5 +1,7 @@ package org.simple.clinic.summary +import org.simple.clinic.cvdrisk.StatinInfo + interface PatientSummaryScreenUi { fun populatePatientProfile(patientSummaryProfile: PatientSummaryProfile) fun showEditButton() @@ -18,6 +20,6 @@ interface PatientSummaryScreenUi { fun showClinicalDecisionSupportAlert() fun hideClinicalDecisionSupportAlert() fun hideClinicalDecisionSupportAlertWithoutAnimation() - fun showStatinAlert() + fun showStatinAlert(statinInfo: StatinInfo) fun hideStatinAlert() } diff --git a/app/src/main/java/org/simple/clinic/summary/PatientSummaryUpdate.kt b/app/src/main/java/org/simple/clinic/summary/PatientSummaryUpdate.kt index 8205a769da1..0197ea4ad45 100644 --- a/app/src/main/java/org/simple/clinic/summary/PatientSummaryUpdate.kt +++ b/app/src/main/java/org/simple/clinic/summary/PatientSummaryUpdate.kt @@ -4,6 +4,7 @@ import com.spotify.mobius.Next import com.spotify.mobius.Next.next import com.spotify.mobius.Next.noChange import com.spotify.mobius.Update +import org.simple.clinic.cvdrisk.StatinInfo import org.simple.clinic.drugs.DiagnosisWarningPrescriptions import org.simple.clinic.drugs.PrescribedDrug import org.simple.clinic.medicalhistory.Answer.Yes @@ -106,21 +107,36 @@ class PatientSummaryUpdate( model: PatientSummaryModel ): Next { val minAgeForStatin = 40 + val maxAgeForCVDRisk = 74 val hasHadStroke = event.medicalHistory.hasHadStroke == Yes val hasHadHeartAttack = event.medicalHistory.hasHadHeartAttack == Yes val hasDiabetes = event.medicalHistory.diagnosedWithDiabetes == Yes val hasCVD = hasHadStroke || hasHadHeartAttack - val isPatientEligibleForStatin = (event.age >= minAgeForStatin && hasDiabetes) || hasCVD val hasStatinsPrescribedAlready = event.prescriptions.any { it.name.contains("statin", ignoreCase = true) } val canPrescribeStatin = event.isPatientDead.not() && event.assignedFacility?.facilityType.equals("UHC", ignoreCase = true) && event.hasBPRecordedToday && - hasStatinsPrescribedAlready.not() && - isPatientEligibleForStatin + hasStatinsPrescribedAlready.not() - val updatedModel = model.updateStatinInfo(canPrescribeStatin) - return next(updatedModel) + return when { + event.age < minAgeForStatin -> { + val updatedModel = model.updateStatinInfo(StatinInfo(canPrescribeStatin = false)) + next(updatedModel) + } + + event.age > maxAgeForCVDRisk -> { + val updatedModel = model.updateStatinInfo(StatinInfo(canPrescribeStatin = canPrescribeStatin && (hasCVD || hasDiabetes))) + next(updatedModel) + } + + hasCVD || hasDiabetes -> { + val updatedModel = model.updateStatinInfo(StatinInfo(canPrescribeStatin = canPrescribeStatin)) + next(updatedModel) + } + + else -> dispatch(LoadCVDRisk(model.patientUuid)) + } } private fun cvdRiskLoaded( @@ -131,7 +147,7 @@ class PatientSummaryUpdate( return if (cvdRisk != null) { dispatch(LoadStatinInfo(model.patientUuid)) } else { - dispatch(CalculateCVDRisk(model.patientSummaryProfile!!.patient)) + dispatch(CalculateCVDRisk(model.patientSummaryProfile!!.patient)) } } @@ -139,9 +155,7 @@ class PatientSummaryUpdate( event: StatinInfoLoaded, model: PatientSummaryModel ): Next { - val statinInfo = event.statinInfo - //update model with statin info - return noChange() + return next(model.updateStatinInfo(event.statinInfo)) } private fun hypertensionNotNowClicked(continueToDiabetesDiagnosisWarning: Boolean): Next { diff --git a/app/src/main/java/org/simple/clinic/summary/PatientSummaryViewRenderer.kt b/app/src/main/java/org/simple/clinic/summary/PatientSummaryViewRenderer.kt index e490c1101a5..a3ed0d63268 100644 --- a/app/src/main/java/org/simple/clinic/summary/PatientSummaryViewRenderer.kt +++ b/app/src/main/java/org/simple/clinic/summary/PatientSummaryViewRenderer.kt @@ -47,7 +47,7 @@ class PatientSummaryViewRenderer( } private fun renderClinicalDecisionBasedOnAppointment(model: PatientSummaryModel) { - if (model.canPrescribeStatin == true) + if (model.statinInfo?.canPrescribeStatin == true) return if (model.hasScheduledAppointment) { @@ -153,8 +153,8 @@ class PatientSummaryViewRenderer( private fun renderStatinAlert(model: PatientSummaryModel) { if (model.hasStatinInfoLoaded.not()) return - if (model.canPrescribeStatin == true) { - ui.showStatinAlert() + if (model.statinInfo?.canPrescribeStatin == true) { + ui.showStatinAlert(model.statinInfo) ui.hideClinicalDecisionSupportAlertWithoutAnimation() } else { ui.hideStatinAlert() diff --git a/app/src/test/java/org/simple/clinic/summary/PatientSummaryUpdateTest.kt b/app/src/test/java/org/simple/clinic/summary/PatientSummaryUpdateTest.kt index 19b6dc5b3ee..b943feb1104 100644 --- a/app/src/test/java/org/simple/clinic/summary/PatientSummaryUpdateTest.kt +++ b/app/src/test/java/org/simple/clinic/summary/PatientSummaryUpdateTest.kt @@ -7,6 +7,7 @@ import com.spotify.mobius.test.NextMatchers.hasNoModel import com.spotify.mobius.test.UpdateSpec import com.spotify.mobius.test.UpdateSpec.assertThatNext import org.junit.Test +import org.simple.clinic.cvdrisk.StatinInfo import org.simple.clinic.drugs.DiagnosisWarningPrescriptions import org.simple.clinic.facility.FacilityConfig import org.simple.clinic.medicalhistory.Answer.No @@ -2108,7 +2109,61 @@ class PatientSummaryUpdateTest { } @Test - fun `when statin prescription check info is loaded and can prescribe statin, then update the state`() { + fun `when statin prescription check info is loaded and person is below 40, then update the state with false`() { + updateSpec + .given(defaultModel) + .whenEvent(StatinPrescriptionCheckInfoLoaded( + age = 39, + isPatientDead = false, + hasBPRecordedToday = true, + assignedFacility = TestData.facility( + name = "UHC Simple", + facilityType = "UHC" + ), + medicalHistory = TestData.medicalHistory( + hasDiabetes = No, + hasHadStroke = No, + hasHadHeartAttack = No, + ), + prescriptions = listOf( + TestData.prescription(name = "losartin") + ), + )) + .then(assertThatNext( + hasModel(defaultModel.updateStatinInfo(StatinInfo(canPrescribeStatin = false))), + hasNoEffects() + )) + } + + @Test + fun `when statin prescription check info is loaded and person is above 74, then update the state`() { + updateSpec + .given(defaultModel) + .whenEvent(StatinPrescriptionCheckInfoLoaded( + age = 75, + isPatientDead = false, + hasBPRecordedToday = true, + assignedFacility = TestData.facility( + name = "UHC Simple", + facilityType = "UHC" + ), + medicalHistory = TestData.medicalHistory( + hasDiabetes = No, + hasHadStroke = No, + hasHadHeartAttack = No, + ), + prescriptions = listOf( + TestData.prescription(name = "losartin") + ), + )) + .then(assertThatNext( + hasModel(defaultModel.updateStatinInfo(StatinInfo(canPrescribeStatin = false))), + hasNoEffects() + )) + } + + @Test + fun `when statin prescription check info is loaded and has history of cvd or diabetes, then update the state`() { updateSpec .given(defaultModel) .whenEvent(StatinPrescriptionCheckInfoLoaded( @@ -2129,11 +2184,38 @@ class PatientSummaryUpdateTest { ), )) .then(assertThatNext( - hasModel(defaultModel.updateStatinInfo(true)), + hasModel(defaultModel.updateStatinInfo(StatinInfo(canPrescribeStatin = true))), hasNoEffects() )) } + @Test + fun `when statin prescription check info is loaded and is eligible for cvd risk, then load cvd risk`() { + updateSpec + .given(defaultModel) + .whenEvent(StatinPrescriptionCheckInfoLoaded( + age = 50, + isPatientDead = false, + hasBPRecordedToday = true, + assignedFacility = TestData.facility( + name = "UHC Simple", + facilityType = "UHC" + ), + medicalHistory = TestData.medicalHistory( + hasDiabetes = No, + hasHadStroke = No, + hasHadHeartAttack = No, + ), + prescriptions = listOf( + TestData.prescription(name = "losartin") + ), + )) + .then(assertThatNext( + hasNoModel(), + hasEffects(LoadCVDRisk(defaultModel.patientUuid)) + )) + } + @Test fun `when cvd risk is loaded and risk score is not null, then load statin info`() { updateSpec @@ -2172,6 +2254,22 @@ class PatientSummaryUpdateTest { )) } + @Test + fun `when statin info is loaded, then update the state`() { + val statinInfo = StatinInfo( + canPrescribeStatin = true + ) + updateSpec + .given(defaultModel) + .whenEvent(StatinInfoLoaded( + statinInfo = statinInfo + )) + .then(assertThatNext( + hasModel(defaultModel.updateStatinInfo(statinInfo)), + hasNoEffects() + )) + } + private fun PatientSummaryModel.forExistingPatient(): PatientSummaryModel { return copy(openIntention = ViewExistingPatient) } diff --git a/app/src/test/java/org/simple/clinic/summary/PatientSummaryViewRendererTest.kt b/app/src/test/java/org/simple/clinic/summary/PatientSummaryViewRendererTest.kt index a1daccc2f7c..faabe40596b 100644 --- a/app/src/test/java/org/simple/clinic/summary/PatientSummaryViewRendererTest.kt +++ b/app/src/test/java/org/simple/clinic/summary/PatientSummaryViewRendererTest.kt @@ -5,6 +5,7 @@ import org.mockito.kotlin.mock import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoMoreInteractions +import org.simple.clinic.cvdrisk.StatinInfo import org.simple.clinic.facility.FacilityConfig import org.simple.clinic.patient.PatientStatus import org.simple.clinic.patient.businessid.Identifier @@ -766,9 +767,10 @@ class PatientSummaryViewRendererTest { @Test fun `when statin info is loaded and can prescribe statin then show the statin alert`() { //given + val statinInfo = StatinInfo(canPrescribeStatin = true) val model = defaultModel .currentFacilityLoaded(facilityWithDiabetesManagementDisabled) - .updateStatinInfo(true) + .updateStatinInfo(statinInfo) // when uiRenderer.render(model) @@ -778,16 +780,17 @@ class PatientSummaryViewRendererTest { verify(ui).hideTeleconsultButton() verify(ui).hideNextAppointmentCard() verify(ui, times(2)).hideClinicalDecisionSupportAlertWithoutAnimation() - verify(ui).showStatinAlert() + verify(ui).showStatinAlert(statinInfo) verifyNoMoreInteractions(ui) } @Test fun `when statin info is loaded and can not prescribe statin then hide the statin alert`() { //given + val statinInfo = StatinInfo(canPrescribeStatin = false) val model = defaultModel .currentFacilityLoaded(facilityWithDiabetesManagementDisabled) - .updateStatinInfo(false) + .updateStatinInfo(statinInfo) // when uiRenderer.render(model) @@ -838,7 +841,7 @@ class PatientSummaryViewRendererTest { .patientSummaryProfileLoaded(patientSummaryProfile = patientSummaryProfile) .clinicalDecisionSupportInfoLoaded(isNewestBpEntryHigh = true, hasPrescribedDrugsChangedToday = false) .scheduledAppointmentLoaded(appointment) - .updateStatinInfo(true) + .updateStatinInfo(StatinInfo(canPrescribeStatin = true)) val uiRenderer = PatientSummaryViewRenderer( @@ -858,7 +861,7 @@ class PatientSummaryViewRendererTest { verify(ui).showPatientDiedStatus() verify(ui).hideDiabetesView() verify(ui).hideTeleconsultButton() - verify(ui).showStatinAlert() + verify(ui).showStatinAlert(StatinInfo(canPrescribeStatin = true)) verify(ui).hideClinicalDecisionSupportAlertWithoutAnimation() verify(ui).showNextAppointmentCard() verifyNoMoreInteractions(ui)