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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
- Add statin translations for `bn-BD`, `si-LK`, `ta-LK`
- Bump Sentry Android to v8.1.0
- Bump Sentry Gradle plugin to v5.1.0
- Show smoking status dialog for low and medium cvd risk.

### Fixes

Expand Down
20 changes: 20 additions & 0 deletions app/src/main/java/org/simple/clinic/cvdrisk/CVDRiskLevel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.simple.clinic.cvdrisk

import androidx.compose.ui.graphics.Color
import org.simple.clinic.R

enum class CVDRiskLevel(val displayStringResId: Int, val color: Color) {
LOW_HIGH(R.string.statin_alert_low_high_risk_patient_x, Color(0xFFFF7A00)),
MEDIUM_HIGH(R.string.statin_alert_medium_high_risk_patient_x, Color(0xFFFF7A00)),
HIGH(R.string.statin_alert_high_risk_patient_x, Color(0xFFFF3355));

companion object {
fun compute(cvdRiskRange: CVDRiskRange): CVDRiskLevel {
return when {
cvdRiskRange.min < 5 -> LOW_HIGH
cvdRiskRange.min < 10 -> MEDIUM_HIGH
else -> HIGH
}
}
}
}
6 changes: 6 additions & 0 deletions app/src/main/java/org/simple/clinic/cvdrisk/CVDRiskRange.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ data class CVDRiskRange(
val max: Int,
) : Parcelable {

val level: CVDRiskLevel
get() = CVDRiskLevel.compute(this)

val canPrescribeStatin: Boolean
get() = max >= 10

class RoomTypeConverter {

@TypeConverter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class PatientSummaryEffectHandler @AssistedInject constructor(
return ObservableTransformer { effects ->
effects
.observeOn(schedulersProvider.io())
.flatMap { effect ->
.switchMap { effect ->
val patient = effect.patient
Observable.combineLatest(
medicalHistoryRepository.historyForPatientOrDefault(
Expand Down Expand Up @@ -246,9 +246,8 @@ class PatientSummaryEffectHandler @AssistedInject constructor(
)
val bmiReading = patientAttributeRepository.getPatientAttributeImmediate(patientUuid)
val cvdRisk = cvdRiskRepository.getCVDRiskImmediate(patientUuid)
val canPrescribeStatin = cvdRisk?.riskScore?.let { it.max >= 10 } ?: false
StatinInfoLoaded(StatinInfo(
canPrescribeStatin = canPrescribeStatin,
canPrescribeStatin = cvdRisk?.riskScore?.canPrescribeStatin ?: false,
cvdRisk = cvdRisk?.riskScore,
isSmoker = medicalHistory.isSmoking,
bmiReading = bmiReading?.bmiReading,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ data class PatientSummaryModel(
val scheduledAppointment: ParcelableOptional<Appointment>?,
val hasShownDiagnosisWarningDialog: Boolean,
val statinInfo: StatinInfo?,
val hasShownSmokingStatusDialog: Boolean,
) : Parcelable, PatientSummaryChildModel {

companion object {
Expand All @@ -48,6 +49,7 @@ data class PatientSummaryModel(
scheduledAppointment = null,
hasShownDiagnosisWarningDialog = false,
statinInfo = null,
hasShownSmokingStatusDialog = false,
)
}
}
Expand Down Expand Up @@ -132,4 +134,8 @@ data class PatientSummaryModel(
fun updateStatinInfo(statinInfo: StatinInfo): PatientSummaryModel {
return copy(statinInfo = statinInfo)
}

fun showSmokingStatusDialog(): PatientSummaryModel {
return copy(hasShownSmokingStatusDialog = true)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.CVDRiskLevel
import org.simple.clinic.cvdrisk.StatinInfo
import org.simple.clinic.drugs.DiagnosisWarningPrescriptions
import org.simple.clinic.drugs.PrescribedDrug
Expand All @@ -24,6 +25,7 @@ import org.simple.clinic.summary.OpenIntention.ViewExistingPatient
import org.simple.clinic.summary.OpenIntention.ViewExistingPatientWithTeleconsultLog
import org.simple.clinic.summary.OpenIntention.ViewNewPatient
import java.util.UUID
import org.simple.clinic.medicalhistory.Answer as MedicalHistoryAnswer

class PatientSummaryUpdate(
private val isPatientReassignmentFeatureEnabled: Boolean,
Expand Down Expand Up @@ -145,7 +147,7 @@ class PatientSummaryUpdate(
}

isEligibleForNonLabBasedCvdRisk && shouldCalculateCVDRisk -> {
dispatch(CalculateNonLabBasedCVDRisk(model.patientSummaryProfile!!.patient))
dispatch(CalculateNonLabBasedCVDRisk(model.patientSummaryProfile!!.patient))
}

isEligibleForNonLabBasedCvdRisk -> {
Expand Down Expand Up @@ -179,7 +181,20 @@ class PatientSummaryUpdate(
event: StatinInfoLoaded,
model: PatientSummaryModel
): Next<PatientSummaryModel, PatientSummaryEffect> {
return next(model.updateStatinInfo(event.statinInfo))
val canShowSmokingStatusDialog =
event.statinInfo.canPrescribeStatin &&
(event.statinInfo.cvdRisk?.level == CVDRiskLevel.LOW_HIGH ||
event.statinInfo.cvdRisk?.level == CVDRiskLevel.MEDIUM_HIGH) &&
event.statinInfo.isSmoker == MedicalHistoryAnswer.Unanswered &&
!model.hasShownSmokingStatusDialog
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this works, and we can leave this for now. The underlying problem still exists. The statin info is being reloaded every time patient info changes. So, the LoadStatinPrescriptionCheckInfo effect gets triggered every time.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for flagging this out. I will create a story and move to backlog.


return if (canShowSmokingStatusDialog) {
next(model.updateStatinInfo(event.statinInfo)
.showSmokingStatusDialog(),
ShowSmokingStatusDialog)
} else {
next(model.updateStatinInfo(event.statinInfo))
}
}

private fun hypertensionNotNowClicked(continueToDiabetesDiagnosisWarning: Boolean): Next<PatientSummaryModel, PatientSummaryEffect> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import org.simple.clinic.R
import org.simple.clinic.common.ui.components.FilledButton
import org.simple.clinic.common.ui.theme.SimpleInverseTheme
import org.simple.clinic.common.ui.theme.SimpleTheme
import org.simple.clinic.cvdrisk.CVDRiskLevel
import org.simple.clinic.cvdrisk.CVDRiskRange
import org.simple.clinic.cvdrisk.StatinInfo
import org.simple.clinic.medicalhistory.Answer
Expand Down Expand Up @@ -119,15 +120,10 @@ fun RiskText(
val riskText = when {
hasCVD -> stringResource(R.string.statin_alert_very_high_risk_patient)
cvdRiskRange == null -> stringResource(R.string.statin_alert_at_risk_patient)
cvdRiskRange.min > 10 -> stringResource(R.string.statin_alert_high_risk_patient_x, riskPercentage)
cvdRiskRange.min < 5 -> stringResource(R.string.statin_alert_low_high_risk_patient_x, riskPercentage)
else -> stringResource(R.string.statin_alert_medium_high_risk_patient_x, riskPercentage)
else -> stringResource(cvdRiskRange.level.displayStringResId, riskPercentage)
}

val riskColor = when {
cvdRiskRange == null || cvdRiskRange.min > 10 -> SimpleTheme.colors.material.error
else -> Color(0xFFFF7A00)
}
val riskColor = cvdRiskRange?.level?.color ?: SimpleTheme.colors.material.error

val textMeasurer = rememberTextMeasurer()
val textWidth = textMeasurer.measure(
Expand Down Expand Up @@ -164,11 +160,11 @@ fun RiskProgressBar(
endOffset: Float
) {
val riskColors = listOf(
Color(0xFF00B849), // Very Low
Color(0xFFFFC800), // Low
SimpleTheme.colors.material.error, // Medium
Color(0xFFB81631), // High
Color(0xFF731814) // Very High
Color(0xFF00B849), // Low
Color(0xFFFFC800), // MEDIUM
SimpleTheme.colors.material.error, // HIGH
Color(0xFFB81631), // VERY HIGH
Color(0xFF731814) // CRITICAL
)

val indicatorColor = Color(0xFF2F363D)
Expand Down Expand Up @@ -240,26 +236,28 @@ fun DescriptionText(
statinInfo: StatinInfo
) {
val text = when {
statinInfo.cvdRisk == null ||
statinInfo.cvdRisk.min >= 10 -> stringResource(R.string.statin_alert_refer_to_doctor)
statinInfo.cvdRisk == null || statinInfo.cvdRisk.level == CVDRiskLevel.HIGH ->
stringResource(R.string.statin_alert_refer_to_doctor)

statinInfo.isSmoker == Answer.Unanswered &&
statinInfo.bmiReading == null -> stringResource(R.string.statin_alert_add_smoking_and_bmi_info)
statinInfo.isSmoker == Answer.Unanswered && statinInfo.bmiReading == null ->
stringResource(R.string.statin_alert_add_smoking_and_bmi_info)

statinInfo.isSmoker == Answer.Unanswered &&
statinInfo.bmiReading != null -> stringResource(R.string.statin_alert_add_smoking_info)
statinInfo.isSmoker == Answer.Unanswered && statinInfo.bmiReading != null ->
stringResource(R.string.statin_alert_add_smoking_info)

statinInfo.isSmoker != Answer.Unanswered &&
statinInfo.bmiReading == null -> stringResource(R.string.statin_alert_add_bmi_info)
statinInfo.isSmoker != Answer.Unanswered && statinInfo.bmiReading == null ->
stringResource(R.string.statin_alert_add_bmi_info)

else -> stringResource(R.string.statin_alert_refer_to_doctor)
}.toAnnotatedString()

val textColor = when {
statinInfo.cvdRisk == null ||
statinInfo.cvdRisk.min >= 10 -> SimpleTheme.colors.material.error
statinInfo.cvdRisk == null || statinInfo.cvdRisk.level == CVDRiskLevel.HIGH
-> SimpleTheme.colors.material.error

statinInfo.isSmoker == Answer.Unanswered || statinInfo.bmiReading == null ->
SimpleTheme.colors.onSurface67

statinInfo.isSmoker == Answer.Unanswered || statinInfo.bmiReading == null -> SimpleTheme.colors.onSurface67
else -> SimpleTheme.colors.material.error
}

Expand Down Expand Up @@ -324,11 +322,11 @@ fun StainNudgeAddButtons(

fun getOffsets(cvdRiskRange: CVDRiskRange?, size: Size): Pair<Float, Float> {
val riskRanges = listOf(
0..4, // Very Low
5..9, // Low
10..19, // Medium
20..29, // High
30..33 // Very High
0..4, // LOW
5..9, // MEDIUM
10..19, // HIGH
20..29, // VERY HIGH
30..33 // CRITICAL
)

val startRatio: Float
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2358,6 +2358,23 @@ class PatientSummaryUpdateTest {
))
}

@Test
fun `when statin info is loaded and risk is low-high, then update the state and show smoking status dialog`() {
val statinInfo = StatinInfo(
canPrescribeStatin = true,
CVDRiskRange(7, 14),
)
updateSpec
.given(defaultModel)
.whenEvent(StatinInfoLoaded(
statinInfo = statinInfo
))
.then(assertThatNext(
hasModel(defaultModel.updateStatinInfo(statinInfo).showSmokingStatusDialog()),
hasEffects(ShowSmokingStatusDialog)
))
}

@Test
fun `when add smoking button is clicked, then show the smoking status dialog`() {
updateSpec
Expand Down