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 @@ -6,6 +6,7 @@

- Handle window insets when displaying app content in edge-to-edge
- Enable edge-to-edge support on all versions of Android
- Handle nullable inputs when removing last chip in the `ChipInputAutoCompleteTextView`

## 2025.05.20

Expand Down
5 changes: 4 additions & 1 deletion app/src/main/java/org/simple/clinic/cvdrisk/CVDRiskRange.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ data class CVDRiskRange(
val level: CVDRiskLevel
get() = CVDRiskLevel.compute(this)

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

val canPrescribeLabBasedStatin: Boolean
get() = max >= 20

class RoomTypeConverter {

@TypeConverter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class PatientSummaryUpdate(
private val isLabBasedStatinNudgeEnabled: Boolean,
private val minAgeForStatin: Int = 40,
private val maxAgeForCVDRisk: Int = 74,
private val minReqMaxRiskRangeForLabBasedNudge: Int = 10,
private val minReqMaxRiskRangeForLabBasedNudge: Int = 20,
) : Update<PatientSummaryModel, PatientSummaryEvent, PatientSummaryEffect> {

override fun update(
Expand Down Expand Up @@ -319,9 +319,9 @@ class PatientSummaryUpdate(
val bmiReading = event.bmiReading
val calculatedRiskRange = event.riskRange
val canPrescribeStatin = if (isLabBasedStatinNudgeEnabled) {
checkIfLabBasedNudgeCanBeShown(event.medicalHistory, event.riskRange)
calculatedRiskRange?.canPrescribeLabBasedStatin ?: false
} else {
calculatedRiskRange?.canPrescribeStatin ?: false
calculatedRiskRange?.canPrescribeNonLabBasedStatin ?: false
}

val canShowSmokingStatusDialog = canPrescribeStatin &&
Expand Down Expand Up @@ -353,14 +353,6 @@ class PatientSummaryUpdate(
}
}

private fun checkIfLabBasedNudgeCanBeShown(
medicalHistory: MedicalHistory,
riskRange: CVDRiskRange?
): Boolean {
val maxRiskRange = riskRange?.max ?: 0
return !(medicalHistory.diagnosedWithDiabetes != Yes && maxRiskRange < minReqMaxRiskRangeForLabBasedNudge)
}

private fun labBasedRiskRange(calculatedRiskRange: CVDRiskRange?): CVDRiskRange? {
if (calculatedRiskRange == null) return null

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ fun StatinNudge(
Spacer(modifier = Modifier.height(16.dp))
DescriptionText(
isLabBasedStatinNudgeEnabled = isLabBasedStatinNudgeEnabled,
isNonLabBasedStatinNudgeEnabled = isNonLabBasedStatinNudgeEnabled,
statinInfo = statinInfo
)

Expand Down Expand Up @@ -262,17 +263,22 @@ fun RiskProgressBar(
fun DescriptionText(
statinInfo: StatinInfo,
isLabBasedStatinNudgeEnabled: Boolean,
isNonLabBasedStatinNudgeEnabled: Boolean,
) {
val text = descriptionText(
isLabBasedStatinNudgeEnabled = isLabBasedStatinNudgeEnabled,
isNonLabBasedStatinNudgeEnabled = isNonLabBasedStatinNudgeEnabled,
statinInfo = statinInfo,
)

val textColor = when {
statinInfo.cvdRisk == null || statinInfo.cvdRisk.level == CVDRiskLevel.HIGH || statinInfo.hasDiabetes
-> SimpleTheme.colors.material.error
statinInfo.hasDiabetes || statinInfo.hasCVD -> SimpleTheme.colors.material.error
isLabBasedStatinNudgeEnabled && (statinInfo.cvdRisk == null || statinInfo.cvdRisk.level == CVDRiskLevel.VERY_HIGH) -> SimpleTheme.colors.material.error
isNonLabBasedStatinNudgeEnabled && (statinInfo.cvdRisk == null || statinInfo.cvdRisk.level == CVDRiskLevel.HIGH) -> SimpleTheme.colors.material.error
isLabBasedStatinNudgeEnabled && (statinInfo.isSmoker == Answer.Unanswered || statinInfo.cholesterol == null) ->
SimpleTheme.colors.onSurface67

statinInfo.isSmoker == Answer.Unanswered || statinInfo.bmiReading == null || statinInfo.cholesterol == null ->
isNonLabBasedStatinNudgeEnabled && (statinInfo.isSmoker == Answer.Unanswered || statinInfo.bmiReading == null) ->
SimpleTheme.colors.onSurface67

else -> SimpleTheme.colors.material.error
Expand All @@ -294,46 +300,67 @@ fun DescriptionText(
@Composable
@ReadOnlyComposable
private fun descriptionText(
isNonLabBasedStatinNudgeEnabled: Boolean,
isLabBasedStatinNudgeEnabled: Boolean,
statinInfo: StatinInfo
): AnnotatedString {
val maxCvdRiskRange = statinInfo.cvdRisk?.max ?: 0

return when {
statinInfo.hasCVD -> stringResource(R.string.statin_alert_refer_to_doctor)

statinInfo.hasDiabetes && statinInfo.age >= MIN_AGE_FOR_STATIN && isLabBasedStatinNudgeEnabled.not() ->
statinInfo.hasDiabetes && statinInfo.age >= MIN_AGE_FOR_STATIN ->
stringResource(R.string.statin_alert_refer_to_doctor_diabetic_40)

statinInfo.hasDiabetes && statinInfo.age >= MIN_AGE_FOR_STATIN && isLabBasedStatinNudgeEnabled && maxCvdRiskRange < LAB_BASED_MIN_REQ_MAX_RISK_RANGE ->
stringResource(R.string.statin_alert_refer_to_doctor_diabetic_40)
isLabBasedStatinNudgeEnabled -> labBasedDescriptionText(statinInfo)
isNonLabBasedStatinNudgeEnabled -> nonLabBasedDescriptionText(statinInfo)

statinInfo.cvdRisk == null || statinInfo.cvdRisk.level == CVDRiskLevel.HIGH ->
stringResource(R.string.statin_alert_refer_to_doctor)
else -> stringResource(R.string.statin_alert_refer_to_doctor)
}.toAnnotatedString()
}

statinInfo.hasDiabetes && isLabBasedStatinNudgeEnabled ->
stringResource(R.string.statin_alert_refer_to_doctor_diabetic)
@Composable
@ReadOnlyComposable
private fun labBasedDescriptionText(
statinInfo: StatinInfo
): String {
return when {

statinInfo.isSmoker == Answer.Unanswered && statinInfo.bmiReading == null && isLabBasedStatinNudgeEnabled.not() ->
stringResource(R.string.statin_alert_add_smoking_and_bmi_info)
statinInfo.cvdRisk == null || statinInfo.cvdRisk.level == CVDRiskLevel.VERY_HIGH ->
stringResource(R.string.statin_alert_refer_to_doctor)

statinInfo.isSmoker == Answer.Unanswered && statinInfo.cholesterol == null ->
stringResource(R.string.statin_alert_add_smoking_and_cholesterol_info)

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

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

statinInfo.isSmoker == Answer.Unanswered && statinInfo.cholesterol == null && isLabBasedStatinNudgeEnabled ->
stringResource(R.string.statin_alert_add_smoking_and_cholesterol_info)
else -> stringResource(R.string.statin_alert_refer_to_doctor)
}
}

@Composable
@ReadOnlyComposable
private fun nonLabBasedDescriptionText(
statinInfo: StatinInfo
): String {
return when {

statinInfo.isSmoker == Answer.Unanswered && statinInfo.cholesterol != null && isLabBasedStatinNudgeEnabled ->
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_info)

statinInfo.isSmoker != Answer.Unanswered && statinInfo.cholesterol == null && isLabBasedStatinNudgeEnabled ->
stringResource(R.string.statin_alert_add_cholesterol_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()
}
}

@Composable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ class ChipInputAutoCompleteTextView(
}

private fun removeLastChip() {
val lastInput = inputs.last()
val lastInput = inputs.lastOrNull() ?: return
val lastChip = rootView
.children
.filterIsInstance<Chip>()
Expand Down
1 change: 0 additions & 1 deletion app/src/main/res/values-si-rLK/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -926,7 +926,6 @@
<string name="statin_alert_at_risk_patient">අවදානමක් සහිත රෝගීන්</string>
<string name="statin_alert_refer_to_doctor">&lt;b&gt;ස්ටැටින් ප්‍රතිකාර&lt;/b&gt; සදහා වෛද්‍යවරයා වෙත යොමු කරන්න</string>
<string name="statin_alert_refer_to_doctor_diabetic_40"> වයස අවුරුදු 40 ට වැඩි, දියවැඩියා රෝගියෙකු නම් ස්ටැටින් ඖෂධ සඳහා වෛද්‍යවරයා වෙත යොමු කරන්න</string>
<string name="statin_alert_refer_to_doctor_diabetic"> දියවැඩියා රෝගියෙකු නම් ස්ටැටින් ඖෂධ සඳහා වෛද්‍යවරයා වෙත යොමු කරන්න</string>
<string name="statin_alert_add_smoking">දුම් පානය</string>
<string name="statin_alert_add_bmi">BMI අගය</string>
<string name="statin_alert_add_cholesterol">කොලෙස්ටරෝල් අගය එකතු කරන්න</string>
Expand Down
1 change: 0 additions & 1 deletion app/src/main/res/values-ta-rLK/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -925,7 +925,6 @@
<string name="statin_alert_at_risk_patient">ஆபத்தில் உள்ள நோயாளி</string>
<string name="statin_alert_refer_to_doctor">&lt;b&gt;ஸ்டேடின் மருந்துக்காக&lt;/b&gt; மருத்துவரை அணுகவும்</string>
<string name="statin_alert_refer_to_doctor_diabetic_40">நீரிழிவு நோயாளி, 40 வயதுக்கு மேற்பட்டவர் எனின் ஸ்டேடின்(statin) மருந்துக்கு மருத்துவரை நாடவும்</string>
<string name="statin_alert_refer_to_doctor_diabetic"> நீரிழிவு நோயாளி எனின் ஸ்டேடின் மருந்துக்கு மருத்துவரைப் நாடவும்</string>
<string name="statin_alert_add_smoking">புகைத்தலைச் சேர்க்க</string>
<string name="statin_alert_add_bmi">உடற் திணிவுச் சுட்டியினை சேர்க்க</string>
<string name="statin_alert_add_cholesterol">கொலஸ்ட்ரோலின் அளவைக் கூட்டுக</string>
Expand Down
1 change: 0 additions & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1093,7 +1093,6 @@ The Simple app contains private health information of patients (“Data”).\n\n
<string name="statin_alert_at_risk_patient">AT RISK PATIENT</string>
<string name="statin_alert_refer_to_doctor">Refer to doctor for &lt;b&gt;statin medicine&lt;/b&gt;</string>
<string name="statin_alert_refer_to_doctor_diabetic_40">Diabetic patient, age above 40. Refer to doctor for &lt;b&gt;statin medicine&lt;/b&gt;</string>
<string name="statin_alert_refer_to_doctor_diabetic">Diabetic patient. Refer to doctor for &lt;b&gt;statin medicine&lt;/b&gt;</string>
<string name="statin_alert_add_smoking">Add smoking</string>
<string name="statin_alert_add_bmi">Add BMI</string>
<string name="statin_alert_add_cholesterol">Add cholesterol</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2377,7 +2377,7 @@ class PatientSummaryUpdateTest {
fun `when statin info is loaded, then update the state`() {
val statinInfo = StatinInfo(
canShowStatinNudge = true,
cvdRisk = CVDRiskRange(11, 11),
cvdRisk = CVDRiskRange(11, 27),
isSmoker = Yes,
bmiReading = BMIReading(165f, 60f),
hasCVD = true,
Expand All @@ -2397,7 +2397,7 @@ class PatientSummaryUpdateTest {
isSmoking = Yes,
cholesterol = null,
),
riskRange = CVDRiskRange(11, 11),
riskRange = CVDRiskRange(11, 27),
bmiReading = BMIReading(165f, 60f),
))
.then(assertThatNext(
Expand Down Expand Up @@ -2445,45 +2445,6 @@ class PatientSummaryUpdateTest {
))
}

@Test
fun `when statin info is loaded and lab-based statin is enabled and has diabetes, then statin can be prescribed`() {
val statinInfo = StatinInfo(
canShowStatinNudge = true,
cvdRisk = null,
isSmoker = Yes,
bmiReading = BMIReading(165f, 60f),
hasCVD = true,
hasDiabetes = true,
age = 55,
cholesterol = null,
)
val updateSpec = UpdateSpec(PatientSummaryUpdate(
isPatientReassignmentFeatureEnabled = true,
isPatientStatinNudgeV1Enabled = true,
isNonLabBasedStatinNudgeEnabled = true,
isLabBasedStatinNudgeEnabled = true,
))

updateSpec
.given(defaultModel)
.whenEvent(StatinInfoLoaded(
age = 55,
medicalHistory = TestData.medicalHistory(
hasHadStroke = Yes,
hasHadHeartAttack = Yes,
hasDiabetes = Yes,
isSmoking = Yes,
cholesterol = null,
),
riskRange = CVDRiskRange(9, 9),
bmiReading = BMIReading(165f, 60f),
))
.then(assertThatNext(
hasModel(defaultModel.updateStatinInfo(statinInfo)),
hasNoEffects()
))
}

@Test
fun `when statin info is loaded and lab-based statin is enabled and has diabetes and max risk is less than 10, then statin cannot be prescribed`() {
val statinInfo = StatinInfo(
Expand Down Expand Up @@ -2527,7 +2488,7 @@ class PatientSummaryUpdateTest {
fun `when statin info is loaded and risk is low-high, then update the state and show smoking status dialog`() {
val statinInfo = StatinInfo(
canShowStatinNudge = true,
cvdRisk = CVDRiskRange(4, 11),
cvdRisk = CVDRiskRange(4, 27),
isSmoker = Unanswered,
bmiReading = BMIReading(165f, 60f),
hasCVD = true,
Expand All @@ -2546,7 +2507,7 @@ class PatientSummaryUpdateTest {
isSmoking = Unanswered,
cholesterol = null,
),
riskRange = CVDRiskRange(4, 11),
riskRange = CVDRiskRange(4, 27),
bmiReading = BMIReading(165f, 60f),
))
.then(assertThatNext(
Expand Down
Loading