Skip to content

Commit 4be148b

Browse files
Pratul KaliaSaket Narayan
authored andcommitted
Add validations for acceptable blood pressure values
1 parent 1186b35 commit 4be148b

File tree

7 files changed

+185
-11
lines changed

7 files changed

+185
-11
lines changed

app/src/main/java/org/simple/clinic/bp/entry/BloodPressureEntrySheet.kt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package org.simple.clinic.bp.entry
22

3+
import android.annotation.SuppressLint
34
import android.app.Activity
45
import android.content.Context
56
import android.content.Intent
67
import android.os.Bundle
8+
import android.view.View
79
import android.view.inputmethod.EditorInfo
810
import android.widget.EditText
11+
import android.widget.TextView
912
import com.jakewharton.rxbinding2.widget.RxTextView
1013
import io.reactivex.Observable
1114
import io.reactivex.android.schedulers.AndroidSchedulers
@@ -27,6 +30,7 @@ class BloodPressureEntrySheet : BottomSheetActivity() {
2730
private val rootLayout by bindView<LinearLayoutWithPreImeKeyEventListener>(R.id.bloodpressureentry_root)
2831
private val systolicEditText by bindView<EditText>(R.id.bloodpressureentry_systolic)
2932
private val diastolicEditText by bindView<EditText>(R.id.bloodpressureentry_diastolic)
33+
private val errorTextView by bindView<TextView>(R.id.bloodpressureentry_error)
3034

3135
private val onDestroys = PublishSubject.create<Any>()
3236

@@ -43,6 +47,7 @@ class BloodPressureEntrySheet : BottomSheetActivity() {
4347
}
4448
}
4549

50+
@SuppressLint("CheckResult")
4651
override fun onCreate(savedInstanceState: Bundle?) {
4752
super.onCreate(savedInstanceState)
4853

@@ -103,4 +108,32 @@ class BloodPressureEntrySheet : BottomSheetActivity() {
103108
finish()
104109
}
105110

111+
fun hideErrorMessage() {
112+
errorTextView.visibility = View.GONE
113+
}
114+
115+
fun showSystolicLessThanDiastolicError() {
116+
errorTextView.text = getString(R.string.bloodpressureentry_error_systolic_more)
117+
errorTextView.visibility = View.VISIBLE
118+
}
119+
120+
fun showSystolicLowError() {
121+
errorTextView.text = getString(R.string.bloodpressureentry_error_systolic_70)
122+
errorTextView.visibility = View.VISIBLE
123+
}
124+
125+
fun showSystolicHighError() {
126+
errorTextView.text = getString(R.string.bloodpressureentry_error_systolic_300)
127+
errorTextView.visibility = View.VISIBLE
128+
}
129+
130+
fun showDiastolicLowError() {
131+
errorTextView.text = getString(R.string.bloodpressureentry_error_diastolic_40)
132+
errorTextView.visibility = View.VISIBLE
133+
}
134+
135+
fun showDiastolicHighError() {
136+
errorTextView.text = getString(R.string.bloodpressureentry_error_diastolic_180)
137+
errorTextView.visibility = View.VISIBLE
138+
}
106139
}

app/src/main/java/org/simple/clinic/bp/entry/BloodPressureEntrySheetController.kt

Lines changed: 67 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ import io.reactivex.rxkotlin.ofType
77
import io.reactivex.rxkotlin.withLatestFrom
88
import org.simple.clinic.ReportAnalyticsEvents
99
import org.simple.clinic.bp.BloodPressureRepository
10+
import org.simple.clinic.bp.entry.BloodPressureEntrySheetController.Validation.ERROR_DIASTOLIC_TOO_HIGH
11+
import org.simple.clinic.bp.entry.BloodPressureEntrySheetController.Validation.ERROR_DIASTOLIC_TOO_LOW
12+
import org.simple.clinic.bp.entry.BloodPressureEntrySheetController.Validation.ERROR_SYSTOLIC_LESS_THAN_DIASTOLIC
13+
import org.simple.clinic.bp.entry.BloodPressureEntrySheetController.Validation.ERROR_SYSTOLIC_TOO_HIGH
14+
import org.simple.clinic.bp.entry.BloodPressureEntrySheetController.Validation.ERROR_SYSTOLIC_TOO_LOW
15+
import org.simple.clinic.bp.entry.BloodPressureEntrySheetController.Validation.SUCCESS
16+
import org.simple.clinic.util.exhaustive
1017
import org.simple.clinic.widgets.UiEvent
1118
import javax.inject.Inject
1219

@@ -18,11 +25,12 @@ class BloodPressureEntrySheetController @Inject constructor(
1825
) : ObservableTransformer<UiEvent, UiChange> {
1926

2027
override fun apply(events: Observable<UiEvent>): ObservableSource<UiChange> {
21-
val replayedEvents = events.compose(ReportAnalyticsEvents()).replay(1).refCount()
28+
val replayedEvents = events.compose(ReportAnalyticsEvents()).replay().refCount()
2229

2330
return Observable.merge(
24-
handleImeOptionClicks(replayedEvents),
25-
systolicTextChanges(replayedEvents))
31+
systolicTextChanges(replayedEvents),
32+
validationErrorResets(replayedEvents),
33+
bpValidationsAndSaves(replayedEvents))
2634
}
2735

2836
private fun systolicTextChanges(events: Observable<UiEvent>): Observable<UiChange> {
@@ -39,7 +47,19 @@ class BloodPressureEntrySheetController @Inject constructor(
3947
|| (systolicText.length == 2 && systolicText.matches("^[3-9].*$".toRegex()))
4048
}
4149

42-
private fun handleImeOptionClicks(events: Observable<UiEvent>): Observable<UiChange> {
50+
private fun validationErrorResets(events: Observable<UiEvent>): Observable<UiChange> {
51+
val systolicChanges = events.ofType<BloodPressureSystolicTextChanged>()
52+
.distinctUntilChanged()
53+
.map { { ui: Ui -> ui.hideErrorMessage() } }
54+
55+
val diastolicChanges = events.ofType<BloodPressureDiastolicTextChanged>()
56+
.distinctUntilChanged()
57+
.map { { ui: Ui -> ui.hideErrorMessage() } }
58+
59+
return Observable.merge(systolicChanges, diastolicChanges)
60+
}
61+
62+
private fun bpValidationsAndSaves(events: Observable<UiEvent>): Observable<UiChange> {
4363
val imeDoneClicks = events.ofType<BloodPressureSaveClicked>()
4464

4565
val patientUuids = events
@@ -54,19 +74,57 @@ class BloodPressureEntrySheetController @Inject constructor(
5474
.ofType<BloodPressureDiastolicTextChanged>()
5575
.map { it.diastolic }
5676

57-
return imeDoneClicks
77+
val triples = imeDoneClicks
5878
.withLatestFrom(patientUuids, systolicChanges, diastolicChanges) { _, uuid, systolic, diastolic -> Triple(uuid, systolic, diastolic) }
59-
.filter { (_, systolic, diastolic) -> isInputValid(systolic, diastolic) }
60-
.take(1)
79+
80+
val errors = triples
81+
.map { (_, systolic, diastolic) ->
82+
{ ui: Ui ->
83+
when (validateInput(systolic, diastolic)) {
84+
ERROR_SYSTOLIC_LESS_THAN_DIASTOLIC -> ui.showSystolicLessThanDiastolicError()
85+
ERROR_SYSTOLIC_TOO_HIGH -> ui.showSystolicHighError()
86+
ERROR_SYSTOLIC_TOO_LOW -> ui.showSystolicLowError()
87+
ERROR_DIASTOLIC_TOO_HIGH -> ui.showDiastolicHighError()
88+
ERROR_DIASTOLIC_TOO_LOW -> ui.showDiastolicLowError()
89+
SUCCESS -> { // Nothing to do here, SUCCESS handled below separately!
90+
}
91+
}.exhaustive()
92+
}
93+
}
94+
95+
val saves = triples
96+
.filter { (_, systolic, diastolic) -> validateInput(systolic, diastolic) == SUCCESS }
97+
.distinctUntilChanged()
6198
.flatMap { (uuid, systolic, diastolic) ->
6299
bloodPressureRepository
63100
.saveMeasurement(uuid, systolic.toInt(), diastolic.toInt())
64101
.toCompletable()
65102
.andThen(Observable.just({ ui: Ui -> ui.setBPSavedResultAndFinish() }))
66103
}
104+
105+
return Observable.merge(errors, saves)
106+
}
107+
108+
private fun validateInput(systolic: String, diastolic: String): Validation {
109+
val systolicNumber = systolic.toInt()
110+
val diastolicNumber = diastolic.toInt()
111+
112+
return when {
113+
systolicNumber < 70 -> ERROR_SYSTOLIC_TOO_LOW
114+
systolicNumber > 300 -> ERROR_SYSTOLIC_TOO_HIGH
115+
diastolicNumber < 40 -> ERROR_DIASTOLIC_TOO_LOW
116+
diastolicNumber > 180 -> ERROR_DIASTOLIC_TOO_HIGH
117+
systolicNumber < diastolicNumber -> ERROR_SYSTOLIC_LESS_THAN_DIASTOLIC
118+
else -> SUCCESS
119+
}
67120
}
68121

69-
private fun isInputValid(systolic: String, diastolic: String): Boolean {
70-
return systolic.length in 2..3 && diastolic.length in 2..3
122+
enum class Validation {
123+
SUCCESS,
124+
ERROR_SYSTOLIC_TOO_HIGH,
125+
ERROR_SYSTOLIC_TOO_LOW,
126+
ERROR_DIASTOLIC_TOO_HIGH,
127+
ERROR_DIASTOLIC_TOO_LOW,
128+
ERROR_SYSTOLIC_LESS_THAN_DIASTOLIC
71129
}
72130
}

app/src/main/java/org/simple/clinic/bp/entry/BloodPressureEntrySheetEvents.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package org.simple.clinic.bp.entry
33
import org.simple.clinic.widgets.UiEvent
44
import java.util.UUID
55

6-
data class BloodPressureEntrySheetCreated(val patientUuid: UUID): UiEvent
6+
data class BloodPressureEntrySheetCreated(val patientUuid: UUID) : UiEvent
77

88
class BloodPressureSystolicTextChanged(val systolic: String) : UiEvent {
99
override val analyticsName = "Blood Pressure Entry:Systolic Text Changed"

app/src/main/res/layout/sheet_blood_pressure_entry.xml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
xmlns:tools="http://schemas.android.com/tools"
55
android:layout_width="match_parent"
66
android:layout_height="match_parent"
7+
android:animateLayoutChanges="true"
78
android:orientation="vertical"
89
android:paddingBottom="@dimen/spacing_24"
910
android:paddingTop="@dimen/spacing_16">
1011

11-
<android.support.v7.widget.AppCompatTextView
12+
<TextView
1213
android:layout_width="match_parent"
1314
android:layout_height="wrap_content"
1415
android:layout_marginBottom="@dimen/spacing_16"
@@ -77,4 +78,14 @@
7778
android:text="@string/bloodpressureentry_diastolic" />
7879
</LinearLayout>
7980
</LinearLayout>
81+
82+
<TextView
83+
android:id="@+id/bloodpressureentry_error"
84+
style="@style/Clinic.V2.TextAppearance.TextInputLayoutError"
85+
android:layout_width="match_parent"
86+
android:layout_height="wrap_content"
87+
android:layout_marginTop="@dimen/spacing_8"
88+
android:gravity="center_horizontal"
89+
android:visibility="gone" />
90+
8091
</org.simple.clinic.bp.entry.LinearLayoutWithPreImeKeyEventListener>

app/src/main/res/values/strings.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@
9595
<string name="bloodpressureentry_systolic">Systolic</string>
9696
<string name="bloodpressureentry_diastolic">Diastolic</string>
9797
<string name="bloodpressureentry_input_hint" translatable="false">•••</string>
98+
<string name="bloodpressureentry_error_systolic_more">Systolic should be more than diastolic</string>
99+
<string name="bloodpressureentry_error_systolic_70">Systolic cannot be less than 70</string>
100+
<string name="bloodpressureentry_error_systolic_300">Systolic cannot be more than 300</string>
101+
<string name="bloodpressureentry_error_diastolic_40">Diastolic cannot be less than 40</string>
102+
<string name="bloodpressureentry_error_diastolic_180">Diastolic cannot be more than 180</string>
98103

99104
<!-- Patient summary -->
100105
<string name="patientsummary_contentdescription_up_button">Go back</string> <!-- TODO: The UP button will probably behave differently for different entry points. Update this CD accordingly. -->

app/src/main/res/values/styles_v2.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
<style name="Clinic.V2.TextAppearance.TextInputLayoutError">
4343
<item name="android:textSize">@dimen/textsize_12</item>
4444
<item name="android:textColor">@color/red1</item>
45+
<item name="android:letterSpacing">0.02</item>
4546
</style>
4647

4748
<style name="Clinic.V2.TextAppearance.Body1Left">

app/src/test/java/org/simple/clinic/bp/entry/BloodPressureEntrySheetControllerTest.kt

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,72 @@ class BloodPressureEntrySheetControllerTest {
3333
.subscribe { uiChange -> uiChange(sheet) }
3434
}
3535

36+
@Test
37+
fun `when systolic is less than diastolic, show error`() {
38+
uiEvents.onNext(BloodPressureEntrySheetCreated(patientUuid))
39+
uiEvents.onNext(BloodPressureSystolicTextChanged("90"))
40+
uiEvents.onNext(BloodPressureDiastolicTextChanged("140"))
41+
uiEvents.onNext(BloodPressureSaveClicked())
42+
43+
verify(bloodPressureRepository, never()).saveMeasurement(any(), any(), any())
44+
verify(sheet).showSystolicLessThanDiastolicError()
45+
}
46+
47+
@Test
48+
fun `when systolic is less than minimum possible, show error`() {
49+
uiEvents.onNext(BloodPressureEntrySheetCreated(patientUuid))
50+
uiEvents.onNext(BloodPressureSystolicTextChanged("55"))
51+
uiEvents.onNext(BloodPressureDiastolicTextChanged("55"))
52+
uiEvents.onNext(BloodPressureSaveClicked())
53+
54+
verify(bloodPressureRepository, never()).saveMeasurement(any(), any(), any())
55+
verify(sheet).showSystolicLowError()
56+
}
57+
58+
@Test
59+
fun `when systolic is more than maximum possible, show error`() {
60+
uiEvents.onNext(BloodPressureEntrySheetCreated(patientUuid))
61+
uiEvents.onNext(BloodPressureSystolicTextChanged("333"))
62+
uiEvents.onNext(BloodPressureDiastolicTextChanged("88"))
63+
uiEvents.onNext(BloodPressureSaveClicked())
64+
65+
verify(bloodPressureRepository, never()).saveMeasurement(any(), any(), any())
66+
verify(sheet).showSystolicHighError()
67+
}
68+
69+
@Test
70+
fun `when diastolic is less than minimum possible, show error`() {
71+
uiEvents.onNext(BloodPressureEntrySheetCreated(patientUuid))
72+
uiEvents.onNext(BloodPressureSystolicTextChanged("110"))
73+
uiEvents.onNext(BloodPressureDiastolicTextChanged("33"))
74+
uiEvents.onNext(BloodPressureSaveClicked())
75+
76+
verify(bloodPressureRepository, never()).saveMeasurement(any(), any(), any())
77+
verify(sheet).showDiastolicLowError()
78+
}
79+
80+
@Test
81+
fun `when diastolic is more than maximum possible, show error`() {
82+
uiEvents.onNext(BloodPressureEntrySheetCreated(patientUuid))
83+
uiEvents.onNext(BloodPressureSystolicTextChanged("233"))
84+
uiEvents.onNext(BloodPressureDiastolicTextChanged("190"))
85+
uiEvents.onNext(BloodPressureSaveClicked())
86+
87+
verify(bloodPressureRepository, never()).saveMeasurement(any(), any(), any())
88+
verify(sheet).showDiastolicHighError()
89+
}
90+
91+
@Test
92+
fun `when systolic or diastolic values change, hide the error message`() {
93+
uiEvents.onNext(BloodPressureSystolicTextChanged("12"))
94+
uiEvents.onNext(BloodPressureSystolicTextChanged("120"))
95+
uiEvents.onNext(BloodPressureSystolicTextChanged("130"))
96+
uiEvents.onNext(BloodPressureDiastolicTextChanged("90"))
97+
uiEvents.onNext(BloodPressureDiastolicTextChanged("99"))
98+
99+
verify(sheet, times(5)).hideErrorMessage()
100+
}
101+
36102
@Test
37103
fun `when save is clicked but input is invalid then blood pressure measurement should not be saved`() {
38104
uiEvents.onNext(BloodPressureEntrySheetCreated(patientUuid))

0 commit comments

Comments
 (0)