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: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@
- Add `cholesterol_value` to `MedicalHistory` table
- Bump AGP to v8.8.1
- Fix JSON variable name in non-lab based statin calculation sheet
- Add `LabBasedCVDRiskCalculator` and effect to calculate lab based cvd risk.
- Add `LabBasedCVDRiskCalculator` and effect to calculate lab based cvd risk
- Add `CholesterolEntrySheet`

### Fixes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import org.simple.clinic.security.pin.PinEntryCardView
import org.simple.clinic.settings.SettingsScreen
import org.simple.clinic.settings.changelanguage.ChangeLanguageScreen
import org.simple.clinic.summary.PatientSummaryScreen
import org.simple.clinic.summary.addcholesterol.CholesterolEntrySheet
import org.simple.clinic.summary.addphone.AddPhoneNumberDialog
import org.simple.clinic.summary.assignedfacility.AssignedFacilityView
import org.simple.clinic.summary.bloodpressures.view.BloodPressureSummaryViewInjector
Expand Down Expand Up @@ -169,7 +170,8 @@ interface TheActivityComponent :
MonthlyReportsScreen.Injector,
MonthlyReportCompleteScreen.Injector,
ReassignPatientSheet.Injector,
BMIEntrySheet.Injector {
BMIEntrySheet.Injector,
CholesterolEntrySheet.Injector {
fun inject(target: TheActivity)

@Subcomponent.Factory
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ data class MedicalHistory(
}
}

fun cholesterolChanged(cholesterol: Float): MedicalHistory {
return copy(cholesterol = cholesterol)
}

@Dao
interface RoomDao {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.simple.clinic.summary.addcholesterol

import java.util.UUID

sealed interface CholesterolEntryEffect

data class SaveCholesterol(val patientUuid: UUID, val cholesterolValue: Float) : CholesterolEntryEffect

sealed interface CholesterolEntryViewEffect : CholesterolEntryEffect

data object HideCholesterolErrorMessage : CholesterolEntryViewEffect

data object ShowReqMinCholesterolValidationError : CholesterolEntryViewEffect

data object ShowReqMaxCholesterolValidationError : CholesterolEntryViewEffect

data object DismissSheet : CholesterolEntryViewEffect
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.simple.clinic.summary.addcholesterol

import com.spotify.mobius.functions.Consumer
import com.spotify.mobius.rx2.RxMobius
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import io.reactivex.ObservableTransformer
import org.simple.clinic.medicalhistory.MedicalHistoryRepository
import org.simple.clinic.util.UtcClock
import org.simple.clinic.util.scheduler.SchedulersProvider
import org.simple.clinic.uuid.UuidGenerator
import java.time.Instant

class CholesterolEntryEffectHandler @AssistedInject constructor(
private val clock: UtcClock,
private val uuidGenerator: UuidGenerator,
private val medicalHistoryRepository: MedicalHistoryRepository,
private val schedulersProvider: SchedulersProvider,
@Assisted private val viewEffectConsumer: Consumer<CholesterolEntryViewEffect>
) {

@AssistedFactory
interface Factory {
fun create(viewEffectConsumer: Consumer<CholesterolEntryViewEffect>): CholesterolEntryEffectHandler
}

fun build(): ObservableTransformer<CholesterolEntryEffect, CholesterolEntryEvent> {
return RxMobius
.subtypeEffectHandler<CholesterolEntryEffect, CholesterolEntryEvent>()
.addTransformer(SaveCholesterol::class.java, saveCholesterol())
.addConsumer(CholesterolEntryViewEffect::class.java, viewEffectConsumer::accept)
.build()
}

private fun saveCholesterol(): ObservableTransformer<SaveCholesterol, CholesterolEntryEvent> {
return ObservableTransformer { effects ->
effects
.observeOn(schedulersProvider.io())
.map { effect ->
val medicalHistory = medicalHistoryRepository.historyForPatientOrDefaultImmediate(
defaultHistoryUuid = uuidGenerator.v4(),
patientUuid = effect.patientUuid
)

medicalHistoryRepository.save(
medicalHistory.cholesterolChanged(effect.cholesterolValue),
Instant.now(clock),
)
}
.map { CholesterolSaved }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.simple.clinic.summary.addcholesterol

import org.simple.clinic.widgets.UiEvent

sealed interface CholesterolEntryEvent : UiEvent

data class CholesterolChanged(val cholesterolValue: Float) : CholesterolEntryEvent

data object SaveClicked : CholesterolEntryEvent {
override val analyticsName: String = "Cholesterol Entry:Save Clicked"
}

data object CholesterolSaved : CholesterolEntryEvent

data object KeyboardClosed : CholesterolEntryEvent
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.simple.clinic.summary.addcholesterol

import android.os.Parcelable
import kotlinx.parcelize.Parcelize
import org.simple.clinic.summary.addcholesterol.CholesterolEntrySaveState.NOT_SAVING_CHOLESTEROL
import org.simple.clinic.summary.addcholesterol.CholesterolEntrySaveState.SAVING_CHOLESTEROL
import java.util.UUID

@Parcelize
data class CholesterolEntryModel(
val patientUUID: UUID,
val cholesterolValue: Float,
val cholesterolSaveState: CholesterolEntrySaveState,
) : Parcelable {

companion object {
fun create(patientUUID: UUID): CholesterolEntryModel {
return CholesterolEntryModel(
patientUUID = patientUUID,
cholesterolValue = 0f,
cholesterolSaveState = NOT_SAVING_CHOLESTEROL,
)
}
}

fun cholesterolChanged(cholesterolValue: Float): CholesterolEntryModel {
return copy(cholesterolValue = cholesterolValue)
}

fun savingCholesterol(): CholesterolEntryModel {
return copy(cholesterolSaveState = SAVING_CHOLESTEROL)
}

fun cholesterolSaved(): CholesterolEntryModel {
return copy(cholesterolSaveState = NOT_SAVING_CHOLESTEROL)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.simple.clinic.summary.addcholesterol

enum class CholesterolEntrySaveState {
SAVING_CHOLESTEROL,
NOT_SAVING_CHOLESTEROL
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package org.simple.clinic.summary.addcholesterol

import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import androidx.fragment.app.Fragment
import com.jakewharton.rxbinding3.widget.editorActions
import com.spotify.mobius.Update
import com.spotify.mobius.functions.Consumer
import io.reactivex.Observable
import io.reactivex.ObservableTransformer
import io.reactivex.rxkotlin.cast
import kotlinx.parcelize.Parcelize
import org.simple.clinic.R
import org.simple.clinic.ReportAnalyticsEvents
import org.simple.clinic.databinding.SheetCholesterolEntryBinding
import org.simple.clinic.di.injector
import org.simple.clinic.mobius.ViewEffectsHandler
import org.simple.clinic.mobius.ViewRenderer
import org.simple.clinic.navigation.v2.Router
import org.simple.clinic.navigation.v2.ScreenKey
import org.simple.clinic.navigation.v2.fragments.BaseBottomSheet
import org.simple.clinic.widgets.UiEvent
import org.simple.clinic.widgets.textChanges
import org.simple.clinic.widgets.visibleOrGone
import java.util.UUID
import javax.inject.Inject

class CholesterolEntrySheet : BaseBottomSheet<
CholesterolEntrySheet.Key,
SheetCholesterolEntryBinding,
CholesterolEntryModel,
CholesterolEntryEvent,
CholesterolEntryEffect,
CholesterolEntryViewEffect>(), CholesterolEntryUi, CholesterolEntryUiActions {

@Inject
lateinit var cholesterolEntryEffectHandlerFactory: CholesterolEntryEffectHandler.Factory

@Inject
lateinit var router: Router

private val rootLayout
get() = binding.rootLayout

private val cholesterolTextField
get() = binding.cholesterolTextField

private val cholesterolErrorTextView
get() = binding.cholesterolErrorTextView

private val progressIndicator
get() = binding.progressLoader

override fun onAttach(context: Context) {
super.onAttach(context)
context.injector<Injector>().inject(this)
}

override fun defaultModel() = CholesterolEntryModel.create(
patientUUID = screenKey.patientUuid,
)

override fun bindView(inflater: LayoutInflater, container: ViewGroup?) = SheetCholesterolEntryBinding
.inflate(inflater, container, false)

override fun createUpdate(): Update<CholesterolEntryModel, CholesterolEntryEvent, CholesterolEntryEffect> {
return CholesterolEntryUpdate()
}

override fun createEffectHandler(viewEffectsConsumer: Consumer<CholesterolEntryViewEffect>): ObservableTransformer<CholesterolEntryEffect, CholesterolEntryEvent> {
return cholesterolEntryEffectHandlerFactory.create(viewEffectsConsumer).build()
}

override fun viewEffectsHandler(): ViewEffectsHandler<CholesterolEntryViewEffect> {
return CholesterolEntryViewEffectHandler(this)
}

override fun uiRenderer(): ViewRenderer<CholesterolEntryModel> {
return CholesterolEntryUiRenderer(this)
}

override fun events() = Observable
.mergeArray(
hardwareBackPresses(),
cholesterolTextChanges(),
imeDoneClicks(),
)
.compose(ReportAnalyticsEvents())
.cast<CholesterolEntryEvent>()


override fun hideErrorMessage() {
cholesterolErrorTextView.visibleOrGone(isVisible = false)
}

override fun dismissSheet() {
router.pop()
}

override fun showReqMaxCholesterolError() {
showCholesterolErrorMessage(getString(R.string.cholesterol_error_upper_limit))
}

override fun showReqMinCholesterolError() {
showCholesterolErrorMessage(getString(R.string.cholesterol_error_lower_limit))
}

override fun showProgress() {
progressIndicator.visibleOrGone(isVisible = true)
cholesterolTextField.visibleOrGone(isVisible = false)
}

override fun hideProgress() {
progressIndicator.visibleOrGone(isVisible = false)
cholesterolTextField.visibleOrGone(isVisible = true)
}

private fun showCholesterolErrorMessage(message: String) {
with(cholesterolErrorTextView) {
text = message
visibility = android.view.View.VISIBLE
}
}

private fun hardwareBackPresses(): Observable<UiEvent> {
return Observable.create { emitter ->
val interceptor = {
emitter.onNext(KeyboardClosed)
}
emitter.setCancellable { rootLayout.backKeyPressInterceptor = null }
rootLayout.backKeyPressInterceptor = interceptor
}
}

private fun cholesterolTextChanges() = cholesterolTextField
.textChanges { CholesterolChanged(it.toFloatOrNull() ?: 0f) }

private fun imeDoneClicks(): Observable<SaveClicked> {
return cholesterolTextField
.editorActions { actionId -> actionId == EditorInfo.IME_ACTION_DONE }
.map { SaveClicked }
}

@Parcelize
data class Key(
val patientUuid: UUID,
override val analyticsName: String = "Cholesterol Entry Sheet",
override val type: ScreenType = ScreenType.Modal,
) : ScreenKey() {

override fun instantiateFragment(): Fragment {
return CholesterolEntrySheet()
}
}

interface Injector {
fun inject(target: CholesterolEntrySheet)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.simple.clinic.summary.addcholesterol

interface CholesterolEntryUi {
fun showProgress()
fun hideProgress()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.simple.clinic.summary.addcholesterol

interface CholesterolEntryUiActions {
fun hideErrorMessage()
fun dismissSheet()
fun showReqMaxCholesterolError()
fun showReqMinCholesterolError()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.simple.clinic.summary.addcholesterol

import org.simple.clinic.mobius.ViewRenderer

class CholesterolEntryUiRenderer(
private val ui: CholesterolEntryUi
) : ViewRenderer<CholesterolEntryModel> {

override fun render(model: CholesterolEntryModel) {
when (model.cholesterolSaveState) {
CholesterolEntrySaveState.SAVING_CHOLESTEROL -> ui.showProgress()
CholesterolEntrySaveState.NOT_SAVING_CHOLESTEROL -> ui.hideProgress()
}
}
}
Loading
Loading