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

## Next Release

### Changes
- Add CVD risk view to patient summary screen behind feature flag `show_cvd_risk`

## 2026.06.25

### Changes
Expand Down
8 changes: 8 additions & 0 deletions app/src/main/java/org/simple/clinic/cvdrisk/CVDRisk.kt
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,14 @@ data class CVDRisk(
@Query("SELECT uuid FROM CVDRisk WHERE syncStatus = :syncStatus")
fun recordIdsWithSyncStatus(syncStatus: SyncStatus): List<UUID>

@Query("""
SELECT * FROM CVDRisk
WHERE patientUuid = :patientUuid AND deletedAt IS NULL
ORDER BY updatedAt DESC
LIMIT 1
""")
fun cvdRisk(patientUuid: UUID): Flowable<CVDRisk>

@Query("""
SELECT * FROM CVDRisk
WHERE patientUuid = :patientUuid AND deletedAt IS NULL
Expand Down
30 changes: 25 additions & 5 deletions app/src/main/java/org/simple/clinic/cvdrisk/CVDRiskLevel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,31 @@ 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)),
VERY_HIGH(R.string.statin_alert_very_high_risk_range, Color(0xFFFF3355));
enum class CVDRiskLevel(
val displayStringResId: Int,
val shortLabelResId: Int,
val color: Color
) {
LOW_HIGH(
R.string.statin_alert_low_high_risk_patient_x,
R.string.cvd_risk_low_high,
Color(0xFFFF7A00)
),
MEDIUM_HIGH(
R.string.statin_alert_medium_high_risk_patient_x,
R.string.cvd_risk_medium_high,
Color(0xFFFF7A00)
),
HIGH(
R.string.statin_alert_high_risk_patient_x,
R.string.cvd_risk_high,
Color(0xFFFF3355)
),
VERY_HIGH(
R.string.statin_alert_very_high_risk_range,
R.string.cvd_risk_very_high,
Color(0xFFFF3355)
);

companion object {
fun compute(cvdRiskRange: CVDRiskRange): CVDRiskLevel {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ class CVDRiskRepository @Inject constructor(
return dao.cvdRiskImmediate(patientUuid)
}

fun getCVDRisk(patientUuid: UUID): Observable<CVDRisk> {
return dao.cvdRisk(patientUuid).toObservable()
}

fun recordsWithSyncStatus(syncStatus: SyncStatus): List<CVDRisk> {
return dao.recordsWithSyncStatus(syncStatus)
}
Expand Down
1 change: 1 addition & 0 deletions app/src/main/java/org/simple/clinic/feature/Feature.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ enum class Feature(
SortOverdueBasedOnReturnScore(false, "sort_overdue_based_on_return_score"),
ShowReturnScoreDebugValues(false, "show_return_score_debug_values"),
ShowBMIContainer(false, "show_bmi_container"),
ShowCVDRisk(false, "show_cvd_risk"),
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package org.simple.clinic.medicalhistory.ui
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Card
Expand Down Expand Up @@ -31,7 +32,7 @@ fun BMIContainer(
onAddOrClick: () -> Unit,
) {
val isBmiRecorded = bmiReading?.calculateBMI() != null
Card(modifier = modifier) {
Card(modifier = modifier.fillMaxWidth()) {
Column(
modifier = Modifier.padding(
start = dimensionResource(R.dimen.spacing_16),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ data class UpdateCVDRisk(

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

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

data class UpdateTobaccoUse(
val patientId: UUID,
val isSmoker: MedicalHistoryAnswer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ class PatientSummaryEffectHandler @AssistedInject constructor(
.addTransformer(UpdateCVDRisk::class.java, updateCVDRisk())
.addTransformer(SaveCVDRisk::class.java, saveCVDRisk())
.addTransformer(LoadStatinInfo::class.java, loadStatinInfo())
.addTransformer(LoadCVDRiskInfo::class.java, loadCVDRiskInfo())
.addConsumer(UpdateTobaccoUse::class.java, { updateTobaccoUse(it.patientId, it.isSmoker, it.isUsingSmokelessTobacco) }, schedulersProvider.io())
.addTransformer(CreateNewBMIEntry::class.java, createNewBMIEntry())
.build()
Expand Down Expand Up @@ -177,6 +178,31 @@ class PatientSummaryEffectHandler @AssistedInject constructor(
}
}

private fun loadCVDRiskInfo(): ObservableTransformer<LoadCVDRiskInfo, PatientSummaryEvent> {
return ObservableTransformer { effects ->
effects
.observeOn(schedulersProvider.io())
.switchMap { effect ->
val patientUuid = effect.patientUuid
Observable.combineLatest(
medicalHistoryRepository.historyForPatientOrDefault(
defaultHistoryUuid = uuidGenerator.v4(),
patientUuid = patientUuid
),
prescriptionRepository.newestPrescriptionsForPatient(patientUuid),
cvdRiskRepository.getCVDRisk(patientUuid)
) { medicalHistory, prescriptions, cvdRisk ->

CVDRiskInfoLoaded(
medicalHistory = medicalHistory,
prescriptions = prescriptions,
cvdRiskRange = cvdRisk.riskScore,
)
}
}
}
}

private fun calculateNonLabBasedCVDRisk(): ObservableTransformer<CalculateNonLabBasedCVDRisk, PatientSummaryEvent> {
return ObservableTransformer { effects ->
effects
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,12 @@ data class StatinPrescriptionCheckInfoLoaded(
val wasCVDCalculatedWithin90Days: Boolean,
) : PatientSummaryEvent()

data class CVDRiskInfoLoaded(
val medicalHistory: MedicalHistory,
val prescriptions: List<PrescribedDrug>,
val cvdRiskRange: CVDRiskRange?,
) : PatientSummaryEvent()

data class CVDRiskCalculated(
val oldRisk: CVDRisk?,
val newRiskRange: CVDRiskRange?
Expand Down
20 changes: 15 additions & 5 deletions app/src/main/java/org/simple/clinic/summary/PatientSummaryModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import org.simple.clinic.overdue.Appointment
import org.simple.clinic.patient.PatientStatus
import org.simple.clinic.patientattribute.BMIReading
import org.simple.clinic.summary.teleconsultation.sync.MedicalOfficer
import org.simple.clinic.summary.ui.CVDRiskInfo
import org.simple.clinic.user.User
import org.simple.clinic.util.ParcelableOptional
import org.simple.clinic.util.parcelable
Expand All @@ -31,8 +32,9 @@ data class PatientSummaryModel(
val hasShownDiagnosisWarningDialog: Boolean,
val statinInfo: StatinInfo?,
val hasShownTobaccoUseDialog: Boolean,
val showBMIContainer: Boolean,
val bmiReading: BMIReading?
val showBMIView: Boolean,
val bmiReading: BMIReading?,
val cVDRiskInfo: CVDRiskInfo?,
) : Parcelable, PatientSummaryChildModel {

companion object {
Expand All @@ -53,8 +55,9 @@ data class PatientSummaryModel(
hasShownDiagnosisWarningDialog = false,
statinInfo = null,
hasShownTobaccoUseDialog = false,
showBMIContainer = false,
bmiReading = null
showBMIView = false,
bmiReading = null,
cVDRiskInfo = null
)
}
}
Expand Down Expand Up @@ -92,6 +95,9 @@ data class PatientSummaryModel(
val hasStatinInfoLoaded: Boolean
get() = statinInfo != null

val hasCVDRiskInfoLoaded: Boolean
get() = cVDRiskInfo != null

override fun readyToRender(): Boolean {
return hasLoadedPatientSummaryProfile && hasLoadedCurrentFacility && hasPatientRegistrationData != null
}
Expand Down Expand Up @@ -145,11 +151,15 @@ data class PatientSummaryModel(
}

fun bmiVisibilityUpdated(isEnabled: Boolean): PatientSummaryModel {
return copy(showBMIContainer = isEnabled)
return copy(showBMIView = isEnabled)
}

fun bmiReadingsLoaded(bmiReading: BMIReading?): PatientSummaryModel {
return copy(bmiReading = bmiReading)
}

fun cvdRiskInfoLoaded(cVDRiskInfo: CVDRiskInfo): PatientSummaryModel {
return copy(cVDRiskInfo = cVDRiskInfo)
}

}
53 changes: 39 additions & 14 deletions app/src/main/java/org/simple/clinic/summary/PatientSummaryScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
Expand Down Expand Up @@ -70,6 +72,8 @@ import org.simple.clinic.summary.clinicaldecisionsupport.ui.ClinicalDecisionHigh
import org.simple.clinic.summary.compose.StatinNudge
import org.simple.clinic.summary.linkId.LinkIdWithPatientSheet.LinkIdWithPatientSheetKey
import org.simple.clinic.summary.teleconsultation.contactdoctor.ContactDoctorSheet
import org.simple.clinic.summary.ui.CVDRiskInfo
import org.simple.clinic.summary.ui.CVDRiskView
import org.simple.clinic.summary.ui.PatientSummaryToolbar
import org.simple.clinic.summary.updatephone.UpdatePhoneNumberDialog
import org.simple.clinic.teleconsultlog.teleconsultrecord.screen.TeleconsultRecordScreenKey
Expand Down Expand Up @@ -143,8 +147,8 @@ class PatientSummaryScreen :
private val facilityNameAndDateTextView
get() = binding.facilityNameAndDateTextView

private val bmiContainerComposeView
get() = binding.bmiContainerComposeView
private val bottomComposeView
get() = binding.bottomComposeView

private val labelRegistered
get() = binding.labelRegistered
Expand Down Expand Up @@ -202,6 +206,9 @@ class PatientSummaryScreen :
private var animateClinicalDecisionSupportAlert by mutableStateOf(false)

private var bmiReading by mutableStateOf<BMIReading?>(null)
private var showBMIView by mutableStateOf(false)

private var cvdRiskInfo by mutableStateOf(CVDRiskInfo.default())

override fun defaultModel(): PatientSummaryModel {
return PatientSummaryModel.from(screenKey.intention, screenKey.patientUuid)
Expand Down Expand Up @@ -250,6 +257,7 @@ class PatientSummaryScreen :
isPatientStatinNudgeV1Enabled = features.isEnabled(Feature.PatientStatinNudge),
isNonLabBasedStatinNudgeEnabled = features.isEnabled(Feature.NonLabBasedStatinNudge),
isLabBasedStatinNudgeEnabled = features.isEnabled(Feature.LabBasedStatinNudge),
isCVDRiskViewEnabled = features.isEnabled(Feature.ShowCVDRisk),
)
}

Expand Down Expand Up @@ -341,14 +349,27 @@ class PatientSummaryScreen :
}
}

bmiContainerComposeView.setContent {
bottomComposeView.setContent {
SimpleTheme {
BMIContainer(
bmiReading = bmiReading,
onAddOrClick = {
additionalEvents.notify(AddBMIClicked)
}
)
Column(
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_8))
) {
if (showBMIView) {
BMIContainer(
modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.spacing_8)),
bmiReading = bmiReading,
onAddOrClick = {
additionalEvents.notify(AddBMIClicked)
}
)
}
if (cvdRiskInfo.canShowCVDRisk) {
CVDRiskView(
modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.spacing_8)),
cvdRiskInfo = cvdRiskInfo
)
}
}
}
}
}
Expand Down Expand Up @@ -842,17 +863,21 @@ class PatientSummaryScreen :
animateClinicalDecisionSupportAlert = false
}

override fun updateStatinAlert(statinInfo: StatinInfo) {
override fun updateStatinNudge(statinInfo: StatinInfo) {
this.statinInfo = statinInfo
}

override fun showBMIContainer(bmiReading: BMIReading?) {
bmiContainerComposeView.visibility = VISIBLE
override fun showBMIView(bmiReading: BMIReading?) {
showBMIView = true
this.bmiReading = bmiReading
}

override fun hideBMIContainer() {
bmiContainerComposeView.visibility = GONE
override fun hideBMIView() {
showBMIView = false
}

override fun updateCVDRiskView(cvdRiskInfo: CVDRiskInfo) {
this.cvdRiskInfo = cvdRiskInfo
}

override fun showReassignPatientWarningSheet(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package org.simple.clinic.summary

import org.simple.clinic.cvdrisk.StatinInfo
import org.simple.clinic.patientattribute.BMIReading
import org.simple.clinic.summary.ui.CVDRiskInfo

interface PatientSummaryScreenUi {
fun renderPatientSummaryToolbar(patientSummaryProfile: PatientSummaryProfile)
Expand All @@ -21,7 +22,9 @@ interface PatientSummaryScreenUi {
fun showClinicalDecisionSupportAlert()
fun hideClinicalDecisionSupportAlert()
fun hideClinicalDecisionSupportAlertWithoutAnimation()
fun updateStatinAlert(statinInfo: StatinInfo)
fun showBMIContainer(bmiReading: BMIReading?)
fun hideBMIContainer()
fun updateStatinNudge(statinInfo: StatinInfo)
fun showBMIView(bmiReading: BMIReading?)
fun hideBMIView()

fun updateCVDRiskView(cvdRiskInfo: CVDRiskInfo)
}
Loading
Loading