From faeddef68edcab3749c595f3732e1b4de4a54d4d Mon Sep 17 00:00:00 2001 From: Jakub Kuczys Date: Wed, 15 Nov 2023 06:14:05 +0100 Subject: [PATCH 1/4] Add support for SensorStartedAt extra in xDrip+ BG source --- .../aaps/core/interfaces/receivers/Intents.kt | 1 + .../aaps/plugins/source/XdripSourcePlugin.kt | 30 +++++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/core/interfaces/src/main/kotlin/app/aaps/core/interfaces/receivers/Intents.kt b/core/interfaces/src/main/kotlin/app/aaps/core/interfaces/receivers/Intents.kt index fbcfca67e6..c4d74505a0 100644 --- a/core/interfaces/src/main/kotlin/app/aaps/core/interfaces/receivers/Intents.kt +++ b/core/interfaces/src/main/kotlin/app/aaps/core/interfaces/receivers/Intents.kt @@ -25,6 +25,7 @@ interface Intents { const val EXTRA_BG_SLOPE = "com.eveningoutpost.dexdrip.Extras.BgSlope" 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_STARTED_AT = "com.eveningoutpost.dexdrip.Extras.SensorStartedAt" const val EXTRA_TIMESTAMP = "com.eveningoutpost.dexdrip.Extras.Time" const val EXTRA_RAW = "com.eveningoutpost.dexdrip.Extras.Raw" const val XDRIP_DATA_SOURCE_DESCRIPTION = "com.eveningoutpost.dexdrip.Extras.SourceDesc" diff --git a/plugins/source/src/main/kotlin/app/aaps/plugins/source/XdripSourcePlugin.kt b/plugins/source/src/main/kotlin/app/aaps/plugins/source/XdripSourcePlugin.kt index 7bcdddc2d0..1942e424be 100644 --- a/plugins/source/src/main/kotlin/app/aaps/plugins/source/XdripSourcePlugin.kt +++ b/plugins/source/src/main/kotlin/app/aaps/plugins/source/XdripSourcePlugin.kt @@ -5,6 +5,7 @@ import androidx.work.WorkerParameters import androidx.work.workDataOf import app.aaps.core.interfaces.logging.AAPSLogger 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.PluginDescription import app.aaps.core.interfaces.plugin.PluginType @@ -12,9 +13,14 @@ import app.aaps.core.interfaces.receivers.Intents import app.aaps.core.interfaces.resources.ResourceHelper import app.aaps.core.interfaces.source.BgSource 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.utils.receivers.DataWorkerStorage 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.transactions.CgmSourceTransaction import app.aaps.database.transactions.TransactionGlucoseValue @@ -22,6 +28,7 @@ import dagger.android.HasAndroidInjector import kotlinx.coroutines.Dispatchers import javax.inject.Inject import javax.inject.Singleton +import kotlin.math.abs import kotlin.math.round @Singleton @@ -63,8 +70,10 @@ class XdripSourcePlugin @Inject constructor( ) : LoggingWorker(context, params, Dispatchers.IO) { @Inject lateinit var xdripSourcePlugin: XdripSourcePlugin + @Inject lateinit var dateUtil: DateUtil @Inject lateinit var repository: AppRepository @Inject lateinit var dataWorkerStorage: DataWorkerStorage + @Inject lateinit var uel: UserEntryLogger override suspend fun doWorkAndLog(): Result { var ret = Result.success() @@ -86,17 +95,32 @@ class XdripSourcePlugin @Inject constructor( ?: "" ) ) - repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)) + val now = dateUtil.now() + var sensorStartTime: Long? = bundle.getLong(Intents.EXTRA_SENSOR_STARTED_AT, 0) + // check start time validity + sensorStartTime?.let { + if (abs(it - now) > T.months(1).msecs() || it > now) sensorStartTime = null + } + repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), sensorStartTime)) .doOnError { aapsLogger.error(LTag.DATABASE, "Error while saving values from Xdrip", it) ret = Result.failure(workDataOf("Error" to it.toString())) } .blockingGet() - .also { savedValues -> - savedValues.all().forEach { + .also { result -> + result.all().forEach { xdripSourcePlugin.detectSource(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) return ret From 13f06ef110a2112964d573808c4763007197fe17 Mon Sep 17 00:00:00 2001 From: Jakub Kuczys Date: Fri, 1 Dec 2023 18:32:36 +0100 Subject: [PATCH 2/4] Add a setting for sensor change upload to xDrip BG source --- core/utils/src/main/res/values/keys.xml | 1 + .../aaps/plugins/source/XdripSourcePlugin.kt | 10 ++++++-- .../source/src/main/res/values/strings.xml | 5 ++-- .../source/src/main/res/xml/pref_dexcom.xml | 4 ++-- .../source/src/main/res/xml/pref_xdrip.xml | 23 +++++++++++++++++++ 5 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 plugins/source/src/main/res/xml/pref_xdrip.xml diff --git a/core/utils/src/main/res/values/keys.xml b/core/utils/src/main/res/values/keys.xml index 8493fae85b..1dc4a7f260 100644 --- a/core/utils/src/main/res/values/keys.xml +++ b/core/utils/src/main/res/values/keys.xml @@ -50,6 +50,7 @@ insight_local_settings data_choices_settings dexcom_settings + xdrip_settings active_pump_change_timestamp active_pump_type active_pump_serial_number diff --git a/plugins/source/src/main/kotlin/app/aaps/plugins/source/XdripSourcePlugin.kt b/plugins/source/src/main/kotlin/app/aaps/plugins/source/XdripSourcePlugin.kt index 1942e424be..fee5212076 100644 --- a/plugins/source/src/main/kotlin/app/aaps/plugins/source/XdripSourcePlugin.kt +++ b/plugins/source/src/main/kotlin/app/aaps/plugins/source/XdripSourcePlugin.kt @@ -11,6 +11,7 @@ import app.aaps.core.interfaces.plugin.PluginDescription import app.aaps.core.interfaces.plugin.PluginType import app.aaps.core.interfaces.receivers.Intents 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.XDripSource import app.aaps.core.interfaces.utils.DateUtil @@ -41,7 +42,7 @@ class XdripSourcePlugin @Inject constructor( .mainType(PluginType.BGSOURCE) .fragmentClass(BGSourceFragment::class.java.name) .pluginIcon((app.aaps.core.main.R.drawable.ic_blooddrop_48)) - .preferencesId(R.xml.pref_bgsource) + .preferencesId(R.xml.pref_xdrip) .pluginName(R.string.source_xdrip) .description(R.string.description_source_xdrip), aapsLogger, rh, injector @@ -70,6 +71,7 @@ class XdripSourcePlugin @Inject constructor( ) : LoggingWorker(context, params, Dispatchers.IO) { @Inject lateinit var xdripSourcePlugin: XdripSourcePlugin + @Inject lateinit var sp: SP @Inject lateinit var dateUtil: DateUtil @Inject lateinit var repository: AppRepository @Inject lateinit var dataWorkerStorage: DataWorkerStorage @@ -96,7 +98,11 @@ class XdripSourcePlugin @Inject constructor( ) ) val now = dateUtil.now() - var sensorStartTime: Long? = bundle.getLong(Intents.EXTRA_SENSOR_STARTED_AT, 0) + var sensorStartTime: Long? = if (sp.getBoolean(R.string.key_xdrip_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 diff --git a/plugins/source/src/main/res/values/strings.xml b/plugins/source/src/main/res/values/strings.xml index 9c2db02d03..d2a3aa7d08 100644 --- a/plugins/source/src/main/res/values/strings.xml +++ b/plugins/source/src/main/res/values/strings.xml @@ -2,6 +2,7 @@ dexcom_lognssensorchange + xdrip_lognssensorchange last_processed_glunovo_timestamp last_processed_intelligo_timestamp @@ -39,8 +40,8 @@ Send BG data to xDrip+ In xDrip+ select 640g/Eversense data source BG upload settings - Log sensor change to NS - Create event \"Sensor Change\" in NS automatically on sensor start + Log sensor change to NS + Create event \"Sensor Change\" in NS automatically on sensor start direction diff --git a/plugins/source/src/main/res/xml/pref_dexcom.xml b/plugins/source/src/main/res/xml/pref_dexcom.xml index f9891d0049..2479f27cd6 100644 --- a/plugins/source/src/main/res/xml/pref_dexcom.xml +++ b/plugins/source/src/main/res/xml/pref_dexcom.xml @@ -15,8 +15,8 @@ + android:summary="@string/log_ns_sensor_change_summary" + android:title="@string/log_ns_sensor_change_title" /> diff --git a/plugins/source/src/main/res/xml/pref_xdrip.xml b/plugins/source/src/main/res/xml/pref_xdrip.xml new file mode 100644 index 0000000000..1eb5069016 --- /dev/null +++ b/plugins/source/src/main/res/xml/pref_xdrip.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + \ No newline at end of file From 65b34599d01c944bc092bd4ca5771e365294e9f3 Mon Sep 17 00:00:00 2001 From: Jakub Kuczys Date: Tue, 5 Dec 2023 23:25:50 +0100 Subject: [PATCH 3/4] Add unit tests --- plugins/source/build.gradle.kts | 2 + .../aaps/plugins/source/XdripSourcePlugin.kt | 26 +++-- .../plugins/source/XdripSourcePluginTest.kt | 94 ++++++++++++++++++- 3 files changed, 110 insertions(+), 12 deletions(-) diff --git a/plugins/source/build.gradle.kts b/plugins/source/build.gradle.kts index df0c6cf34a..4b0ac810c4 100644 --- a/plugins/source/build.gradle.kts +++ b/plugins/source/build.gradle.kts @@ -22,6 +22,8 @@ dependencies { implementation(project(":core:ui")) implementation(project(":core:utils")) + testImplementation(Libs.AndroidX.Work.testing) + testImplementation(project(":shared:tests")) kapt(Libs.Dagger.compiler) diff --git a/plugins/source/src/main/kotlin/app/aaps/plugins/source/XdripSourcePlugin.kt b/plugins/source/src/main/kotlin/app/aaps/plugins/source/XdripSourcePlugin.kt index fee5212076..dcb91782f7 100644 --- a/plugins/source/src/main/kotlin/app/aaps/plugins/source/XdripSourcePlugin.kt +++ b/plugins/source/src/main/kotlin/app/aaps/plugins/source/XdripSourcePlugin.kt @@ -1,6 +1,7 @@ package app.aaps.plugins.source import android.content.Context +import android.os.Bundle import androidx.work.WorkerParameters import androidx.work.workDataOf import app.aaps.core.interfaces.logging.AAPSLogger @@ -77,6 +78,20 @@ class XdripSourcePlugin @Inject constructor( @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_xdrip_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 { var ret = Result.success() @@ -97,16 +112,7 @@ class XdripSourcePlugin @Inject constructor( ?: "" ) ) - val now = dateUtil.now() - var sensorStartTime: Long? = if (sp.getBoolean(R.string.key_xdrip_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 - } + val sensorStartTime = getSensorStartTime(bundle) repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), sensorStartTime)) .doOnError { aapsLogger.error(LTag.DATABASE, "Error while saving values from Xdrip", it) diff --git a/plugins/source/src/test/kotlin/app/aaps/plugins/source/XdripSourcePluginTest.kt b/plugins/source/src/test/kotlin/app/aaps/plugins/source/XdripSourcePluginTest.kt index 3e645fb3e9..360a837549 100644 --- a/plugins/source/src/test/kotlin/app/aaps/plugins/source/XdripSourcePluginTest.kt +++ b/plugins/source/src/test/kotlin/app/aaps/plugins/source/XdripSourcePluginTest.kt @@ -1,25 +1,115 @@ 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.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 com.google.common.truth.Truth.assertThat import dagger.android.AndroidInjector +import dagger.android.HasAndroidInjector import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.mockito.Mock +import org.mockito.Mockito.`when` 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 context: ContextWithInjector @BeforeEach 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 { + val bundle = BundleMock.mock() + sensorStartTime?.let { bundle.putLong(Intents.EXTRA_SENSOR_STARTED_AT, sensorStartTime) } + `when`(sp.getBoolean(R.string.key_xdrip_log_ns_sensor_change, false)).thenReturn(logNsSensorChange) + + lateinit var worker: XdripSourcePlugin.XdripSourceWorker + TestListenableWorkerBuilder(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() { 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() + } } From 78f1fe096a514f75ef9f0a1b42e2f29a5cb46c2c Mon Sep 17 00:00:00 2001 From: Jakub Kuczys Date: Wed, 6 Dec 2023 15:51:55 +0100 Subject: [PATCH 4/4] Remove xdrip prefs file --- core/utils/src/main/res/values/keys.xml | 1 - .../aaps/plugins/source/XdripSourcePlugin.kt | 4 ++-- .../source/src/main/res/values/strings.xml | 5 ++-- .../source/src/main/res/xml/pref_dexcom.xml | 4 ++-- .../source/src/main/res/xml/pref_xdrip.xml | 23 ------------------- .../plugins/source/XdripSourcePluginTest.kt | 2 +- 6 files changed, 7 insertions(+), 32 deletions(-) delete mode 100644 plugins/source/src/main/res/xml/pref_xdrip.xml diff --git a/core/utils/src/main/res/values/keys.xml b/core/utils/src/main/res/values/keys.xml index 1dc4a7f260..8493fae85b 100644 --- a/core/utils/src/main/res/values/keys.xml +++ b/core/utils/src/main/res/values/keys.xml @@ -50,7 +50,6 @@ insight_local_settings data_choices_settings dexcom_settings - xdrip_settings active_pump_change_timestamp active_pump_type active_pump_serial_number diff --git a/plugins/source/src/main/kotlin/app/aaps/plugins/source/XdripSourcePlugin.kt b/plugins/source/src/main/kotlin/app/aaps/plugins/source/XdripSourcePlugin.kt index dcb91782f7..0ee8e1d7e8 100644 --- a/plugins/source/src/main/kotlin/app/aaps/plugins/source/XdripSourcePlugin.kt +++ b/plugins/source/src/main/kotlin/app/aaps/plugins/source/XdripSourcePlugin.kt @@ -43,7 +43,7 @@ class XdripSourcePlugin @Inject constructor( .mainType(PluginType.BGSOURCE) .fragmentClass(BGSourceFragment::class.java.name) .pluginIcon((app.aaps.core.main.R.drawable.ic_blooddrop_48)) - .preferencesId(R.xml.pref_xdrip) + .preferencesId(R.xml.pref_dexcom) .pluginName(R.string.source_xdrip) .description(R.string.description_source_xdrip), aapsLogger, rh, injector @@ -80,7 +80,7 @@ class XdripSourcePlugin @Inject constructor( fun getSensorStartTime(bundle: Bundle): Long? { val now = dateUtil.now() - var sensorStartTime: Long? = if (sp.getBoolean(R.string.key_xdrip_log_ns_sensor_change, false)) { + 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 diff --git a/plugins/source/src/main/res/values/strings.xml b/plugins/source/src/main/res/values/strings.xml index d2a3aa7d08..9c2db02d03 100644 --- a/plugins/source/src/main/res/values/strings.xml +++ b/plugins/source/src/main/res/values/strings.xml @@ -2,7 +2,6 @@ dexcom_lognssensorchange - xdrip_lognssensorchange last_processed_glunovo_timestamp last_processed_intelligo_timestamp @@ -40,8 +39,8 @@ Send BG data to xDrip+ In xDrip+ select 640g/Eversense data source BG upload settings - Log sensor change to NS - Create event \"Sensor Change\" in NS automatically on sensor start + Log sensor change to NS + Create event \"Sensor Change\" in NS automatically on sensor start direction diff --git a/plugins/source/src/main/res/xml/pref_dexcom.xml b/plugins/source/src/main/res/xml/pref_dexcom.xml index 2479f27cd6..f9891d0049 100644 --- a/plugins/source/src/main/res/xml/pref_dexcom.xml +++ b/plugins/source/src/main/res/xml/pref_dexcom.xml @@ -15,8 +15,8 @@ + android:summary="@string/dexcom_log_ns_sensor_change_summary" + android:title="@string/dexcom_log_ns_sensor_change_title" /> diff --git a/plugins/source/src/main/res/xml/pref_xdrip.xml b/plugins/source/src/main/res/xml/pref_xdrip.xml deleted file mode 100644 index 1eb5069016..0000000000 --- a/plugins/source/src/main/res/xml/pref_xdrip.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/plugins/source/src/test/kotlin/app/aaps/plugins/source/XdripSourcePluginTest.kt b/plugins/source/src/test/kotlin/app/aaps/plugins/source/XdripSourcePluginTest.kt index 360a837549..6afe6d546a 100644 --- a/plugins/source/src/test/kotlin/app/aaps/plugins/source/XdripSourcePluginTest.kt +++ b/plugins/source/src/test/kotlin/app/aaps/plugins/source/XdripSourcePluginTest.kt @@ -59,7 +59,7 @@ class XdripSourcePluginTest : TestBase() { ): Pair { val bundle = BundleMock.mock() sensorStartTime?.let { bundle.putLong(Intents.EXTRA_SENSOR_STARTED_AT, sensorStartTime) } - `when`(sp.getBoolean(R.string.key_xdrip_log_ns_sensor_change, false)).thenReturn(logNsSensorChange) + `when`(sp.getBoolean(R.string.key_dexcom_log_ns_sensor_change, false)).thenReturn(logNsSensorChange) lateinit var worker: XdripSourcePlugin.XdripSourceWorker TestListenableWorkerBuilder(context)