Merge pull request #3092 from Jackenmen/handle_sensor_start_time_in_xdrip_broadcast

Add support for SensorStartedAt extra in xDrip+ BG source
This commit is contained in:
Milos Kozak 2023-12-07 15:50:52 +01:00 committed by GitHub
commit c972b7ed02
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 135 additions and 6 deletions

View file

@ -25,6 +25,7 @@ interface Intents {
const val EXTRA_BG_SLOPE = "com.eveningoutpost.dexdrip.Extras.BgSlope" const val EXTRA_BG_SLOPE = "com.eveningoutpost.dexdrip.Extras.BgSlope"
const val EXTRA_BG_SLOPE_NAME = "com.eveningoutpost.dexdrip.Extras.BgSlopeName" const val EXTRA_BG_SLOPE_NAME = "com.eveningoutpost.dexdrip.Extras.BgSlopeName"
const val EXTRA_SENSOR_BATTERY = "com.eveningoutpost.dexdrip.Extras.SensorBattery" const val EXTRA_SENSOR_BATTERY = "com.eveningoutpost.dexdrip.Extras.SensorBattery"
const val EXTRA_SENSOR_STARTED_AT = "com.eveningoutpost.dexdrip.Extras.SensorStartedAt"
const val EXTRA_TIMESTAMP = "com.eveningoutpost.dexdrip.Extras.Time" const val EXTRA_TIMESTAMP = "com.eveningoutpost.dexdrip.Extras.Time"
const val EXTRA_RAW = "com.eveningoutpost.dexdrip.Extras.Raw" const val EXTRA_RAW = "com.eveningoutpost.dexdrip.Extras.Raw"
const val XDRIP_DATA_SOURCE_DESCRIPTION = "com.eveningoutpost.dexdrip.Extras.SourceDesc" const val XDRIP_DATA_SOURCE_DESCRIPTION = "com.eveningoutpost.dexdrip.Extras.SourceDesc"

View file

@ -22,6 +22,8 @@ dependencies {
implementation(project(":core:ui")) implementation(project(":core:ui"))
implementation(project(":core:utils")) implementation(project(":core:utils"))
testImplementation(Libs.AndroidX.Work.testing)
testImplementation(project(":shared:tests")) testImplementation(project(":shared:tests"))
kapt(Libs.Dagger.compiler) kapt(Libs.Dagger.compiler)

View file

@ -1,20 +1,28 @@
package app.aaps.plugins.source package app.aaps.plugins.source
import android.content.Context import android.content.Context
import android.os.Bundle
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import androidx.work.workDataOf import androidx.work.workDataOf
import app.aaps.core.interfaces.logging.AAPSLogger import app.aaps.core.interfaces.logging.AAPSLogger
import app.aaps.core.interfaces.logging.LTag import app.aaps.core.interfaces.logging.LTag
import app.aaps.core.interfaces.logging.UserEntryLogger
import app.aaps.core.interfaces.plugin.PluginBase import app.aaps.core.interfaces.plugin.PluginBase
import app.aaps.core.interfaces.plugin.PluginDescription import app.aaps.core.interfaces.plugin.PluginDescription
import app.aaps.core.interfaces.plugin.PluginType import app.aaps.core.interfaces.plugin.PluginType
import app.aaps.core.interfaces.receivers.Intents import app.aaps.core.interfaces.receivers.Intents
import app.aaps.core.interfaces.resources.ResourceHelper import app.aaps.core.interfaces.resources.ResourceHelper
import app.aaps.core.interfaces.sharedPreferences.SP
import app.aaps.core.interfaces.source.BgSource import app.aaps.core.interfaces.source.BgSource
import app.aaps.core.interfaces.source.XDripSource import app.aaps.core.interfaces.source.XDripSource
import app.aaps.core.interfaces.utils.DateUtil
import app.aaps.core.interfaces.utils.T
import app.aaps.core.main.utils.worker.LoggingWorker import app.aaps.core.main.utils.worker.LoggingWorker
import app.aaps.core.utils.receivers.DataWorkerStorage import app.aaps.core.utils.receivers.DataWorkerStorage
import app.aaps.database.entities.GlucoseValue import app.aaps.database.entities.GlucoseValue
import app.aaps.database.entities.UserEntry.Action
import app.aaps.database.entities.UserEntry.Sources
import app.aaps.database.entities.ValueWithUnit
import app.aaps.database.impl.AppRepository import app.aaps.database.impl.AppRepository
import app.aaps.database.impl.transactions.CgmSourceTransaction import app.aaps.database.impl.transactions.CgmSourceTransaction
import app.aaps.database.transactions.TransactionGlucoseValue import app.aaps.database.transactions.TransactionGlucoseValue
@ -22,6 +30,7 @@ import dagger.android.HasAndroidInjector
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
import kotlin.math.abs
import kotlin.math.round import kotlin.math.round
@Singleton @Singleton
@ -34,7 +43,7 @@ class XdripSourcePlugin @Inject constructor(
.mainType(PluginType.BGSOURCE) .mainType(PluginType.BGSOURCE)
.fragmentClass(BGSourceFragment::class.java.name) .fragmentClass(BGSourceFragment::class.java.name)
.pluginIcon((app.aaps.core.main.R.drawable.ic_blooddrop_48)) .pluginIcon((app.aaps.core.main.R.drawable.ic_blooddrop_48))
.preferencesId(R.xml.pref_bgsource) .preferencesId(R.xml.pref_dexcom)
.pluginName(R.string.source_xdrip) .pluginName(R.string.source_xdrip)
.description(R.string.description_source_xdrip), .description(R.string.description_source_xdrip),
aapsLogger, rh, injector aapsLogger, rh, injector
@ -63,8 +72,25 @@ class XdripSourcePlugin @Inject constructor(
) : LoggingWorker(context, params, Dispatchers.IO) { ) : LoggingWorker(context, params, Dispatchers.IO) {
@Inject lateinit var xdripSourcePlugin: XdripSourcePlugin @Inject lateinit var xdripSourcePlugin: XdripSourcePlugin
@Inject lateinit var sp: SP
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
@Inject lateinit var dataWorkerStorage: DataWorkerStorage @Inject lateinit var dataWorkerStorage: DataWorkerStorage
@Inject lateinit var uel: UserEntryLogger
fun getSensorStartTime(bundle: Bundle): Long? {
val now = dateUtil.now()
var sensorStartTime: Long? = if (sp.getBoolean(R.string.key_dexcom_log_ns_sensor_change, false)) {
bundle.getLong(Intents.EXTRA_SENSOR_STARTED_AT, 0)
} else {
null
}
// check start time validity
sensorStartTime?.let {
if (abs(it - now) > T.months(1).msecs() || it > now) sensorStartTime = null
}
return sensorStartTime
}
override suspend fun doWorkAndLog(): Result { override suspend fun doWorkAndLog(): Result {
var ret = Result.success() var ret = Result.success()
@ -86,17 +112,27 @@ class XdripSourcePlugin @Inject constructor(
?: "" ?: ""
) )
) )
repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)) val sensorStartTime = getSensorStartTime(bundle)
repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), sensorStartTime))
.doOnError { .doOnError {
aapsLogger.error(LTag.DATABASE, "Error while saving values from Xdrip", it) aapsLogger.error(LTag.DATABASE, "Error while saving values from Xdrip", it)
ret = Result.failure(workDataOf("Error" to it.toString())) ret = Result.failure(workDataOf("Error" to it.toString()))
} }
.blockingGet() .blockingGet()
.also { savedValues -> .also { result ->
savedValues.all().forEach { result.all().forEach {
xdripSourcePlugin.detectSource(it) xdripSourcePlugin.detectSource(it)
aapsLogger.debug(LTag.DATABASE, "Inserted bg $it") aapsLogger.debug(LTag.DATABASE, "Inserted bg $it")
} }
result.sensorInsertionsInserted.forEach {
uel.log(
Action.CAREPORTAL,
Sources.Xdrip,
ValueWithUnit.Timestamp(it.timestamp),
ValueWithUnit.TherapyEventType(it.type)
)
aapsLogger.debug(LTag.DATABASE, "Inserted sensor insertion $it")
}
} }
xdripSourcePlugin.sensorBatteryLevel = bundle.getInt(Intents.EXTRA_SENSOR_BATTERY, -1) xdripSourcePlugin.sensorBatteryLevel = bundle.getInt(Intents.EXTRA_SENSOR_BATTERY, -1)
return ret return ret

View file

@ -1,25 +1,115 @@
package app.aaps.plugins.source package app.aaps.plugins.source
import android.content.Context
import android.os.Bundle
import androidx.work.WorkerFactory
import androidx.work.WorkerParameters
import androidx.work.testing.TestListenableWorkerBuilder
import app.aaps.core.interfaces.receivers.Intents
import app.aaps.core.interfaces.resources.ResourceHelper import app.aaps.core.interfaces.resources.ResourceHelper
import app.aaps.core.interfaces.utils.DateUtil
import app.aaps.core.interfaces.utils.T
import app.aaps.core.interfaces.sharedPreferences.SP
import app.aaps.core.utils.receivers.DataWorkerStorage
import app.aaps.shared.impl.utils.DateUtilImpl
import app.aaps.shared.tests.BundleMock
import app.aaps.shared.tests.TestBase import app.aaps.shared.tests.TestBase
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import dagger.android.AndroidInjector import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito.`when`
class XdripSourcePluginTest : TestBase() { class XdripSourcePluginTest : TestBase() {
private lateinit var xdripSourcePlugin: XdripSourcePlugin abstract class ContextWithInjector : Context(), HasAndroidInjector
private lateinit var xdripSourcePlugin: XdripSourcePlugin
private lateinit var dateUtil: DateUtil
private lateinit var dataWorkerStorage: DataWorkerStorage
private val injector = HasAndroidInjector {
AndroidInjector {
if (it is XdripSourcePlugin.XdripSourceWorker) {
it.dataWorkerStorage = dataWorkerStorage
it.dateUtil = dateUtil
it.sp = sp
}
}
}
@Mock lateinit var sp: SP
@Mock lateinit var rh: ResourceHelper @Mock lateinit var rh: ResourceHelper
@Mock lateinit var context: ContextWithInjector
@BeforeEach @BeforeEach
fun setup() { fun setup() {
xdripSourcePlugin = XdripSourcePlugin({ AndroidInjector { } }, rh, aapsLogger) `when`(context.applicationContext).thenReturn(context)
`when`(context.androidInjector()).thenReturn(injector.androidInjector())
xdripSourcePlugin = XdripSourcePlugin(injector, rh, aapsLogger)
dateUtil = DateUtilImpl(context)
dataWorkerStorage = DataWorkerStorage(context)
}
private fun prepareWorker(
sensorStartTime: Long? = dateUtil.now(),
logNsSensorChange: Boolean = true,
): Pair<Bundle, XdripSourcePlugin.XdripSourceWorker> {
val bundle = BundleMock.mock()
sensorStartTime?.let { bundle.putLong(Intents.EXTRA_SENSOR_STARTED_AT, sensorStartTime) }
`when`(sp.getBoolean(R.string.key_dexcom_log_ns_sensor_change, false)).thenReturn(logNsSensorChange)
lateinit var worker: XdripSourcePlugin.XdripSourceWorker
TestListenableWorkerBuilder<XdripSourcePlugin.XdripSourceWorker>(context)
.setWorkerFactory(object: WorkerFactory() {
override fun createWorker(appContext: Context, workerClassName: String, workerParameters: WorkerParameters): XdripSourcePlugin.XdripSourceWorker {
worker = XdripSourcePlugin.XdripSourceWorker(context, workerParameters)
return worker
}
})
.setInputData(dataWorkerStorage.storeInputData(bundle, Intents.ACTION_NEW_BG_ESTIMATE)).build()
return Pair(bundle, worker)
} }
@Test fun advancedFilteringSupported() { @Test fun advancedFilteringSupported() {
assertThat(xdripSourcePlugin.advancedFilteringSupported()).isFalse() assertThat(xdripSourcePlugin.advancedFilteringSupported()).isFalse()
} }
@Test fun getSensorStartTime_withoutValue_returnsNull() {
val (bundle, worker) = prepareWorker(sensorStartTime = null)
val result = worker.getSensorStartTime(bundle)
assertThat(result).isNull()
}
@Test fun getSensorStartTime_withSettingDisabled_returnsNull() {
val sensorStartTime = dateUtil.now()
val (bundle, worker) = prepareWorker(sensorStartTime = sensorStartTime, logNsSensorChange = false)
val result = worker.getSensorStartTime(bundle)
assertThat(result).isNull()
}
@Test fun getSensorStartTime_withRecentValue_returnsStartTime() {
val sensorStartTime = dateUtil.now()
val (bundle, worker) = prepareWorker(sensorStartTime = sensorStartTime)
val result = worker.getSensorStartTime(bundle)
assertThat(result).isEqualTo(sensorStartTime)
}
@Test fun getSensorStartTime_withOldValue_returnsNull() {
val sensorStartTime = dateUtil.now() - T.months(2).msecs()
val (bundle, worker) = prepareWorker(sensorStartTime = sensorStartTime)
val result = worker.getSensorStartTime(bundle)
assertThat(result).isNull()
}
} }