Skip to content

Commit

Permalink
Add PatientAttributeRepository
Browse files Browse the repository at this point in the history
  • Loading branch information
Siddharth Agarwal committed Dec 9, 2024
1 parent 1bfe2a8 commit 3d1b8ac
Show file tree
Hide file tree
Showing 9 changed files with 288 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import org.simple.clinic.patient.PatientRepositoryAndroidTest
import org.simple.clinic.patient.download.PatientLineListCsvGeneratorTest
import org.simple.clinic.patient.download.PatientLineListDownloaderTest
import org.simple.clinic.patient.onlinelookup.api.LookupPatientOnlineApiIntegrationTest
import org.simple.clinic.patientattribute.PatientAttributeRepositoryAndroidTest
import org.simple.clinic.protocolv2.ProtocolRepositoryAndroidTest
import org.simple.clinic.protocolv2.sync.ProtocolSyncAndroidTest
import org.simple.clinic.rules.LocalAuthenticationRule
Expand Down Expand Up @@ -162,4 +163,5 @@ interface TestAppComponent {
fun inject(target: QuestionnaireResponseRepositoryAndroidTest)
fun inject(target: QuestionnaireResponseSyncIntegrationTest)
fun inject(target: DatabaseEncryptorTest)
fun inject(target: PatientAttributeRepositoryAndroidTest)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.simple.clinic.patientattribute

import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
import org.simple.clinic.TestClinicApp
import org.simple.clinic.rules.LocalAuthenticationRule
import org.simple.clinic.rules.SaveDatabaseRule
import org.simple.sharedTestCode.TestData
import org.simple.sharedTestCode.util.Rules
import javax.inject.Inject

class PatientAttributeRepositoryAndroidTest {

@Inject
lateinit var repository: PatientAttributeRepository

@Inject
lateinit var testData: TestData

@get:Rule
val rules: RuleChain = Rules
.global()
.around(LocalAuthenticationRule())
.around(SaveDatabaseRule())

@Before
fun setup() {
TestClinicApp.appComponent().inject(this)
}

@Test
fun saving_a_patient_attribute_should_work_correctly() {
//given
val bmiReading = BMIReading(height = "177", weight = "64")
val patientAttribute = testData.patientAttribute(reading = bmiReading)

//when
repository.save(listOf(patientAttribute))

//then
val savedPatientAttribute = repository.getPatientAttribute(patientAttribute.patientUuid)
assertThat(savedPatientAttribute).isEqualTo(patientAttribute)
}
}
3 changes: 3 additions & 0 deletions app/src/main/java/org/simple/clinic/AppDatabase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@ abstract class AppDatabase : RoomDatabase() {

abstract fun questionnaireResponseDao(): QuestionnaireResponse.RoomDao

abstract fun patientAttributeDao(): PatientAttribute.RoomDao

fun clearAppData() {
runInTransaction {
patientDao().clear()
Expand All @@ -218,6 +220,7 @@ abstract class AppDatabase : RoomDatabase() {
callResultDao().clear()
questionnaireDao().clear()
questionnaireResponseDao().clear()
patientAttributeDao().clear()
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
package org.simple.clinic.patientattribute

import android.os.Parcelable
import androidx.room.Dao
import androidx.room.Embedded
import androidx.room.Entity
import androidx.room.Index
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.PrimaryKey
import androidx.room.Query
import androidx.room.RawQuery
import androidx.sqlite.db.SimpleSQLiteQuery
import io.reactivex.Flowable
import kotlinx.parcelize.Parcelize
import org.simple.clinic.patient.SyncStatus
import org.simple.clinic.storage.Timestamps
Expand Down Expand Up @@ -32,4 +39,54 @@ data class PatientAttribute(
val timestamps: Timestamps,

val syncStatus: SyncStatus
) : Parcelable
) : Parcelable {

@Dao
interface RoomDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun save(patientAttributes: List<PatientAttribute>)

@Query("UPDATE PatientAttribute SET syncStatus = :to WHERE syncStatus = :from")
fun updateSyncStatus(from: SyncStatus, to: SyncStatus)

@RawQuery
fun updateSyncStatusForIdsRaw(query: SimpleSQLiteQuery): Int

fun updateSyncStatusForIds(ids: List<UUID>, to: SyncStatus) {
updateSyncStatusForIdsRaw(SimpleSQLiteQuery(
"UPDATE PatientAttribute SET syncStatus = '$to' WHERE uuid IN (${ids.joinToString(prefix = "'", postfix = "'", separator = "','")})"
))
}

@Query("SELECT COUNT(uuid) FROM PatientAttribute")
fun count(): Flowable<Int>

@Query("SELECT COUNT(uuid) FROM PatientAttribute WHERE syncStatus = :syncStatus")
fun countWithStatus(syncStatus: SyncStatus): Flowable<Int>

@Query("""
SELECT * FROM PatientAttribute
WHERE syncStatus = :syncStatus
LIMIT :limit OFFSET :offset
""")
fun recordsWithSyncStatusBatched(
syncStatus: SyncStatus,
limit: Int,
offset: Int
): List<PatientAttribute>

@Query("SELECT uuid FROM PatientAttribute WHERE syncStatus = :syncStatus")
fun recordIdsWithSyncStatus(syncStatus: SyncStatus): List<UUID>

@Query("""
SELECT * FROM PatientAttribute
WHERE patientUuid = :patientUuid AND deletedAt IS NULL
ORDER BY updatedAt DESC
LIMIT 1
""")
fun patientImmediate(patientUuid: UUID): PatientAttribute?

@Query("DELETE FROM PatientAttribute")
fun clear()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.simple.clinic.patientattribute

import dagger.Module
import dagger.Provides
import org.simple.clinic.AppDatabase

@Module
class PatientAttributeModule {
@Provides
fun dao(appDatabase: AppDatabase): PatientAttribute.RoomDao {
return appDatabase.patientAttributeDao()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package org.simple.clinic.patientattribute

import io.reactivex.Completable
import io.reactivex.Observable
import org.simple.clinic.di.AppScope
import org.simple.clinic.patient.SyncStatus
import org.simple.clinic.patient.SyncStatus.PENDING
import org.simple.clinic.patientattribute.sync.PatientAttributePayload
import org.simple.clinic.storage.Timestamps
import org.simple.clinic.sync.SynceableRepository
import org.simple.clinic.user.User
import org.simple.clinic.util.UtcClock
import java.util.UUID
import javax.inject.Inject

@AppScope
class PatientAttributeRepository @Inject constructor(
val dao: PatientAttribute.RoomDao,
private val utcClock: UtcClock
) : SynceableRepository<PatientAttribute, PatientAttributePayload> {
fun save(
reading: BMIReading,
patientUuid: UUID,
loggedInUser: User,
uuid: UUID,
): Completable {
val patientAttribute = PatientAttribute(
uuid = uuid,
patientUuid = patientUuid,
userUuid = loggedInUser.uuid,
reading = reading,
timestamps = Timestamps.create(utcClock),
syncStatus = PENDING
)
return Completable.fromAction { save(listOf(patientAttribute)) }
}

override fun save(records: List<PatientAttribute>) {
dao.save(records)
}

override fun setSyncStatus(from: SyncStatus, to: SyncStatus) {
dao.updateSyncStatus(from, to)
}

override fun setSyncStatus(ids: List<UUID>, to: SyncStatus) {
if (ids.isEmpty()) {
throw AssertionError()
}

dao.updateSyncStatusForIds(ids, to)
}

override fun recordCount(): Observable<Int> = dao.count().toObservable()

override fun pendingSyncRecordCount(): Observable<Int> =
dao.countWithStatus(PENDING).toObservable()

override fun pendingSyncRecords(limit: Int, offset: Int): List<PatientAttribute> {
return dao
.recordsWithSyncStatusBatched(
syncStatus = PENDING,
limit = limit,
offset = offset
)
}

override fun mergeWithLocalData(payloads: List<PatientAttributePayload>) {
val dirtyRecords = dao.recordIdsWithSyncStatus(PENDING)

val payloadsToSave = payloads
.filterNot { it.uuid in dirtyRecords }
.map { it.toDatabaseModel(SyncStatus.DONE) }

dao.save(payloadsToSave)
}

fun getPatientAttribute(patientUuid: UUID): PatientAttribute? {
return dao.patientImmediate(patientUuid)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.simple.clinic.patientattribute.sync

import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import org.simple.clinic.patient.SyncStatus
import org.simple.clinic.patientattribute.BMIReading
import org.simple.clinic.patientattribute.PatientAttribute
import org.simple.clinic.storage.Timestamps
import java.time.Instant
import java.util.UUID

@JsonClass(generateAdapter = false)
data class PatientAttributePayload(
@Json(name = "id")
val uuid: UUID,

@Json(name = "height")
val height: Float,

@Json(name = "weight")
val weight: Float,

@Json(name = "patient_id")
val patientUuid: UUID,

@Json(name = "user_id")
val userUuid: UUID,

@Json(name = "created_at")
val createdAt: Instant,

@Json(name = "updated_at")
val updatedAt: Instant,

@Json(name = "deleted_at")
val deletedAt: Instant?,
) {

fun toDatabaseModel(syncStatus: SyncStatus) = PatientAttribute(
uuid = uuid,
patientUuid = patientUuid,
userUuid = userUuid,
reading = BMIReading(
height = height.toString(),
weight = weight.toString()
),
timestamps = Timestamps(
createdAt = createdAt,
updatedAt = updatedAt,
deletedAt = deletedAt
),
syncStatus = syncStatus,
)
}
8 changes: 5 additions & 3 deletions app/src/main/java/org/simple/clinic/sync/SyncModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ import org.simple.clinic.main.TypedPreference.Type.FacilitySyncGroupSwitchedAt
import org.simple.clinic.medicalhistory.MedicalHistoryModule
import org.simple.clinic.medicalhistory.MedicalHistoryRepository
import org.simple.clinic.medicalhistory.sync.MedicalHistorySync
import org.simple.clinic.questionnaire.di.QuestionnaireModule
import org.simple.clinic.questionnaireresponse.di.QuestionnaireResponseModule
import org.simple.clinic.overdue.AppointmentModule
import org.simple.clinic.overdue.AppointmentRepository
import org.simple.clinic.overdue.AppointmentSync
Expand All @@ -38,9 +36,12 @@ import org.simple.clinic.overdue.download.di.OverdueListDownloadModule
import org.simple.clinic.patient.PatientRepository
import org.simple.clinic.patient.sync.PatientSync
import org.simple.clinic.patient.sync.PatientSyncModule
import org.simple.clinic.patientattribute.PatientAttributeModule
import org.simple.clinic.protocol.ProtocolModule
import org.simple.clinic.protocol.sync.ProtocolSync
import org.simple.clinic.questionnaire.di.QuestionnaireModule
import org.simple.clinic.questionnaire.sync.QuestionnaireSync
import org.simple.clinic.questionnaireresponse.di.QuestionnaireResponseModule
import org.simple.clinic.questionnaireresponse.sync.QuestionnaireResponseSync
import org.simple.clinic.reports.ReportsModule
import org.simple.clinic.reports.ReportsSync
Expand Down Expand Up @@ -68,7 +69,8 @@ import javax.inject.Named
CallResultModule::class,
OverdueListDownloadModule::class,
QuestionnaireModule::class,
QuestionnaireResponseModule::class
QuestionnaireResponseModule::class,
PatientAttributeModule::class,
])
class SyncModule {

Expand Down
25 changes: 25 additions & 0 deletions sharedTestCode/src/main/java/org/simple/sharedTestCode/TestData.kt
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ import org.simple.clinic.patient.sync.BusinessIdPayload
import org.simple.clinic.patient.sync.PatientAddressPayload
import org.simple.clinic.patient.sync.PatientPayload
import org.simple.clinic.patient.sync.PatientPhoneNumberPayload
import org.simple.clinic.patientattribute.BMIReading
import org.simple.clinic.patientattribute.PatientAttribute
import org.simple.clinic.protocol.Protocol
import org.simple.clinic.protocol.ProtocolDrug
import org.simple.clinic.protocol.sync.ProtocolDrugPayload
Expand Down Expand Up @@ -1647,4 +1649,27 @@ object TestData {
"monthly_screening_reports.is_smoking" to true,
)
}

fun patientAttribute(
uuid: UUID = UUID.randomUUID(),
patientUuid: UUID = UUID.randomUUID(),
userUuid: UUID = UUID.randomUUID(),
reading: BMIReading,
syncStatus: SyncStatus = randomOfEnum(SyncStatus::class),
createdAt: Instant = Instant.now(),
updatedAt: Instant = Instant.now(),
deletedAt: Instant? = null
): PatientAttribute {
return PatientAttribute(
uuid = uuid,
patientUuid = patientUuid,
userUuid = userUuid,
reading = reading,
timestamps = Timestamps(
createdAt, updatedAt, deletedAt
),
syncStatus = syncStatus
)

}
}

0 comments on commit 3d1b8ac

Please sign in to comment.