From 6735d2293472fa1d1fd1027e58e6667e749e8b14 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Fri, 28 May 2021 16:06:44 +0200 Subject: [PATCH] OfflineEvent -> room --- .../androidaps/db/CompatDBHelper.kt | 4 + .../dependencyInjection/AppModule.kt | 2 +- .../androidaps/dialogs/CarbsDialog.kt | 8 +- .../androidaps/dialogs/InsulinDialog.kt | 4 +- .../androidaps/dialogs/LoopDialog.kt | 168 +- .../androidaps/dialogs/TempTargetDialog.kt | 4 +- .../androidaps/plugins/aps/loop/LoopPlugin.kt | 117 +- .../DataSyncSelectorImplementation.kt | 45 + .../general/nsclient/NSClientAddAckWorker.kt | 20 + .../nsclient/NSClientAddUpdateWorker.kt | 39 +- .../nsclient/NSClientUpdateRemoveAckWorker.kt | 9 + .../plugins/general/overview/OverviewData.kt | 11 +- .../general/overview/OverviewPlugin.kt | 4 +- .../smsCommunicator/SmsCommunicatorPlugin.kt | 59 +- .../general/wear/ActionStringHandler.kt | 4 +- .../androidaps/queue/CommandQueue.kt | 2 +- .../androidaps/utils/wizard/BolusWizard.kt | 5 +- app/src/main/res/values/strings.xml | 4 + .../main/res/xml/pref_nsclientinternal.xml | 6 + .../SmsCommunicatorPluginTest.kt | 64 +- .../general/automation/AutomationPlugin.kt | 4 +- .../automation/actions/ActionLoopDisable.kt | 4 +- .../automation/actions/ActionLoopEnable.kt | 4 +- .../automation/actions/ActionLoopResume.kt | 23 +- .../automation/actions/ActionLoopSuspend.kt | 4 +- .../actions/ActionStartTempTarget.kt | 4 +- .../general/automation/AutomationEventTest.kt | 4 +- .../actions/ActionLoopResumeTest.kt | 27 +- .../actions/ActionStartTempTargetTest.kt | 10 +- .../automation/actions/ActionsTestBase.kt | 21 +- .../androidaps/events/EventOfflineChange.kt | 3 + .../BolusCalculatorResultExtension.kt | 1 + .../androidaps/extensions/CarbsExtension.kt | 1 + .../extensions/DeviceStatusExtension.kt | 4 +- .../extensions/GlucoseValueExtension.kt | 1 + .../extensions/OfflineEventExtension.kt | 64 + .../extensions/ProfileSwitchExtension.kt | 1 + .../extensions/TemporaryBasalExtension.kt | 1 + .../extensions/TherapyEventExtension.kt | 1 + .../androidaps/interfaces/DataSyncSelector.kt | 6 + .../interfaces/{LoopInterface.kt => Loop.kt} | 8 +- .../nightscout/androidaps/utils/Translator.kt | 233 +- .../userEntry/UserEntryPresentationHelper.kt | 14 +- core/src/main/res/values/strings.xml | 3 + .../21.json | 3593 +++++++++++++++++ .../androidaps/database/AppDatabase.kt | 6 +- .../androidaps/database/AppRepository.kt | 58 + .../androidaps/database/Converters.kt | 7 +- .../androidaps/database/DatabaseModule.kt | 67 +- .../database/DelegatedAppDatabase.kt | 2 + .../androidaps/database/TableNames.kt | 1 + .../database/daos/OfflineEventDao.kt | 51 + .../delegated/DelegatedOfflineEventDao.kt | 18 + .../database/entities/OfflineEvent.kt | 64 + .../androidaps/database/entities/UserEntry.kt | 2 + .../database/entities/ValueWithUnit.kt | 5 +- ...ncelCurrentOfflineEventIfAnyTransaction.kt | 25 + ...AndCancelCurrentOfflineEventTransaction.kt | 30 + ...ancelCurrentTemporaryTargetTransaction.kt} | 4 +- .../InvalidateOfflineEventTransaction.kt | 10 + .../SyncNsOfflineEventTransaction.kt | 73 + .../UpdateNsIdOfflineEventTransaction.kt | 12 + .../UpdateTemporaryTargetTransaction.kt | 9 - 63 files changed, 4653 insertions(+), 409 deletions(-) create mode 100644 core/src/main/java/info/nightscout/androidaps/events/EventOfflineChange.kt create mode 100644 core/src/main/java/info/nightscout/androidaps/extensions/OfflineEventExtension.kt rename core/src/main/java/info/nightscout/androidaps/interfaces/{LoopInterface.kt => Loop.kt} (76%) create mode 100644 database/schemas/info.nightscout.androidaps.database.AppDatabase/21.json create mode 100644 database/src/main/java/info/nightscout/androidaps/database/daos/OfflineEventDao.kt create mode 100644 database/src/main/java/info/nightscout/androidaps/database/daos/delegated/DelegatedOfflineEventDao.kt create mode 100644 database/src/main/java/info/nightscout/androidaps/database/entities/OfflineEvent.kt create mode 100644 database/src/main/java/info/nightscout/androidaps/database/transactions/CancelCurrentOfflineEventIfAnyTransaction.kt create mode 100644 database/src/main/java/info/nightscout/androidaps/database/transactions/InsertAndCancelCurrentOfflineEventTransaction.kt rename database/src/main/java/info/nightscout/androidaps/database/transactions/{InsertTemporaryTargetAndCancelCurrentTransaction.kt => InsertAndCancelCurrentTemporaryTargetTransaction.kt} (90%) create mode 100644 database/src/main/java/info/nightscout/androidaps/database/transactions/InvalidateOfflineEventTransaction.kt create mode 100644 database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsOfflineEventTransaction.kt create mode 100644 database/src/main/java/info/nightscout/androidaps/database/transactions/UpdateNsIdOfflineEventTransaction.kt delete mode 100644 database/src/main/java/info/nightscout/androidaps/database/transactions/UpdateTemporaryTargetTransaction.kt diff --git a/app/src/main/java/info/nightscout/androidaps/db/CompatDBHelper.kt b/app/src/main/java/info/nightscout/androidaps/db/CompatDBHelper.kt index 2bb83ef910..1f238d014a 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/CompatDBHelper.kt +++ b/app/src/main/java/info/nightscout/androidaps/db/CompatDBHelper.kt @@ -82,5 +82,9 @@ class CompatDBHelper @Inject constructor( aapsLogger.debug(LTag.DATABASE, "Firing EventProfileSwitchChanged") rxBus.send(EventProfileSwitchChanged()) } + it.filterIsInstance().firstOrNull()?.let { + aapsLogger.debug(LTag.DATABASE, "Firing EventOfflineChange") + rxBus.send(EventOfflineChange()) + } } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt index 30037c3948..65f3d692e7 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt @@ -83,7 +83,7 @@ open class AppModule { @Binds fun bindNotificationHolderInterface(notificationHolder: NotificationHolderImpl): NotificationHolder @Binds fun bindImportExportPrefsInterface(importExportPrefs: ImportExportPrefsImpl): ImportExportPrefs @Binds fun bindIconsProviderInterface(iconsProvider: IconsProviderImplementation): IconsProvider - @Binds fun bindLoopInterface(loopPlugin: LoopPlugin): LoopInterface + @Binds fun bindLoopInterface(loopPlugin: LoopPlugin): Loop @Binds fun bindIobCobCalculatorInterface(iobCobCalculatorPlugin: IobCobCalculatorPlugin): IobCobCalculator @Binds fun bindSmsCommunicatorInterface(smsCommunicatorPlugin: SmsCommunicatorPlugin): SmsCommunicator @Binds fun bindDataSyncSelector(dataSyncSelectorImplementation: DataSyncSelectorImplementation): DataSyncSelector diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt index 214847aff9..3bfa076da9 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt @@ -17,7 +17,7 @@ import info.nightscout.androidaps.database.entities.TemporaryTarget import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.ValueWithUnit -import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction +import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction import info.nightscout.androidaps.databinding.DialogCarbsBinding import info.nightscout.androidaps.extensions.formatColor import info.nightscout.androidaps.interfaces.Constraint @@ -226,7 +226,7 @@ class CarbsDialog : DialogFragmentWithDate() { ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.ACTIVITY), ValueWithUnit.fromGlucoseUnit(activityTT, units.asText), ValueWithUnit.Minute(activityTTDuration)) - disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( + disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction( timestamp = System.currentTimeMillis(), duration = TimeUnit.MINUTES.toMillis(activityTTDuration.toLong()), reason = TemporaryTarget.Reason.ACTIVITY, @@ -245,7 +245,7 @@ class CarbsDialog : DialogFragmentWithDate() { ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.EATING_SOON), ValueWithUnit.fromGlucoseUnit(eatingSoonTT, units.asText), ValueWithUnit.Minute(eatingSoonTTDuration)) - disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( + disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction( timestamp = System.currentTimeMillis(), duration = TimeUnit.MINUTES.toMillis(eatingSoonTTDuration.toLong()), reason = TemporaryTarget.Reason.EATING_SOON, @@ -264,7 +264,7 @@ class CarbsDialog : DialogFragmentWithDate() { ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.HYPOGLYCEMIA), ValueWithUnit.fromGlucoseUnit(hypoTT, units.asText), ValueWithUnit.Minute(hypoTTDuration)) - disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( + disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction( timestamp = System.currentTimeMillis(), duration = TimeUnit.MINUTES.toMillis(hypoTTDuration.toLong()), reason = TemporaryTarget.Reason.HYPOGLYCEMIA, diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt index 1d624d09d4..8d55d861a6 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt @@ -16,7 +16,7 @@ import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.TemporaryTarget import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Sources -import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction +import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction import info.nightscout.androidaps.databinding.DialogInsulinBinding import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.UserEntryLogger @@ -186,7 +186,7 @@ class InsulinDialog : DialogFragmentWithDate() { ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.EATING_SOON), ValueWithUnit.fromGlucoseUnit(eatingSoonTT, units.asText), ValueWithUnit.Minute(eatingSoonTTDuration)) - disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( + disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction( timestamp = System.currentTimeMillis(), duration = TimeUnit.MINUTES.toMillis(eatingSoonTTDuration.toLong()), reason = TemporaryTarget.Reason.EATING_SOON, diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/LoopDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/LoopDialog.kt index efadf17ca7..cfe1a210a3 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/LoopDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/LoopDialog.kt @@ -12,9 +12,13 @@ import androidx.fragment.app.FragmentManager import dagger.android.support.DaggerDialogFragment import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.ErrorHelperActivity +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.entities.OfflineEvent import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Sources +import info.nightscout.androidaps.database.transactions.CancelCurrentOfflineEventIfAnyTransaction +import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentOfflineEventTransaction import info.nightscout.androidaps.databinding.DialogLoopBinding import info.nightscout.androidaps.events.EventPreferenceChange import info.nightscout.androidaps.events.EventRefreshOverview @@ -29,8 +33,13 @@ import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.extensions.toVisibility +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxkotlin.plusAssign import javax.inject.Inject class LoopDialog : DaggerDialogFragment() { @@ -48,6 +57,8 @@ class LoopDialog : DaggerDialogFragment() { @Inject lateinit var commandQueue: CommandQueueProvider @Inject lateinit var configBuilder: ConfigBuilder @Inject lateinit var uel: UserEntryLogger + @Inject lateinit var dateUtil: DateUtil + @Inject lateinit var repository: AppRepository private var showOkCancel: Boolean = true private var _binding: DialogLoopBinding? = null @@ -58,6 +69,8 @@ class LoopDialog : DaggerDialogFragment() { // onDestroyView. private val binding get() = _binding!! + val disposable = CompositeDisposable() + override fun onStart() { super.onStart() dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) @@ -118,6 +131,7 @@ class LoopDialog : DaggerDialogFragment() { super.onDestroyView() _binding = null loopHandler.removeCallbacksAndMessages(null) + disposable.clear() } var task: Runnable? = null @@ -212,22 +226,22 @@ class LoopDialog : DaggerDialogFragment() { private fun onClickOkCancelEnabled(v: View): Boolean { var description = "" when (v.id) { - R.id.overview_closeloop -> description = resourceHelper.gs(R.string.closedloop) - R.id.overview_lgsloop -> description = resourceHelper.gs(R.string.lowglucosesuspend) - R.id.overview_openloop -> description = resourceHelper.gs(R.string.openloop) - R.id.overview_disable -> description = resourceHelper.gs(R.string.disableloop) - R.id.overview_enable -> description = resourceHelper.gs(R.string.enableloop) - R.id.overview_resume -> description = resourceHelper.gs(R.string.resume) - R.id.overview_reconnect -> description = resourceHelper.gs(R.string.reconnect) - R.id.overview_suspend_1h -> description = resourceHelper.gs(R.string.suspendloopfor1h) - R.id.overview_suspend_2h -> description = resourceHelper.gs(R.string.suspendloopfor2h) - R.id.overview_suspend_3h -> description = resourceHelper.gs(R.string.suspendloopfor3h) - R.id.overview_suspend_10h -> description = resourceHelper.gs(R.string.suspendloopfor10h) + R.id.overview_closeloop -> description = resourceHelper.gs(R.string.closedloop) + R.id.overview_lgsloop -> description = resourceHelper.gs(R.string.lowglucosesuspend) + R.id.overview_openloop -> description = resourceHelper.gs(R.string.openloop) + R.id.overview_disable -> description = resourceHelper.gs(R.string.disableloop) + R.id.overview_enable -> description = resourceHelper.gs(R.string.enableloop) + R.id.overview_resume -> description = resourceHelper.gs(R.string.resume) + R.id.overview_reconnect -> description = resourceHelper.gs(R.string.reconnect) + R.id.overview_suspend_1h -> description = resourceHelper.gs(R.string.suspendloopfor1h) + R.id.overview_suspend_2h -> description = resourceHelper.gs(R.string.suspendloopfor2h) + R.id.overview_suspend_3h -> description = resourceHelper.gs(R.string.suspendloopfor3h) + R.id.overview_suspend_10h -> description = resourceHelper.gs(R.string.suspendloopfor10h) R.id.overview_disconnect_15m -> description = resourceHelper.gs(R.string.disconnectpumpfor15m) R.id.overview_disconnect_30m -> description = resourceHelper.gs(R.string.disconnectpumpfor30m) - R.id.overview_disconnect_1h -> description = resourceHelper.gs(R.string.disconnectpumpfor1h) - R.id.overview_disconnect_2h -> description = resourceHelper.gs(R.string.disconnectpumpfor2h) - R.id.overview_disconnect_3h -> description = resourceHelper.gs(R.string.disconnectpumpfor3h) + R.id.overview_disconnect_1h -> description = resourceHelper.gs(R.string.disconnectpumpfor1h) + R.id.overview_disconnect_2h -> description = resourceHelper.gs(R.string.disconnectpumpfor2h) + R.id.overview_disconnect_3h -> description = resourceHelper.gs(R.string.disconnectpumpfor3h) } activity?.let { activity -> OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.confirm), description, Runnable { @@ -238,30 +252,29 @@ class LoopDialog : DaggerDialogFragment() { } fun onClick(v: View): Boolean { - val profile = profileFunction.getProfile() ?: return true when (v.id) { - R.id.overview_closeloop -> { + R.id.overview_closeloop -> { uel.log(Action.CLOSED_LOOP_MODE, Sources.LoopDialog) sp.putString(R.string.key_aps_mode, "closed") rxBus.send(EventPreferenceChange(resourceHelper.gs(R.string.closedloop))) return true } - R.id.overview_lgsloop -> { + R.id.overview_lgsloop -> { uel.log(Action.LGS_LOOP_MODE, Sources.LoopDialog) sp.putString(R.string.key_aps_mode, "lgs") rxBus.send(EventPreferenceChange(resourceHelper.gs(R.string.lowglucosesuspend))) return true } - R.id.overview_openloop -> { + R.id.overview_openloop -> { uel.log(Action.OPEN_LOOP_MODE, Sources.LoopDialog) sp.putString(R.string.key_aps_mode, "open") rxBus.send(EventPreferenceChange(resourceHelper.gs(R.string.lowglucosesuspend))) return true } - R.id.overview_disable -> { + R.id.overview_disable -> { uel.log(Action.LOOP_DISABLED, Sources.LoopDialog) loopPlugin.setPluginEnabled(PluginType.LOOP, false) loopPlugin.setFragmentVisible(PluginType.LOOP, false) @@ -274,23 +287,39 @@ class LoopDialog : DaggerDialogFragment() { } } }) - loopPlugin.createOfflineEvent(24 * 60) // upload 24h, we don't know real duration + disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.days(365).msecs(), OfflineEvent.Reason.DISABLE_LOOP)) + .subscribe({ result -> + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") } + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") } + }, { + aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it) + }) return true } - R.id.overview_enable -> { + R.id.overview_enable -> { uel.log(Action.LOOP_ENABLED, Sources.LoopDialog) loopPlugin.setPluginEnabled(PluginType.LOOP, true) loopPlugin.setFragmentVisible(PluginType.LOOP, true) configBuilder.storeSettings("EnablingLoop") rxBus.send(EventRefreshOverview("suspendmenu")) - loopPlugin.createOfflineEvent(0) + disposable += repository.runTransactionForResult(CancelCurrentOfflineEventIfAnyTransaction(dateUtil.now())) + .subscribe({ result -> + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") } + }, { + aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it) + }) return true } R.id.overview_resume, R.id.overview_reconnect -> { - uel.log(if (v.id==R.id.overview_resume) Action.RESUME else Action.RECONNECT, Sources.LoopDialog) - loopPlugin.suspendTo(0L) + uel.log(if (v.id == R.id.overview_resume) Action.RESUME else Action.RECONNECT, Sources.LoopDialog) + disposable += repository.runTransactionForResult(CancelCurrentOfflineEventIfAnyTransaction(dateUtil.now())) + .subscribe({ result -> + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") } + }, { + aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it) + }) rxBus.send(EventRefreshOverview("suspendmenu")) commandQueue.cancelTempBasal(true, object : Callback() { override fun run() { @@ -300,70 +329,123 @@ class LoopDialog : DaggerDialogFragment() { } }) sp.putBoolean(R.string.key_objectiveusereconnect, true) - loopPlugin.createOfflineEvent(0) return true } - R.id.overview_suspend_1h -> { + R.id.overview_suspend_1h -> { uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(1)) - loopPlugin.suspendLoop(60) + disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.hours(1).msecs(), OfflineEvent.Reason.SUSPEND)) + .subscribe({ result -> + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") } + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") } + }, { + aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it) + }) rxBus.send(EventRefreshOverview("suspendmenu")) return true } - R.id.overview_suspend_2h -> { + R.id.overview_suspend_2h -> { uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(2)) - loopPlugin.suspendLoop(120) + disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.hours(2).msecs(), OfflineEvent.Reason.SUSPEND)) + .subscribe({ result -> + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") } + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") } + }, { + aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it) + }) rxBus.send(EventRefreshOverview("suspendmenu")) return true } - R.id.overview_suspend_3h -> { + R.id.overview_suspend_3h -> { uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(3)) - loopPlugin.suspendLoop(180) + disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.hours(3).msecs(), OfflineEvent.Reason.SUSPEND)) + .subscribe({ result -> + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") } + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") } + }, { + aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it) + }) rxBus.send(EventRefreshOverview("suspendmenu")) return true } - R.id.overview_suspend_10h -> { + R.id.overview_suspend_10h -> { uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(10)) - loopPlugin.suspendLoop(600) + disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.hours(10).msecs(), OfflineEvent.Reason.SUSPEND)) + .subscribe({ result -> + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") } + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") } + }, { + aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it) + }) rxBus.send(EventRefreshOverview("suspendmenu")) return true } - R.id.overview_disconnect_15m -> { + R.id.overview_disconnect_15m -> { uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Minute(15)) - loopPlugin.disconnectPump(15, profile) + disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(15).msecs(), OfflineEvent.Reason.DISCONNECT_PUMP)) + .subscribe({ result -> + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") } + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") } + }, { + aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it) + }) rxBus.send(EventRefreshOverview("suspendmenu")) return true } - R.id.overview_disconnect_30m -> { + R.id.overview_disconnect_30m -> { uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Minute(30)) - loopPlugin.disconnectPump(30, profile) + disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(30).msecs(), OfflineEvent.Reason.DISCONNECT_PUMP)) + .subscribe({ result -> + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") } + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") } + }, { + aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it) + }) rxBus.send(EventRefreshOverview("suspendmenu")) return true } - R.id.overview_disconnect_1h -> { + R.id.overview_disconnect_1h -> { uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Hour(1)) - loopPlugin.disconnectPump(60, profile) + disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(60).msecs(), OfflineEvent.Reason.DISCONNECT_PUMP)) + .subscribe({ result -> + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") } + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") } + }, { + aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it) + }) sp.putBoolean(R.string.key_objectiveusedisconnect, true) rxBus.send(EventRefreshOverview("suspendmenu")) return true } - R.id.overview_disconnect_2h -> { + R.id.overview_disconnect_2h -> { uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Hour(2)) - loopPlugin.disconnectPump(120, profile) + disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(120).msecs(), OfflineEvent.Reason.DISCONNECT_PUMP)) + .subscribe({ result -> + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") } + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") } + }, { + aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it) + }) rxBus.send(EventRefreshOverview("suspendmenu")) return true } - R.id.overview_disconnect_3h -> { + R.id.overview_disconnect_3h -> { uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Hour(3)) - loopPlugin.disconnectPump(180, profile) + disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(180).msecs(), OfflineEvent.Reason.DISCONNECT_PUMP)) + .subscribe({ result -> + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") } + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") } + }, { + aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it) + }) rxBus.send(EventRefreshOverview("suspendmenu")) return true } diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/TempTargetDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/TempTargetDialog.kt index 699d3c43c1..36c157df72 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/TempTargetDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/TempTargetDialog.kt @@ -17,7 +17,7 @@ import info.nightscout.androidaps.database.entities.TemporaryTarget import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.transactions.CancelCurrentTemporaryTargetIfAnyTransaction -import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction +import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction import info.nightscout.androidaps.databinding.DialogTemptargetBinding import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.interfaces.ProfileFunction @@ -196,7 +196,7 @@ class TempTargetDialog : DialogFragmentWithDate() { aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) }) } else { - disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( + disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction( timestamp = eventTime, duration = TimeUnit.MINUTES.toMillis(duration.toLong()), reason = when (reason) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt index 072a9fb1d5..b2a13d8386 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt @@ -16,18 +16,19 @@ import info.nightscout.androidaps.data.DetailedBolusInfo import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.data.PumpEnactResult import info.nightscout.androidaps.database.AppRepository -import info.nightscout.androidaps.database.entities.TherapyEvent +import info.nightscout.androidaps.database.ValueWrapper +import info.nightscout.androidaps.database.entities.OfflineEvent import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.ValueWithUnit -import info.nightscout.androidaps.database.transactions.InsertIfNewByTimestampTherapyEventTransaction +import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentOfflineEventTransaction import info.nightscout.androidaps.database.transactions.InsertTherapyEventAnnouncementTransaction import info.nightscout.androidaps.events.EventAcceptOpenLoopChange import info.nightscout.androidaps.events.EventAutosensCalculationFinished import info.nightscout.androidaps.events.EventNewBG import info.nightscout.androidaps.events.EventTempTargetChange import info.nightscout.androidaps.interfaces.* -import info.nightscout.androidaps.interfaces.LoopInterface.LastRun +import info.nightscout.androidaps.interfaces.Loop.LastRun import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.UserEntryLogger @@ -96,7 +97,7 @@ open class LoopPlugin @Inject constructor( .enableByDefault(config.APS) .description(R.string.description_loop), aapsLogger, resourceHelper, injector -), LoopInterface { +), Loop { private val disposable = CompositeDisposable() private var lastBgTriggeredRun: Long = 0 @@ -158,48 +159,19 @@ open class LoopPlugin @Inject constructor( } } - override fun suspendTo(endTime: Long) { - sp.putLong("loopSuspendedTill", endTime) - sp.putBoolean("isSuperBolus", false) - sp.putBoolean("isDisconnected", false) + override fun minutesToEndOfSuspend(): Int { + val offlineEventWrapped = repository.getOfflineEventActiveAt(dateUtil.now()).blockingGet() + return if (offlineEventWrapped is ValueWrapper.Existing) T.msecs(offlineEventWrapped.value.timestamp + offlineEventWrapped.value.duration - dateUtil.now()).mins().toInt() + else 0 } - fun superBolusTo(endTime: Long) { - sp.putLong("loopSuspendedTill", endTime) - sp.putBoolean("isSuperBolus", true) - sp.putBoolean("isDisconnected", false) - } - - private fun disconnectTo(endTime: Long) { - sp.putLong("loopSuspendedTill", endTime) - sp.putBoolean("isSuperBolus", false) - sp.putBoolean("isDisconnected", true) - } - - fun minutesToEndOfSuspend(): Int { - val loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L) - if (loopSuspendedTill == 0L) return 0 - val now = System.currentTimeMillis() - val millisDiff = loopSuspendedTill - now - if (loopSuspendedTill <= now) { // time exceeded - suspendTo(0L) - return 0 - } - return (millisDiff / 60.0 / 1000.0).toInt() - } - - // time exceeded override val isSuspended: Boolean - get() { - val loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L) - if (loopSuspendedTill == 0L) return false - val now = System.currentTimeMillis() - if (loopSuspendedTill <= now) { // time exceeded - suspendTo(0L) - return false - } - return true - } + get() = repository.getOfflineEventActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing + + override var enabled: Boolean + get() = isEnabled() + set(value) { setPluginEnabled(PluginType.LOOP, value)} + val isLGS: Boolean get() { val closedLoopEnabled = constraintChecker.isClosedLoopAllowed() @@ -211,30 +183,16 @@ open class LoopPlugin @Inject constructor( return isLGS } - // time exceeded val isSuperBolus: Boolean get() { - val loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L) - if (loopSuspendedTill == 0L) return false - val now = System.currentTimeMillis() - if (loopSuspendedTill <= now) { // time exceeded - suspendTo(0L) - return false - } - return sp.getBoolean("isSuperBolus", false) + val offlineEventWrapped = repository.getOfflineEventActiveAt(dateUtil.now()).blockingGet() + return offlineEventWrapped is ValueWrapper.Existing && offlineEventWrapped.value.reason == OfflineEvent.Reason.SUPER_BOLUS } - // time exceeded val isDisconnected: Boolean get() { - val loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L) - if (loopSuspendedTill == 0L) return false - val now = System.currentTimeMillis() - if (loopSuspendedTill <= now) { // time exceeded - suspendTo(0L) - return false - } - return sp.getBoolean("isDisconnected", false) + val offlineEventWrapped = repository.getOfflineEventActiveAt(dateUtil.now()).blockingGet() + return offlineEventWrapped is ValueWrapper.Existing && offlineEventWrapped.value.reason == OfflineEvent.Reason.DISCONNECT_PUMP } @Suppress("SameParameterValue") @@ -642,11 +600,17 @@ open class LoopPlugin @Inject constructor( return virtualPumpPlugin.isEnabled(PluginType.PUMP) } - fun disconnectPump(durationInMinutes: Int, profile: Profile?) { + override fun goToZeroTemp(durationInMinutes: Int, profile: Profile, reason: OfflineEvent.Reason) { val pump = activePlugin.activePump - disconnectTo(System.currentTimeMillis() + durationInMinutes * 60 * 1000L) + disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(durationInMinutes.toLong()).msecs(), reason)) + .subscribe({ result -> + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") } + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") } + }, { + aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it) + }) if (pump.pumpDescription.tempBasalStyle == PumpDescription.ABSOLUTE) { - commandQueue.tempBasalAbsolute(0.0, durationInMinutes, true, profile!!, PumpSync.TemporaryBasalType.EMULATED_PUMP_SUSPEND, object : Callback() { + commandQueue.tempBasalAbsolute(0.0, durationInMinutes, true, profile, PumpSync.TemporaryBasalType.EMULATED_PUMP_SUSPEND, object : Callback() { override fun run() { if (!result.success) { ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror) @@ -654,7 +618,7 @@ open class LoopPlugin @Inject constructor( } }) } else { - commandQueue.tempBasalPercent(0, durationInMinutes, true, profile!!, PumpSync.TemporaryBasalType.EMULATED_PUMP_SUSPEND, object : Callback() { + commandQueue.tempBasalPercent(0, durationInMinutes, true, profile, PumpSync.TemporaryBasalType.EMULATED_PUMP_SUSPEND, object : Callback() { override fun run() { if (!result.success) { ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror) @@ -671,11 +635,16 @@ open class LoopPlugin @Inject constructor( } }) } - createOfflineEvent(durationInMinutes) } override fun suspendLoop(durationInMinutes: Int) { - suspendTo(System.currentTimeMillis() + durationInMinutes * 60 * 1000) + disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(durationInMinutes.toLong()).msecs(), OfflineEvent.Reason.SUSPEND)) + .subscribe({ result -> + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") } + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") } + }, { + aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it) + }) commandQueue.cancelTempBasal(true, object : Callback() { override fun run() { if (!result.success) { @@ -683,20 +652,6 @@ open class LoopPlugin @Inject constructor( } } }) - createOfflineEvent(durationInMinutes) - } - - override fun createOfflineEvent(durationInMinutes: Int) { - disposable += repository.runTransactionForResult(InsertIfNewByTimestampTherapyEventTransaction( - timestamp = dateUtil.now(), - type = TherapyEvent.Type.APS_OFFLINE, - duration = T.mins(durationInMinutes.toLong()).msecs(), - enteredBy = "openaps://" + "AndroidAPS", - glucoseUnit = TherapyEvent.GlucoseUnit.MGDL - )).subscribe( - { result -> result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted therapy event $it") } }, - { aapsLogger.error(LTag.DATABASE, "Error while saving therapy event", it) } - ) } companion object { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/DataSyncSelectorImplementation.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/DataSyncSelectorImplementation.kt index 5f1844315f..c5a41f057a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/DataSyncSelectorImplementation.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/DataSyncSelectorImplementation.kt @@ -39,6 +39,7 @@ class DataSyncSelectorImplementation @Inject constructor( processChangedFoodsCompat() processChangedTherapyEventsCompat() processChangedDeviceStatusesCompat() + processChangedOfflineEventsCompat() processChangedProfileStore() } } @@ -55,6 +56,7 @@ class DataSyncSelectorImplementation @Inject constructor( sp.remove(R.string.key_ns_extended_bolus_last_synced_id) sp.remove(R.string.key_ns_therapy_event_last_synced_id) sp.remove(R.string.key_ns_profile_switch_last_synced_id) + sp.remove(R.string.key_ns_offline_event_last_synced_id) sp.remove(R.string.key_ns_profile_store_last_synced_timestamp) } @@ -553,6 +555,49 @@ class DataSyncSelectorImplementation @Inject constructor( return false } + override fun confirmLastOfflineEventIdIfGreater(lastSynced: Long) { + if (lastSynced > sp.getLong(R.string.key_ns_offline_event_last_synced_id, 0)) { + aapsLogger.debug(LTag.NSCLIENT, "Setting OfflineEvent data sync from $lastSynced") + sp.putLong(R.string.key_ns_offline_event_last_synced_id, lastSynced) + } + } + + // Prepared for v3 (returns all modified after) + override fun changedOfflineEvents(): List { + val startId = sp.getLong(R.string.key_ns_offline_event_last_synced_id, 0) + return appRepository.getModifiedOfflineEventsDataFromId(startId).blockingGet().also { + aapsLogger.debug(LTag.NSCLIENT, "Loading OfflineEvent data for sync from $startId. Records ${it.size}") + } + } + + private var lastOeId = -1L + private var lastOeTime = -1L + override fun processChangedOfflineEventsCompat(): Boolean { + val lastDbIdWrapped = appRepository.getLastOfflineEventIdWrapped().blockingGet() + val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L + var startId = sp.getLong(R.string.key_ns_offline_event_last_synced_id, 0) + if (startId > lastDbId) { + sp.putLong(R.string.key_ns_offline_event_last_synced_id, 0) + startId = 0 + } + if (startId == lastOeId && dateUtil.now() - lastOeTime < 5000) return false + lastOeId = startId + lastOeTime = dateUtil.now() + appRepository.getNextSyncElementOfflineEvent(startId).blockingGet()?.let { oe -> + aapsLogger.info(LTag.DATABASE, "Loading OfflineEvent data Start: $startId ID: ${oe.first.id} HistoryID: ${oe.second} ") + when { + // without nsId = create new + oe.first.interfaceIDs.nightscoutId == null -> + nsClientPlugin.nsClientService?.dbAdd("treatments", oe.first.toJson(true, dateUtil), DataSyncSelector.PairOfflineEvent(oe.first, oe.second), "$startId/$lastDbId") + // existing with nsId = update + oe.first.interfaceIDs.nightscoutId != null -> + nsClientPlugin.nsClientService?.dbUpdate("treatments", oe.first.interfaceIDs.nightscoutId, oe.first.toJson(false, dateUtil), DataSyncSelector.PairOfflineEvent(oe.first, oe.second), "$startId/$lastDbId") + } + return true + } + return false + } + override fun confirmLastProfileStore(lastSynced: Long) { sp.putLong(R.string.key_ns_profile_store_last_synced_timestamp, lastSynced) } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientAddAckWorker.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientAddAckWorker.kt index cc3c6b6aea..1df6309037 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientAddAckWorker.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientAddAckWorker.kt @@ -251,6 +251,26 @@ class NSClientAddAckWorker( dataSyncSelector.confirmLastProfileStore(ack.originalObject.timestampSync) rxBus.send(EventNSClientNewLog("DBADD", "Acked ProfileStore " + ack.id)) } + + is PairOfflineEvent -> { + val pair = ack.originalObject + pair.value.interfaceIDs.nightscoutId = ack.id + repository.runTransactionForResult(UpdateNsIdOfflineEventTransaction(pair.value)) + .doOnError { error -> + aapsLogger.error(LTag.DATABASE, "Updated ns id of OfflineEvent failed", error) + ret = Result.failure((workDataOf("Error" to error.toString()))) + } + .doOnSuccess { + ret = Result.success(workDataOf("ProcessedData" to pair.toString())) + aapsLogger.debug(LTag.DATABASE, "Updated ns id of OfflineEvent " + pair.value) + dataSyncSelector.confirmLastOfflineEventIdIfGreater(pair.updateRecordId) + } + .blockingGet() + rxBus.send(EventNSClientNewLog("DBADD", "Acked OfflineEvent " + pair.value.interfaceIDs.nightscoutId)) + // Send new if waiting + dataSyncSelector.processChangedOfflineEventsCompat() + } + } return ret } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientAddUpdateWorker.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientAddUpdateWorker.kt index 216e4eb0ca..8bb8413c6d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientAddUpdateWorker.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientAddUpdateWorker.kt @@ -26,7 +26,6 @@ import info.nightscout.androidaps.receivers.DataWorker import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.JsonHelper import info.nightscout.androidaps.utils.JsonHelper.safeGetLong -import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.sharedPreferences.SP import java.util.concurrent.TimeUnit @@ -194,7 +193,6 @@ class NSClientAddUpdateWorker( eventType == TherapyEvent.Type.ANNOUNCEMENT.text || eventType == TherapyEvent.Type.QUESTION.text || eventType == TherapyEvent.Type.EXERCISE.text || - eventType == TherapyEvent.Type.APS_OFFLINE.text || eventType == TherapyEvent.Type.PUMP_BATTERY_CHANGE.text -> if (sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT) { therapyEventFromJson(json)?.let { therapyEvent -> @@ -341,6 +339,43 @@ class NSClientAddUpdateWorker( } } ?: aapsLogger.error("Error parsing ProfileSwitch json $json") } + eventType == TherapyEvent.Type.APS_OFFLINE.text -> + if (sp.getBoolean(R.string.key_ns_receive_offline_event, false) && buildHelper.isEngineeringMode() || config.NSCLIENT) { + offlineEventFromJson(json)?.let { offlineEvent -> + repository.runTransactionForResult(SyncNsOfflineEventTransaction(offlineEvent, invalidateByNsOnly = false)) + .doOnError { + aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it) + ret = Result.failure(workDataOf("Error" to it.toString())) + } + .blockingGet() + .also { result -> + result.inserted.forEach { oe -> + uel.log(Action.LOOP_CHANGE, Sources.NSClient, + ValueWithUnit.OfflineEventReason(oe.reason), + ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(oe.duration).toInt()) + ) + aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $oe") + } + result.invalidated.forEach { oe -> + uel.log(Action.LOOP_REMOVED, Sources.NSClient, + ValueWithUnit.OfflineEventReason(oe.reason), + ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(oe.duration).toInt()) + ) + aapsLogger.debug(LTag.DATABASE, "Invalidated OfflineEvent $oe") + } + result.ended.forEach { oe -> + uel.log(Action.LOOP_CHANGE, Sources.NSClient, + ValueWithUnit.OfflineEventReason(oe.reason), + ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(oe.duration).toInt()) + ) + aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $oe") + } + result.updatedNsId.forEach { + aapsLogger.debug(LTag.DATABASE, "Updated nsId OfflineEvent $it") + } + } + } ?: aapsLogger.error("Error parsing OfflineEvent json $json") + } } if (sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT) if (eventType == TherapyEvent.Type.ANNOUNCEMENT.text) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientUpdateRemoveAckWorker.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientUpdateRemoveAckWorker.kt index a7f51cbc70..5a38645c4d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientUpdateRemoveAckWorker.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientUpdateRemoveAckWorker.kt @@ -125,6 +125,15 @@ class NSClientUpdateRemoveAckWorker( dataSyncSelector.processChangedProfileSwitchesCompat() ret = Result.success(workDataOf("ProcessedData" to pair.toString())) } + + is PairOfflineEvent -> { + val pair = ack.originalObject + dataSyncSelector.confirmLastOfflineEventIdIfGreater(pair.updateRecordId) + rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked OfflineEvent" + ack._id)) + // Send new if waiting + dataSyncSelector.processChangedOfflineEventsCompat() + ret = Result.success(workDataOf("ProcessedData" to pair.toString())) + } } return ret } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewData.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewData.kt index 11a1ff2203..415ae58299 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewData.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewData.kt @@ -10,11 +10,7 @@ import info.nightscout.androidaps.R import info.nightscout.androidaps.data.IobTotal import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.ValueWrapper -import info.nightscout.androidaps.database.entities.Bolus -import info.nightscout.androidaps.database.entities.ExtendedBolus -import info.nightscout.androidaps.database.entities.GlucoseValue -import info.nightscout.androidaps.database.entities.TemporaryBasal -import info.nightscout.androidaps.database.entities.TemporaryTarget +import info.nightscout.androidaps.database.entities.* import info.nightscout.androidaps.extensions.* import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.logging.AAPSLogger @@ -533,6 +529,11 @@ class OverviewData @Inject constructor( .map { EffectiveProfileSwitchDataPoint(it) } .forEach(filteredTreatments::add) + // OfflineEvent + repository.getOfflineEventDataFromTimeToTime(fromTime, endTime, true).blockingGet() + .map { TherapyEventDataPoint(TherapyEvent(timestamp = it.timestamp, duration = it.duration, type = TherapyEvent.Type.APS_OFFLINE, glucoseUnit = TherapyEvent.GlucoseUnit.MMOL), resourceHelper, profileFunction, translator) } + .forEach(filteredTreatments::add) + // Extended bolus if (!activePlugin.activePump.isFakingTempsByExtendedBoluses) { repository.getExtendedBolusDataFromTimeToTime(fromTime, endTime, true).blockingGet() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewPlugin.kt index fe5a30dbb4..97fbb823a1 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewPlugin.kt @@ -139,9 +139,9 @@ class OverviewPlugin @Inject constructor( .observeOn(aapsSchedulers.io) .subscribe({ overviewData.preparePredictions("EventLoopInvoked") }, fabricPrivacy::logException) disposable.add(rxBus - .toObservable(EventProfileSwitchChanged::class.java) + .toObservable(EventNewBasalProfile::class.java) .observeOn(aapsSchedulers.io) - .subscribe({ loadProfile("EventProfileSwitchChanged") }, fabricPrivacy::logException)) + .subscribe({ loadProfile("EventNewBasalProfile") }, fabricPrivacy::logException)) disposable.add(rxBus .toObservable(EventAutosensCalculationFinished::class.java) .observeOn(aapsSchedulers.io) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt index 801f98fa91..e8ecaf6238 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt @@ -11,25 +11,26 @@ import androidx.work.Worker import androidx.work.WorkerParameters import androidx.work.workDataOf import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R import info.nightscout.androidaps.data.DetailedBolusInfo -import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.database.AppRepository -import info.nightscout.androidaps.database.entities.ValueWithUnit +import info.nightscout.androidaps.database.entities.OfflineEvent import info.nightscout.androidaps.database.entities.TemporaryTarget import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Sources +import info.nightscout.androidaps.database.entities.ValueWithUnit +import info.nightscout.androidaps.database.transactions.CancelCurrentOfflineEventIfAnyTransaction import info.nightscout.androidaps.database.transactions.CancelCurrentTemporaryTargetIfAnyTransaction -import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction +import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentOfflineEventTransaction +import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction import info.nightscout.androidaps.events.EventPreferenceChange import info.nightscout.androidaps.events.EventRefreshOverview +import info.nightscout.androidaps.extensions.valueToUnitsString import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.UserEntryLogger -import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart @@ -41,7 +42,6 @@ import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProv import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.receivers.DataWorker import info.nightscout.androidaps.utils.* -import info.nightscout.androidaps.extensions.valueToUnitsString import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.sharedPreferences.SP @@ -72,7 +72,7 @@ class SmsCommunicatorPlugin @Inject constructor( private val fabricPrivacy: FabricPrivacy, private val activePlugin: ActivePlugin, private val commandQueue: CommandQueueProvider, - private val loopPlugin: LoopPlugin, + private val loop: Loop, private val iobCobCalculator: IobCobCalculator, private val xdripCalibrations: XdripCalibrations, private var otp: OneTimePassword, @@ -340,14 +340,14 @@ class SmsCommunicatorPlugin @Inject constructor( private fun processLOOP(divided: Array, receivedSms: Sms) { when (divided[1].uppercase(Locale.getDefault())) { "DISABLE", "STOP" -> { - if (loopPlugin.isEnabled(PluginType.LOOP)) { + if (loop.enabled) { val passCode = generatePassCode() val reply = String.format(resourceHelper.gs(R.string.smscommunicator_loopdisablereplywithcode), passCode) receivedSms.processed = true messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() { override fun run() { uel.log(Action.LOOP_DISABLED, Sources.SMS) - loopPlugin.setPluginEnabled(PluginType.LOOP, false) + loop.enabled = false commandQueue.cancelTempBasal(true, object : Callback() { override fun run() { rxBus.send(EventRefreshOverview("SMS_LOOP_STOP")) @@ -364,14 +364,14 @@ class SmsCommunicatorPlugin @Inject constructor( } "ENABLE", "START" -> { - if (!loopPlugin.isEnabled(PluginType.LOOP)) { + if (!loop.enabled) { val passCode = generatePassCode() val reply = String.format(resourceHelper.gs(R.string.smscommunicator_loopenablereplywithcode), passCode) receivedSms.processed = true messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() { override fun run() { uel.log(Action.LOOP_ENABLED, Sources.SMS) - loopPlugin.setPluginEnabled(PluginType.LOOP, true) + loop.enabled= true sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_loophasbeenenabled))) rxBus.send(EventRefreshOverview("SMS_LOOP_START")) } @@ -382,8 +382,8 @@ class SmsCommunicatorPlugin @Inject constructor( } "STATUS" -> { - val reply = if (loopPlugin.isEnabled(PluginType.LOOP)) { - if (loopPlugin.isSuspended) String.format(resourceHelper.gs(R.string.loopsuspendedfor), loopPlugin.minutesToEndOfSuspend()) + val reply = if (loop.enabled) { + if (loop.isSuspended) String.format(resourceHelper.gs(R.string.loopsuspendedfor), loop.minutesToEndOfSuspend()) else resourceHelper.gs(R.string.smscommunicator_loopisenabled) } else resourceHelper.gs(R.string.loopisdisabled) @@ -398,7 +398,12 @@ class SmsCommunicatorPlugin @Inject constructor( messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() { override fun run() { uel.log(Action.RESUME, Sources.SMS) - loopPlugin.suspendTo(0L) + disposable += repository.runTransactionForResult(CancelCurrentOfflineEventIfAnyTransaction(dateUtil.now())) + .subscribe({ result -> + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") } + }, { + aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it) + }) rxBus.send(EventRefreshOverview("SMS_LOOP_RESUME")) commandQueue.cancelTempBasal(true, object : Callback() { override fun run() { @@ -409,7 +414,6 @@ class SmsCommunicatorPlugin @Inject constructor( } } }) - loopPlugin.createOfflineEvent(0) sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_loopresumed))) } }) @@ -434,8 +438,13 @@ class SmsCommunicatorPlugin @Inject constructor( commandQueue.cancelTempBasal(true, object : Callback() { override fun run() { if (result.success) { - loopPlugin.suspendTo(dateUtil.now() + anInteger() * 60L * 1000) - loopPlugin.createOfflineEvent(anInteger() * 60) + disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(anInteger().toLong()).msecs(), OfflineEvent.Reason.SUSPEND)) + .subscribe({ result -> + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") } + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") } + }, { + aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it) + }) rxBus.send(EventRefreshOverview("SMS_LOOP_SUSPENDED")) val replyText = resourceHelper.gs(R.string.smscommunicator_loopsuspended) + " " + resourceHelper.gs(if (result.success) R.string.smscommunicator_tempbasalcanceled else R.string.smscommunicator_tempbasalcancelfailed) @@ -512,10 +521,14 @@ class SmsCommunicatorPlugin @Inject constructor( if (!result.success) { sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_pumpconnectfail))) } else { - loopPlugin.suspendTo(0L) + disposable += repository.runTransactionForResult(CancelCurrentOfflineEventIfAnyTransaction(dateUtil.now())) + .subscribe({ result -> + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") } + }, { + aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it) + }) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_reconnect))) rxBus.send(EventRefreshOverview("SMS_PUMP_START")) - loopPlugin.createOfflineEvent(0) } } }) @@ -536,8 +549,8 @@ class SmsCommunicatorPlugin @Inject constructor( messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() { override fun run() { uel.log(Action.DISCONNECT, Sources.SMS) - val profile = profileFunction.getProfile() - loopPlugin.disconnectPump(duration, profile) + val profile = profileFunction.getProfile() ?: return + loop.goToZeroTemp(duration, profile, OfflineEvent.Reason.DISCONNECT_PUMP) rxBus.send(EventRefreshOverview("SMS_PUMP_DISCONNECT")) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_pumpdisconnected))) } @@ -832,7 +845,7 @@ class SmsCommunicatorPlugin @Inject constructor( currentProfile.units == GlucoseUnit.MMOL -> Constants.defaultEatingSoonTTmmol else -> Constants.defaultEatingSoonTTmgdl } - disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( + disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction( timestamp = dateUtil.now(), duration = TimeUnit.MINUTES.toMillis(eatingSoonTTDuration.toLong()), reason = TemporaryTarget.Reason.EATING_SOON, @@ -979,7 +992,7 @@ class SmsCommunicatorPlugin @Inject constructor( var tt = sp.getDouble(keyTarget, if (units == GlucoseUnit.MMOL) defaultTargetMMOL else defaultTargetMGDL) tt = Profile.toCurrentUnits(profileFunction, tt) tt = if (tt > 0) tt else if (units == GlucoseUnit.MMOL) defaultTargetMMOL else defaultTargetMGDL - disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( + disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction( timestamp = dateUtil.now(), duration = TimeUnit.MINUTES.toMillis(ttDuration.toLong()), reason = TemporaryTarget.Reason.EATING_SOON, diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt index 0385823728..df0881d908 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt @@ -20,7 +20,7 @@ import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.interfaces.end import info.nightscout.androidaps.database.transactions.CancelCurrentTemporaryTargetIfAnyTransaction -import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction +import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction import info.nightscout.androidaps.extensions.total import info.nightscout.androidaps.extensions.valueToUnits import info.nightscout.androidaps.interfaces.* @@ -572,7 +572,7 @@ class ActionStringHandler @Inject constructor( private fun generateTempTarget(duration: Int, low: Double, high: Double) { if (duration != 0) { - disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( + disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction( timestamp = System.currentTimeMillis(), duration = TimeUnit.MINUTES.toMillis(duration.toLong()), reason = TemporaryTarget.Reason.WEAR, diff --git a/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.kt b/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.kt index d759844d87..cdaba94dc6 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.kt +++ b/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.kt @@ -86,7 +86,6 @@ open class CommandQueue @Inject constructor( ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.failedupdatebasalprofile), R.raw.boluserror) } if (result.enacted) { - rxBus.send(EventNewBasalProfile()) repository.createEffectiveProfileSwitch( EffectiveProfileSwitch( timestamp = dateUtil.now(), @@ -104,6 +103,7 @@ open class CommandQueue @Inject constructor( insulinConfiguration = it.insulinConfiguration ) ) + rxBus.send(EventNewBasalProfile()) } } }) diff --git a/app/src/main/java/info/nightscout/androidaps/utils/wizard/BolusWizard.kt b/app/src/main/java/info/nightscout/androidaps/utils/wizard/BolusWizard.kt index a9f2462bc6..cce8d1b195 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/wizard/BolusWizard.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/wizard/BolusWizard.kt @@ -12,6 +12,7 @@ import info.nightscout.androidaps.data.DetailedBolusInfo import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.BolusCalculatorResult +import info.nightscout.androidaps.database.entities.OfflineEvent import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.TemporaryTarget import info.nightscout.androidaps.database.entities.UserEntry.Action @@ -374,7 +375,7 @@ class BolusWizard @Inject constructor( if (useSuperBolus) { uel.log(Action.SUPERBOLUS_TBR, Sources.WizardDialog) if (loopPlugin.isEnabled(PluginType.LOOP)) { - loopPlugin.superBolusTo(System.currentTimeMillis() + 2 * 60L * 60 * 1000) + loopPlugin.goToZeroTemp(2 * 60, profile, OfflineEvent.Reason.SUPER_BOLUS) rxBus.send(EventRefreshOverview("WizardDialog")) } @@ -408,7 +409,7 @@ class BolusWizard @Inject constructor( context = ctx mgdlGlucose = Profile.toMgdl(bg, profile.units) glucoseType = DetailedBolusInfo.MeterType.MANUAL - carbTime = this@BolusWizard.carbTime + carbsTimestamp = dateUtil.now() + T.mins(this@BolusWizard.carbTime.toLong()).msecs() bolusCalculatorResult = createBolusCalculatorResult() notes = this@BolusWizard.notes if (insulin > 0 || carbs > 0) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index af198dc3e4..1e932c24dd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -47,6 +47,7 @@ ns_temporary_basal_last_synced_id ns_extended_bolus_last_synced_id profile_switch_last_synced_id + ns_offline_event_last_synced_id ns_profile_store_last_synced_timestamp local_profile_last_change @@ -1137,8 +1138,11 @@ Receive temporary targets Accept temporary targets entered through NS or NSClient ns_receive_profile_switch + ns_receive_offline_event Receive profile switches Accept profile switches entered through NS or NSClient + Receive APS offline events + Accept APS Offline events entered through NS or NSClient ns_receive_insulin Receive insulin Accept insulin entered through NS or NSClient (it\'s not delivered, only calculated towards IOB) diff --git a/app/src/main/res/xml/pref_nsclientinternal.xml b/app/src/main/res/xml/pref_nsclientinternal.xml index 9056aaf8d9..5929db5532 100644 --- a/app/src/main/res/xml/pref_nsclientinternal.xml +++ b/app/src/main/res/xml/pref_nsclientinternal.xml @@ -78,6 +78,12 @@ android:summary="@string/ns_receive_therapy_events_summary" android:title="@string/ns_receive_therapy_events" /> + + > ()) - ).thenReturn(Single.just(InsertTemporaryTargetAndCancelCurrentTransaction.TransactionResult().apply { + repository.runTransactionForResult(anyObject()) + ).thenReturn(Single.just(InsertAndCancelCurrentTemporaryTargetTransaction.TransactionResult().apply { })) val glucoseStatusProvider = GlucoseStatusProvider(aapsLogger = aapsLogger, iobCobCalculator = iobCobCalculator, dateUtil = dateUtilMocked) @@ -309,7 +310,7 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() { `when`(sp.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)).thenReturn(true) //LOOP STATUS : disabled - PowerMockito.`when`(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(false) + PowerMockito.`when`(loopPlugin.enabled).thenReturn(false) smsCommunicatorPlugin.messages = ArrayList() sms = Sms("1234", "LOOP STATUS") smsCommunicatorPlugin.processSms(sms) @@ -318,7 +319,7 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() { //LOOP STATUS : suspended PowerMockito.`when`(loopPlugin.minutesToEndOfSuspend()).thenReturn(10) - PowerMockito.`when`(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true) + PowerMockito.`when`(loopPlugin.enabled).thenReturn(true) PowerMockito.`when`(loopPlugin.isSuspended).thenReturn(true) smsCommunicatorPlugin.messages = ArrayList() sms = Sms("1234", "LOOP STATUS") @@ -327,7 +328,7 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() { Assert.assertEquals("Suspended (10 m)", smsCommunicatorPlugin.messages[1].text) //LOOP STATUS : enabled - PowerMockito.`when`(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true) + PowerMockito.`when`(loopPlugin.enabled).thenReturn(true) PowerMockito.`when`(loopPlugin.isSuspended).thenReturn(false) smsCommunicatorPlugin.messages = ArrayList() sms = Sms("1234", "LOOP STATUS") @@ -337,7 +338,7 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() { Assert.assertEquals("Loop is enabled", smsCommunicatorPlugin.messages[1].text) //LOOP : wrong format - PowerMockito.`when`(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true) + PowerMockito.`when`(loopPlugin.enabled).thenReturn(true) smsCommunicatorPlugin.messages = ArrayList() sms = Sms("1234", "LOOP") smsCommunicatorPlugin.processSms(sms) @@ -346,7 +347,7 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() { Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages[1].text) //LOOP DISABLE : already disabled - PowerMockito.`when`(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(false) + PowerMockito.`when`(loopPlugin.enabled).thenReturn(false) smsCommunicatorPlugin.messages = ArrayList() sms = Sms("1234", "LOOP DISABLE") smsCommunicatorPlugin.processSms(sms) @@ -356,11 +357,11 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() { //LOOP DISABLE : from enabled hasBeenRun = false - PowerMockito.`when`(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true) - PowerMockito.doAnswer(Answer { - hasBeenRun = true - null - } as Answer<*>).`when`(loopPlugin).setPluginEnabled(PluginType.LOOP, false) + PowerMockito.`when`(loopPlugin.enabled).thenReturn(true) + // PowerMockito.doAnswer(Answer { + // hasBeenRun = true + // null + // } as Answer<*>).`when`(loopPlugin).setPluginEnabled(PluginType.LOOP, false) smsCommunicatorPlugin.messages = ArrayList() sms = Sms("1234", "LOOP DISABLE") smsCommunicatorPlugin.processSms(sms) @@ -371,10 +372,10 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() { smsCommunicatorPlugin.processSms(Sms("1234", passCode)) Assert.assertEquals(passCode, smsCommunicatorPlugin.messages[2].text) Assert.assertEquals("Loop has been disabled Temp basal canceled", smsCommunicatorPlugin.messages[3].text) - Assert.assertTrue(hasBeenRun) + //Assert.assertTrue(hasBeenRun) //LOOP ENABLE : already enabled - PowerMockito.`when`(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true) + PowerMockito.`when`(loopPlugin.enabled).thenReturn(true) smsCommunicatorPlugin.messages = ArrayList() sms = Sms("1234", "LOOP ENABLE") smsCommunicatorPlugin.processSms(sms) @@ -384,11 +385,11 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() { //LOOP ENABLE : from disabled hasBeenRun = false - PowerMockito.`when`(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(false) - PowerMockito.doAnswer(Answer { - hasBeenRun = true - null - } as Answer<*>).`when`(loopPlugin).setPluginEnabled(PluginType.LOOP, true) + PowerMockito.`when`(loopPlugin.enabled).thenReturn(false) + // PowerMockito.doAnswer(Answer { + // hasBeenRun = true + // null + // } as Answer<*>).`when`(loopPlugin).setPluginEnabled(PluginType.LOOP, true) smsCommunicatorPlugin.messages = ArrayList() sms = Sms("1234", "LOOP ENABLE") smsCommunicatorPlugin.processSms(sms) @@ -399,9 +400,13 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() { smsCommunicatorPlugin.processSms(Sms("1234", passCode)) Assert.assertEquals(passCode, smsCommunicatorPlugin.messages[2].text) Assert.assertEquals("Loop has been enabled", smsCommunicatorPlugin.messages[3].text) - Assert.assertTrue(hasBeenRun) + //Assert.assertTrue(hasBeenRun) //LOOP RESUME : already enabled + `when`( + repository.runTransactionForResult(anyObject>()) + ).thenReturn(Single.just(CancelCurrentOfflineEventIfAnyTransaction.TransactionResult().apply { + })) smsCommunicatorPlugin.messages = ArrayList() sms = Sms("1234", "LOOP RESUME") smsCommunicatorPlugin.processSms(sms) @@ -430,6 +435,10 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() { Assert.assertEquals("Wrong duration", smsCommunicatorPlugin.messages[1].text) //LOOP SUSPEND 100 : suspend for 100 min + correct answer + `when`( + repository.runTransactionForResult(anyObject>()) + ).thenReturn(Single.just(InsertAndCancelCurrentOfflineEventTransaction.TransactionResult().apply { + })) smsCommunicatorPlugin.messages = ArrayList() sms = Sms("1234", "LOOP SUSPEND 100") smsCommunicatorPlugin.processSms(sms) @@ -523,7 +532,11 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() { Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages[1].text) //PUMP CONNECT - PowerMockito.`when`(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true) + `when`( + repository.runTransactionForResult(anyObject>()) + ).thenReturn(Single.just(CancelCurrentOfflineEventIfAnyTransaction.TransactionResult().apply { + })) + PowerMockito.`when`(loopPlugin.enabled).thenReturn(true) smsCommunicatorPlugin.messages = ArrayList() sms = Sms("1234", "PUMP CONNECT") smsCommunicatorPlugin.processSms(sms) @@ -551,6 +564,7 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() { Assert.assertEquals("Wrong duration", smsCommunicatorPlugin.messages[1].text) //PUMP DISCONNECT 30 + `when`(profileFunction.getProfile()).thenReturn(validProfile) smsCommunicatorPlugin.messages = ArrayList() sms = Sms("1234", "PUMP DISCONNECT 30") smsCommunicatorPlugin.processSms(sms) diff --git a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationPlugin.kt b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationPlugin.kt index 6fdcd20ff7..e730cd61a7 100644 --- a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationPlugin.kt +++ b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationPlugin.kt @@ -8,7 +8,7 @@ import dagger.android.HasAndroidInjector import info.nightscout.androidaps.automation.R import info.nightscout.androidaps.events.* import info.nightscout.androidaps.interfaces.Config -import info.nightscout.androidaps.interfaces.LoopInterface +import info.nightscout.androidaps.interfaces.Loop import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginDescription import info.nightscout.androidaps.interfaces.PluginType @@ -45,7 +45,7 @@ class AutomationPlugin @Inject constructor( private val context: Context, private val sp: SP, private val fabricPrivacy: FabricPrivacy, - private val loopPlugin: LoopInterface, + private val loopPlugin: Loop, private val rxBus: RxBusWrapper, private val constraintChecker: ConstraintChecker, aapsLogger: AAPSLogger, diff --git a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopDisable.kt b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopDisable.kt index 848dc3c3b4..b6c391ebac 100644 --- a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopDisable.kt +++ b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopDisable.kt @@ -9,7 +9,7 @@ import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.events.EventRefreshOverview import info.nightscout.androidaps.interfaces.CommandQueueProvider import info.nightscout.androidaps.interfaces.ConfigBuilder -import info.nightscout.androidaps.interfaces.LoopInterface +import info.nightscout.androidaps.interfaces.Loop import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginType import info.nightscout.androidaps.logging.UserEntryLogger @@ -19,7 +19,7 @@ import info.nightscout.androidaps.utils.resources.ResourceHelper import javax.inject.Inject class ActionLoopDisable(injector: HasAndroidInjector) : Action(injector) { - @Inject lateinit var loopPlugin: LoopInterface + @Inject lateinit var loopPlugin: Loop @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var configBuilder: ConfigBuilder @Inject lateinit var commandQueue: CommandQueueProvider diff --git a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopEnable.kt b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopEnable.kt index d9c99ab41b..2864c2b96e 100644 --- a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopEnable.kt +++ b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopEnable.kt @@ -8,7 +8,7 @@ import info.nightscout.androidaps.database.entities.UserEntry import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.events.EventRefreshOverview import info.nightscout.androidaps.interfaces.ConfigBuilder -import info.nightscout.androidaps.interfaces.LoopInterface +import info.nightscout.androidaps.interfaces.Loop import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginType import info.nightscout.androidaps.logging.UserEntryLogger @@ -20,7 +20,7 @@ import javax.inject.Inject class ActionLoopEnable(injector: HasAndroidInjector) : Action(injector) { @Inject lateinit var resourceHelper: ResourceHelper - @Inject lateinit var loopPlugin: LoopInterface + @Inject lateinit var loopPlugin: Loop @Inject lateinit var configBuilder: ConfigBuilder @Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var uel: UserEntryLogger diff --git a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopResume.kt b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopResume.kt index a113a84506..25a22f54f0 100644 --- a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopResume.kt +++ b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopResume.kt @@ -4,33 +4,46 @@ import androidx.annotation.DrawableRes import dagger.android.HasAndroidInjector import info.nightscout.androidaps.automation.R import info.nightscout.androidaps.data.PumpEnactResult +import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.UserEntry import info.nightscout.androidaps.database.entities.UserEntry.Sources +import info.nightscout.androidaps.database.transactions.CancelCurrentOfflineEventIfAnyTransaction import info.nightscout.androidaps.events.EventRefreshOverview import info.nightscout.androidaps.interfaces.ConfigBuilder -import info.nightscout.androidaps.interfaces.LoopInterface +import info.nightscout.androidaps.interfaces.Loop +import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.queue.Callback +import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.resources.ResourceHelper +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxkotlin.plusAssign import javax.inject.Inject class ActionLoopResume(injector: HasAndroidInjector) : Action(injector) { @Inject lateinit var resourceHelper: ResourceHelper - @Inject lateinit var loopPlugin: LoopInterface + @Inject lateinit var loopPlugin: Loop @Inject lateinit var configBuilder: ConfigBuilder @Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var uel: UserEntryLogger + @Inject lateinit var repository: AppRepository + @Inject lateinit var dateUtil: DateUtil override fun friendlyName(): Int = R.string.resumeloop override fun shortDescription(): String = resourceHelper.gs(R.string.resumeloop) @DrawableRes override fun icon(): Int = R.drawable.ic_replay_24dp + val disposable = CompositeDisposable() + override fun doAction(callback: Callback) { if (loopPlugin.isSuspended) { - loopPlugin.suspendTo(0) - configBuilder.storeSettings("ActionLoopResume") - loopPlugin.createOfflineEvent(0) + disposable += repository.runTransactionForResult(CancelCurrentOfflineEventIfAnyTransaction(dateUtil.now())) + .subscribe({ result -> + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") } + }, { + aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it) + }) rxBus.send(EventRefreshOverview("ActionLoopResume")) uel.log(UserEntry.Action.RESUME, Sources.Automation, title) callback.result(PumpEnactResult(injector).success(true).comment(R.string.ok))?.run() diff --git a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopSuspend.kt b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopSuspend.kt index 797250b443..1ca41aa699 100644 --- a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopSuspend.kt +++ b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopSuspend.kt @@ -9,7 +9,7 @@ import info.nightscout.androidaps.database.entities.UserEntry import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.events.EventRefreshOverview -import info.nightscout.androidaps.interfaces.LoopInterface +import info.nightscout.androidaps.interfaces.Loop import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.general.automation.elements.InputDuration @@ -23,7 +23,7 @@ import javax.inject.Inject class ActionLoopSuspend(injector: HasAndroidInjector) : Action(injector) { @Inject lateinit var resourceHelper: ResourceHelper - @Inject lateinit var loopPlugin: LoopInterface + @Inject lateinit var loopPlugin: Loop @Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var uel: UserEntryLogger diff --git a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStartTempTarget.kt b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStartTempTarget.kt index b2e0397954..6775c1b34b 100644 --- a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStartTempTarget.kt +++ b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStartTempTarget.kt @@ -12,7 +12,7 @@ import info.nightscout.androidaps.database.entities.TemporaryTarget import info.nightscout.androidaps.database.entities.UserEntry import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.ValueWithUnit -import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction +import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.LTag @@ -59,7 +59,7 @@ class ActionStartTempTarget(injector: HasAndroidInjector) : Action(injector) { @DrawableRes override fun icon(): Int = R.drawable.ic_temptarget_high override fun doAction(callback: Callback) { - disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction(tt())) + disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction(tt())) .subscribe({ result -> result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted temp target $it") } result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") } diff --git a/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/AutomationEventTest.kt b/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/AutomationEventTest.kt index 6c0087b86b..c63aeb1356 100644 --- a/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/AutomationEventTest.kt +++ b/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/AutomationEventTest.kt @@ -4,7 +4,7 @@ import dagger.android.AndroidInjector import dagger.android.HasAndroidInjector import info.nightscout.androidaps.TestBase import info.nightscout.androidaps.interfaces.ConfigBuilder -import info.nightscout.androidaps.interfaces.LoopInterface +import info.nightscout.androidaps.interfaces.Loop import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.general.automation.actions.ActionLoopEnable import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnectorTest @@ -20,7 +20,7 @@ import org.powermock.modules.junit4.PowerMockRunner @RunWith(PowerMockRunner::class) class AutomationEventTest : TestBase() { - @Mock lateinit var loopPlugin: LoopInterface + @Mock lateinit var loopPlugin: Loop @Mock lateinit var resourceHelper: ResourceHelper @Mock lateinit var configBuilder: ConfigBuilder diff --git a/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopResumeTest.kt b/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopResumeTest.kt index b24aef178c..598fc9628c 100644 --- a/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopResumeTest.kt +++ b/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionLoopResumeTest.kt @@ -1,12 +1,15 @@ package info.nightscout.androidaps.plugins.general.automation.actions import info.nightscout.androidaps.automation.R +import info.nightscout.androidaps.database.entities.TemporaryTarget +import info.nightscout.androidaps.database.transactions.CancelCurrentOfflineEventIfAnyTransaction +import info.nightscout.androidaps.database.transactions.Transaction import info.nightscout.androidaps.queue.Callback +import io.reactivex.Single import org.junit.Assert import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mockito import org.mockito.Mockito.`when` import org.powermock.modules.junit4.PowerMockRunner @@ -38,20 +41,30 @@ class ActionLoopResumeTest : ActionsTestBase() { @Test fun doActionTest() { `when`(loopPlugin.isSuspended).thenReturn(true) + val inserted = mutableListOf().apply { + // insert all inserted TTs + } + val updated = mutableListOf().apply { + // add(TemporaryTarget(id = 0, version = 0, dateCreated = 0, isValid = false, referenceId = null, interfaceIDs_backing = null, timestamp = 0, utcOffset = 0, reason =, highTarget = 0.0, lowTarget = 0.0, duration = 0)) + // insert all updated TTs + } + `when`( + repository.runTransactionForResult(anyObject>()) + ).thenReturn(Single.just(CancelCurrentOfflineEventIfAnyTransaction.TransactionResult().apply { + inserted.addAll(inserted) + updated.addAll(updated) + })) + sut.doAction(object : Callback() { override fun run() {} }) - Mockito.verify(loopPlugin, Mockito.times(1)).suspendTo(0) - Mockito.verify(configBuilder, Mockito.times(1)).storeSettings("ActionLoopResume") - Mockito.verify(loopPlugin, Mockito.times(1)).createOfflineEvent(0) + //Mockito.verify(loopPlugin, Mockito.times(1)).suspendTo(0) // another call should keep it resumed, , no new invocation `when`(loopPlugin.isSuspended).thenReturn(false) sut.doAction(object : Callback() { override fun run() {} }) - Mockito.verify(loopPlugin, Mockito.times(1)).suspendTo(0) - Mockito.verify(configBuilder, Mockito.times(1)).storeSettings("ActionLoopResume") - Mockito.verify(loopPlugin, Mockito.times(1)).createOfflineEvent(0) + //Mockito.verify(loopPlugin, Mockito.times(1)).suspendTo(0) } } \ No newline at end of file diff --git a/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStartTempTargetTest.kt b/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStartTempTargetTest.kt index da2c4b8a3f..8569d710e0 100644 --- a/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStartTempTargetTest.kt +++ b/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionStartTempTargetTest.kt @@ -1,9 +1,8 @@ package info.nightscout.androidaps.plugins.general.automation.actions -import info.nightscout.androidaps.Constants import info.nightscout.androidaps.automation.R import info.nightscout.androidaps.database.entities.TemporaryTarget -import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction +import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction import info.nightscout.androidaps.database.transactions.Transaction import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.plugins.general.automation.elements.InputDuration @@ -14,7 +13,6 @@ import org.junit.Assert import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentMatcher import org.mockito.Mockito import org.mockito.Mockito.`when` import org.powermock.modules.junit4.PowerMockRunner @@ -72,12 +70,12 @@ class ActionStartTempTargetTest : ActionsTestBase() { } `when`( - repository.runTransactionForResult(argThatKotlin { + repository.runTransactionForResult(argThatKotlin { it.temporaryTarget .copy(timestamp = expectedTarget.timestamp, utcOffset = expectedTarget.utcOffset) // those can be different .contentEqualsTo(expectedTarget) }) - ).thenReturn(Single.just(InsertTemporaryTargetAndCancelCurrentTransaction.TransactionResult().apply { + ).thenReturn(Single.just(InsertAndCancelCurrentTemporaryTargetTransaction.TransactionResult().apply { inserted.addAll(inserted) updated.addAll(updated) })) @@ -87,7 +85,7 @@ class ActionStartTempTargetTest : ActionsTestBase() { Assert.assertTrue(result.success) } }) - Mockito.verify(repository, Mockito.times(1)).runTransactionForResult(anyObject>()) + Mockito.verify(repository, Mockito.times(1)).runTransactionForResult(anyObject>()) } @Test fun hasDialogTest() { diff --git a/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionsTestBase.kt b/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionsTestBase.kt index 8831ea6cb4..2205947563 100644 --- a/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionsTestBase.kt +++ b/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionsTestBase.kt @@ -2,15 +2,15 @@ package info.nightscout.androidaps.plugins.general.automation.actions import dagger.android.AndroidInjector import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.Constants -import info.nightscout.androidaps.plugins.general.automation.TestBaseWithProfile import info.nightscout.androidaps.TestPumpPlugin import info.nightscout.androidaps.automation.R import info.nightscout.androidaps.data.PumpEnactResult +import info.nightscout.androidaps.database.entities.OfflineEvent import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.general.automation.TestBaseWithProfile import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP @@ -29,13 +29,20 @@ open class ActionsTestBase : TestBaseWithProfile() { pluginDescription: PluginDescription ) : PluginBase( pluginDescription, aapsLogger, resourceHelper, injector - ), LoopInterface { + ), Loop { private var suspended = false - override var lastRun: LoopInterface.LastRun? = LoopInterface.LastRun() + override var lastRun: Loop.LastRun? = Loop.LastRun() override val isSuspended: Boolean = suspended - override fun suspendTo(endTime: Long) {} - override fun createOfflineEvent(durationInMinutes: Int) {} + override var enabled: Boolean + get() = true + set(value) {} + + override fun minutesToEndOfSuspend(): Int = 0 + + override fun goToZeroTemp(durationInMinutes: Int, profile: Profile, reason: OfflineEvent.Reason) { + } + override fun suspendLoop(durationInMinutes: Int) {} } @@ -102,6 +109,8 @@ open class ActionsTestBase : TestBaseWithProfile() { it.resourceHelper = resourceHelper it.configBuilder = configBuilder it.rxBus = rxBus + it.repository = repository + it.dateUtil = dateUtil it.uel = uel } if (it is ActionLoopEnable) { diff --git a/core/src/main/java/info/nightscout/androidaps/events/EventOfflineChange.kt b/core/src/main/java/info/nightscout/androidaps/events/EventOfflineChange.kt new file mode 100644 index 0000000000..6b088ae711 --- /dev/null +++ b/core/src/main/java/info/nightscout/androidaps/events/EventOfflineChange.kt @@ -0,0 +1,3 @@ +package info.nightscout.androidaps.events + +class EventOfflineChange : Event() \ No newline at end of file diff --git a/core/src/main/java/info/nightscout/androidaps/extensions/BolusCalculatorResultExtension.kt b/core/src/main/java/info/nightscout/androidaps/extensions/BolusCalculatorResultExtension.kt index b083c1d238..b1f49b603c 100644 --- a/core/src/main/java/info/nightscout/androidaps/extensions/BolusCalculatorResultExtension.kt +++ b/core/src/main/java/info/nightscout/androidaps/extensions/BolusCalculatorResultExtension.kt @@ -11,6 +11,7 @@ fun BolusCalculatorResult.toJson(isAdd: Boolean, dateUtil: DateUtil): JSONObject JSONObject() .put("eventType", TherapyEvent.Type.BOLUS_WIZARD.text) .put("created_at", dateUtil.toISOString(timestamp)) + .put("isValid", isValid) .put("bolusCalculatorResult", Gson().toJson(this)) .put("date", timestamp) .put("glucose", glucoseValue) diff --git a/core/src/main/java/info/nightscout/androidaps/extensions/CarbsExtension.kt b/core/src/main/java/info/nightscout/androidaps/extensions/CarbsExtension.kt index 24aa4f9cf1..b3c6e6ac81 100644 --- a/core/src/main/java/info/nightscout/androidaps/extensions/CarbsExtension.kt +++ b/core/src/main/java/info/nightscout/androidaps/extensions/CarbsExtension.kt @@ -12,6 +12,7 @@ fun Carbs.toJson(isAdd: Boolean, dateUtil: DateUtil): JSONObject = .put("eventType", if (amount < 12) TherapyEvent.Type.CARBS_CORRECTION.text else TherapyEvent.Type.MEAL_BOLUS.text) .put("carbs", amount) .put("created_at", dateUtil.toISOString(timestamp)) + .put("isValid", isValid) .put("date", timestamp).also { if (duration != 0L) it.put("duration", duration) if (interfaceIDs.pumpId != null) it.put("pumpId", interfaceIDs.pumpId) diff --git a/core/src/main/java/info/nightscout/androidaps/extensions/DeviceStatusExtension.kt b/core/src/main/java/info/nightscout/androidaps/extensions/DeviceStatusExtension.kt index 1d5d6dea9c..fcd53753ea 100644 --- a/core/src/main/java/info/nightscout/androidaps/extensions/DeviceStatusExtension.kt +++ b/core/src/main/java/info/nightscout/androidaps/extensions/DeviceStatusExtension.kt @@ -3,7 +3,7 @@ package info.nightscout.androidaps.extensions import android.os.Build import info.nightscout.androidaps.database.entities.DeviceStatus import info.nightscout.androidaps.interfaces.IobCobCalculator -import info.nightscout.androidaps.interfaces.LoopInterface +import info.nightscout.androidaps.interfaces.Loop import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.Pump import info.nightscout.androidaps.plugins.configBuilder.RunningConfiguration @@ -28,7 +28,7 @@ fun DeviceStatus.toJson(dateUtil: DateUtil): JSONObject = fun buildDeviceStatus( dateUtil: DateUtil, - loopPlugin: LoopInterface, + loopPlugin: Loop, iobCobCalculatorPlugin: IobCobCalculator, profileFunction: ProfileFunction, pump: Pump, diff --git a/core/src/main/java/info/nightscout/androidaps/extensions/GlucoseValueExtension.kt b/core/src/main/java/info/nightscout/androidaps/extensions/GlucoseValueExtension.kt index 140a409822..a74fdff40b 100644 --- a/core/src/main/java/info/nightscout/androidaps/extensions/GlucoseValueExtension.kt +++ b/core/src/main/java/info/nightscout/androidaps/extensions/GlucoseValueExtension.kt @@ -20,6 +20,7 @@ fun GlucoseValue.toJson(isAdd : Boolean, dateUtil: DateUtil): JSONObject = .put("device", sourceSensor.text) .put("date", timestamp) .put("dateString", dateUtil.toISOString(timestamp)) + .put("isValid", isValid) .put("sgv", value) .put("direction", trendArrow.text) .put("type", "sgv") diff --git a/core/src/main/java/info/nightscout/androidaps/extensions/OfflineEventExtension.kt b/core/src/main/java/info/nightscout/androidaps/extensions/OfflineEventExtension.kt new file mode 100644 index 0000000000..c18089c6c2 --- /dev/null +++ b/core/src/main/java/info/nightscout/androidaps/extensions/OfflineEventExtension.kt @@ -0,0 +1,64 @@ +package info.nightscout.androidaps.extensions + +import info.nightscout.androidaps.database.embedments.InterfaceIDs +import info.nightscout.androidaps.database.entities.OfflineEvent +import info.nightscout.androidaps.database.entities.TherapyEvent +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.JsonHelper +import info.nightscout.androidaps.utils.T +import org.json.JSONObject + +fun OfflineEvent.toJson(isAdd: Boolean, dateUtil: DateUtil): JSONObject = + JSONObject() + .put("created_at", dateUtil.toISOString(timestamp)) + .put("enteredBy", "openaps://" + "AndroidAPS") + .put("eventType", TherapyEvent.Type.APS_OFFLINE.text) + .put("isValid", isValid) + .put("duration", T.msecs(duration).mins()) + .put("reason", reason.name) + .also { + if (interfaceIDs.pumpId != null) it.put("pumpId", interfaceIDs.pumpId) + if (interfaceIDs.pumpType != null) it.put("pumpType", interfaceIDs.pumpType!!.name) + if (interfaceIDs.pumpSerial != null) it.put("pumpSerial", interfaceIDs.pumpSerial) + if (isAdd && interfaceIDs.nightscoutId != null) it.put("_id", interfaceIDs.nightscoutId) + } + +/* NS PS +{ + "enteredBy": "undefined", + "eventType": "OpenAPS Offline", + "duration": 60, + "created_at": "2021-05-27T15:11:52.230Z", + "utcOffset": 0, + "_id": "60afb6ba3c0d77e3e720f2fe", + "mills": 1622128312230, + "carbs": null, + "insulin": null +} + */ +fun offlineEventFromJson(jsonObject: JSONObject): OfflineEvent? { + val timestamp = JsonHelper.safeGetLongAllowNull(jsonObject, "mills", null) ?: return null + val duration = JsonHelper.safeGetLong(jsonObject, "duration") + val isValid = JsonHelper.safeGetBoolean(jsonObject, "isValid", true) + val id = JsonHelper.safeGetStringAllowNull(jsonObject, "_id", null) + val pumpId = JsonHelper.safeGetLongAllowNull(jsonObject, "pumpId", null) + val pumpType = InterfaceIDs.PumpType.fromString(JsonHelper.safeGetStringAllowNull(jsonObject, "pumpType", null)) + val pumpSerial = JsonHelper.safeGetStringAllowNull(jsonObject, "pumpSerial", null) + val reason = OfflineEvent.Reason.fromString(JsonHelper.safeGetString(jsonObject, "reason", OfflineEvent.Reason.OTHER.name)) + + + + return OfflineEvent( + timestamp = timestamp, + duration = T.mins(duration).msecs(), + isValid = isValid, + reason = reason + ).also { + it.interfaceIDs.nightscoutId = id + it.interfaceIDs.pumpId = pumpId + it.interfaceIDs.pumpType = pumpType + it.interfaceIDs.pumpSerial = pumpSerial + } +} + + diff --git a/core/src/main/java/info/nightscout/androidaps/extensions/ProfileSwitchExtension.kt b/core/src/main/java/info/nightscout/androidaps/extensions/ProfileSwitchExtension.kt index 12f25fcdcf..fcb480a87c 100644 --- a/core/src/main/java/info/nightscout/androidaps/extensions/ProfileSwitchExtension.kt +++ b/core/src/main/java/info/nightscout/androidaps/extensions/ProfileSwitchExtension.kt @@ -21,6 +21,7 @@ fun ProfileSwitch.toJson(isAdd: Boolean, dateUtil: DateUtil): JSONObject = JSONObject() .put("created_at", dateUtil.toISOString(timestamp)) .put("enteredBy", "openaps://" + "AndroidAPS") + .put("isValid", isValid) .put("eventType", TherapyEvent.Type.PROFILE_SWITCH.text) .put("duration", T.msecs(duration).mins()) .put("profile", getCustomizedName()) diff --git a/core/src/main/java/info/nightscout/androidaps/extensions/TemporaryBasalExtension.kt b/core/src/main/java/info/nightscout/androidaps/extensions/TemporaryBasalExtension.kt index 35f3c195eb..391fd5fed6 100644 --- a/core/src/main/java/info/nightscout/androidaps/extensions/TemporaryBasalExtension.kt +++ b/core/src/main/java/info/nightscout/androidaps/extensions/TemporaryBasalExtension.kt @@ -67,6 +67,7 @@ fun TemporaryBasal.toJson(isAdd: Boolean, profile: Profile, dateUtil: DateUtil, .put("created_at", dateUtil.toISOString(timestamp)) .put("enteredBy", "openaps://" + "AndroidAPS") .put("eventType", TherapyEvent.Type.TEMPORARY_BASAL.text) + .put("isValid", isValid) .put("duration", T.msecs(duration).mins()) .put("rate", rate) .put("type", type.name) diff --git a/core/src/main/java/info/nightscout/androidaps/extensions/TherapyEventExtension.kt b/core/src/main/java/info/nightscout/androidaps/extensions/TherapyEventExtension.kt index 54b55f33ab..898fa5f371 100644 --- a/core/src/main/java/info/nightscout/androidaps/extensions/TherapyEventExtension.kt +++ b/core/src/main/java/info/nightscout/androidaps/extensions/TherapyEventExtension.kt @@ -100,6 +100,7 @@ fun therapyEventFromJson(jsonObject: JSONObject): TherapyEvent? { fun TherapyEvent.toJson(isAdd: Boolean): JSONObject = JSONObject() .put("eventType", type.text) + .put("isValid", isValid) .put("created_at", timestamp) .put("enteredBy", enteredBy) .put("units", if (glucoseUnit == TherapyEvent.GlucoseUnit.MGDL) Constants.MGDL else Constants.MMOL) diff --git a/core/src/main/java/info/nightscout/androidaps/interfaces/DataSyncSelector.kt b/core/src/main/java/info/nightscout/androidaps/interfaces/DataSyncSelector.kt index e4feb850d9..83f9e5e7bb 100644 --- a/core/src/main/java/info/nightscout/androidaps/interfaces/DataSyncSelector.kt +++ b/core/src/main/java/info/nightscout/androidaps/interfaces/DataSyncSelector.kt @@ -16,6 +16,7 @@ interface DataSyncSelector { data class PairTemporaryBasal(val value: TemporaryBasal, val updateRecordId: Long) data class PairExtendedBolus(val value: ExtendedBolus, val updateRecordId: Long) data class PairProfileSwitch(val value: ProfileSwitch, val updateRecordId: Long) + data class PairOfflineEvent(val value: OfflineEvent, val updateRecordId: Long) data class PairProfileStore(val value: JSONObject, val timestampSync: Long) fun doUpload() @@ -77,6 +78,11 @@ interface DataSyncSelector { // Until NS v3 fun processChangedProfileSwitchesCompat(): Boolean + fun confirmLastOfflineEventIdIfGreater(lastSynced: Long) + fun changedOfflineEvents() : List + // Until NS v3 + fun processChangedOfflineEventsCompat(): Boolean + fun confirmLastProfileStore(lastSynced: Long) fun processChangedProfileStore() } \ No newline at end of file diff --git a/core/src/main/java/info/nightscout/androidaps/interfaces/LoopInterface.kt b/core/src/main/java/info/nightscout/androidaps/interfaces/Loop.kt similarity index 76% rename from core/src/main/java/info/nightscout/androidaps/interfaces/LoopInterface.kt rename to core/src/main/java/info/nightscout/androidaps/interfaces/Loop.kt index fb6cb46594..a761e116b0 100644 --- a/core/src/main/java/info/nightscout/androidaps/interfaces/LoopInterface.kt +++ b/core/src/main/java/info/nightscout/androidaps/interfaces/Loop.kt @@ -1,9 +1,10 @@ package info.nightscout.androidaps.interfaces import info.nightscout.androidaps.data.PumpEnactResult +import info.nightscout.androidaps.database.entities.OfflineEvent import info.nightscout.androidaps.plugins.aps.loop.APSResult -interface LoopInterface { +interface Loop { class LastRun { @@ -22,8 +23,9 @@ interface LoopInterface { var lastRun: LastRun? val isSuspended: Boolean + var enabled: Boolean - fun suspendTo(endTime: Long) - fun createOfflineEvent(durationInMinutes: Int) + fun minutesToEndOfSuspend(): Int + fun goToZeroTemp(durationInMinutes: Int, profile: Profile, reason: OfflineEvent.Reason) fun suspendLoop(durationInMinutes: Int) } \ No newline at end of file diff --git a/core/src/main/java/info/nightscout/androidaps/utils/Translator.kt b/core/src/main/java/info/nightscout/androidaps/utils/Translator.kt index 9ee3dbee13..36b4705a13 100644 --- a/core/src/main/java/info/nightscout/androidaps/utils/Translator.kt +++ b/core/src/main/java/info/nightscout/androidaps/utils/Translator.kt @@ -1,6 +1,7 @@ package info.nightscout.androidaps.utils import info.nightscout.androidaps.core.R +import info.nightscout.androidaps.database.entities.OfflineEvent import info.nightscout.androidaps.database.entities.TemporaryTarget import info.nightscout.androidaps.database.entities.TherapyEvent import info.nightscout.androidaps.database.entities.UserEntry.Action @@ -15,94 +16,93 @@ class Translator @Inject internal constructor( private val resourceHelper: ResourceHelper ) { - @Deprecated("use type instead of string") - fun translate(text: String): String = text - - fun translate(action: Action): String = when(action) { - Action.BOLUS -> resourceHelper.gs(R.string.uel_bolus) - Action.SMB -> resourceHelper.gs(R.string.smb_shortname) - Action.BOLUS_ADVISOR -> resourceHelper.gs(R.string.uel_bolus_advisor) - Action.EXTENDED_BOLUS -> resourceHelper.gs(R.string.uel_extended_bolus) - Action.SUPERBOLUS_TBR -> resourceHelper.gs(R.string.uel_superbolus_tbr) - Action.CARBS -> resourceHelper.gs(R.string.uel_carbs) - Action.EXTENDED_CARBS -> resourceHelper.gs(R.string.uel_extended_carbs) - Action.TEMP_BASAL -> resourceHelper.gs(R.string.uel_temp_basal) - Action.TT -> resourceHelper.gs(R.string.uel_tt) - Action.NEW_PROFILE -> resourceHelper.gs(R.string.uel_new_profile) - Action.CLONE_PROFILE -> resourceHelper.gs(R.string.uel_clone_profile) - Action.STORE_PROFILE -> resourceHelper.gs(R.string.uel_store_profile) - Action.PROFILE_SWITCH -> resourceHelper.gs(R.string.uel_profile_switch) - Action.PROFILE_SWITCH_CLONED -> resourceHelper.gs(R.string.uel_profile_switch_cloned) - Action.CLOSED_LOOP_MODE -> resourceHelper.gs(R.string.uel_closed_loop_mode) - Action.LGS_LOOP_MODE -> resourceHelper.gs(R.string.uel_lgs_loop_mode) - Action.OPEN_LOOP_MODE -> resourceHelper.gs(R.string.uel_open_loop_mode) - Action.LOOP_DISABLED -> resourceHelper.gs(R.string.uel_loop_disabled) - Action.LOOP_ENABLED -> resourceHelper.gs(R.string.uel_loop_enabled) - Action.RECONNECT -> resourceHelper.gs(R.string.uel_reconnect) - Action.DISCONNECT -> resourceHelper.gs(R.string.uel_disconnect) - Action.RESUME -> resourceHelper.gs(R.string.uel_resume) - Action.SUSPEND -> resourceHelper.gs(R.string.uel_suspend) - Action.HW_PUMP_ALLOWED -> resourceHelper.gs(R.string.uel_hw_pump_allowed) - Action.CLEAR_PAIRING_KEYS -> resourceHelper.gs(R.string.uel_clear_pairing_keys) - Action.ACCEPTS_TEMP_BASAL -> resourceHelper.gs(R.string.uel_accepts_temp_basal) - Action.CANCEL_TEMP_BASAL -> resourceHelper.gs(R.string.uel_cancel_temp_basal) - Action.CANCEL_BOLUS -> resourceHelper.gs(R.string.uel_cancel_bolus) - Action.CANCEL_EXTENDED_BOLUS -> resourceHelper.gs(R.string.uel_cancel_extended_bolus) - Action.CANCEL_TT -> resourceHelper.gs(R.string.uel_cancel_tt) - Action.CAREPORTAL -> resourceHelper.gs(R.string.uel_careportal) - Action.SITE_CHANGE -> resourceHelper.gs(R.string.uel_site_change) - Action.RESERVOIR_CHANGE -> resourceHelper.gs(R.string.uel_reservoir_change) - Action.CALIBRATION -> resourceHelper.gs(R.string.uel_calibration) - Action.PRIME_BOLUS -> resourceHelper.gs(R.string.uel_prime_bolus) - Action.TREATMENT -> resourceHelper.gs(R.string.uel_treatment) - Action.CAREPORTAL_NS_REFRESH -> resourceHelper.gs(R.string.uel_careportal_ns_refresh) - Action.PROFILE_SWITCH_NS_REFRESH -> resourceHelper.gs(R.string.uel_profile_switch_ns_refresh) - Action.TREATMENTS_NS_REFRESH -> resourceHelper.gs(R.string.uel_treatments_ns_refresh) - Action.TT_NS_REFRESH -> resourceHelper.gs(R.string.uel_tt_ns_refresh) - Action.AUTOMATION_REMOVED -> resourceHelper.gs(R.string.uel_automation_removed) - Action.BG_REMOVED -> resourceHelper.gs(R.string.uel_bg_removed) - Action.CAREPORTAL_REMOVED -> resourceHelper.gs(R.string.uel_careportal_removed) - Action.BOLUS_REMOVED -> resourceHelper.gs(R.string.uel_bolus_removed) - Action.CARBS_REMOVED -> resourceHelper.gs(R.string.uel_carbs_removed) - Action.TEMP_BASAL_REMOVED -> resourceHelper.gs(R.string.uel_temp_basal_removed) - Action.EXTENDED_BOLUS_REMOVED -> resourceHelper.gs(R.string.uel_extended_bolus_removed) - Action.FOOD -> resourceHelper.gs(R.string.uel_food) - Action.FOOD_REMOVED -> resourceHelper.gs(R.string.uel_food_removed) - Action.PROFILE_REMOVED -> resourceHelper.gs(R.string.uel_profile_removed) - Action.PROFILE_SWITCH_REMOVED -> resourceHelper.gs(R.string.uel_profile_switch_removed) - Action.RESTART_EVENTS_REMOVED -> resourceHelper.gs(R.string.uel_restart_events_removed) - Action.TREATMENT_REMOVED -> resourceHelper.gs(R.string.uel_treatment_removed) - Action.TT_REMOVED -> resourceHelper.gs(R.string.uel_tt_removed) - Action.NS_PAUSED -> resourceHelper.gs(R.string.uel_ns_paused) - Action.NS_RESUME -> resourceHelper.gs(R.string.uel_ns_resume) - Action.NS_QUEUE_CLEARED -> resourceHelper.gs(R.string.uel_ns_queue_cleared) - Action.NS_SETTINGS_COPIED -> resourceHelper.gs(R.string.uel_ns_settings_copied) - Action.ERROR_DIALOG_OK -> resourceHelper.gs(R.string.uel_error_dialog_ok) - Action.ERROR_DIALOG_MUTE -> resourceHelper.gs(R.string.uel_error_dialog_mute) - Action.ERROR_DIALOG_MUTE_5MIN -> resourceHelper.gs(R.string.uel_error_dialog_mute_5min) - Action.OBJECTIVE_STARTED -> resourceHelper.gs(R.string.uel_objective_started) - Action.OBJECTIVE_UNSTARTED -> resourceHelper.gs(R.string.uel_objective_unstarted) - Action.OBJECTIVES_SKIPPED -> resourceHelper.gs(R.string.uel_objectives_skipped) - Action.STAT_RESET -> resourceHelper.gs(R.string.uel_stat_reset) - Action.DELETE_LOGS -> resourceHelper.gs(R.string.uel_delete_logs) - Action.DELETE_FUTURE_TREATMENTS -> resourceHelper.gs(R.string.uel_delete_future_treatments) - Action.EXPORT_SETTINGS -> resourceHelper.gs(R.string.uel_export_settings) - Action.IMPORT_SETTINGS -> resourceHelper.gs(R.string.uel_import_settings) - Action.RESET_DATABASES -> resourceHelper.gs(R.string.uel_reset_databases) - Action.EXPORT_DATABASES -> resourceHelper.gs(R.string.uel_export_databases) - Action.IMPORT_DATABASES -> resourceHelper.gs(R.string.uel_import_databases) - Action.OTP_EXPORT -> resourceHelper.gs(R.string.uel_otp_export) - Action.OTP_RESET -> resourceHelper.gs(R.string.uel_otp_reset) - Action.EXPORT_CSV -> resourceHelper.gs(R.string.uel_export_csv) - Action.STOP_SMS -> resourceHelper.gs(R.string.uel_stop_sms) - Action.START_AAPS -> resourceHelper.gs(R.string.uel_start_aaps) - Action.EXIT_AAPS -> resourceHelper.gs(R.string.uel_exit_aaps) - Action.PLUGIN_ENABLED -> resourceHelper.gs(R.string.uel_plugin_enabled) - Action.PLUGIN_DISABLED -> resourceHelper.gs(R.string.uel_plugin_disabled) - Action.UNKNOWN -> resourceHelper.gs(R.string.unknown) + fun translate(action: Action): String = when (action) { + Action.BOLUS -> resourceHelper.gs(R.string.uel_bolus) + Action.SMB -> resourceHelper.gs(R.string.smb_shortname) + Action.BOLUS_ADVISOR -> resourceHelper.gs(R.string.uel_bolus_advisor) + Action.EXTENDED_BOLUS -> resourceHelper.gs(R.string.uel_extended_bolus) + Action.SUPERBOLUS_TBR -> resourceHelper.gs(R.string.uel_superbolus_tbr) + Action.CARBS -> resourceHelper.gs(R.string.uel_carbs) + Action.EXTENDED_CARBS -> resourceHelper.gs(R.string.uel_extended_carbs) + Action.TEMP_BASAL -> resourceHelper.gs(R.string.uel_temp_basal) + Action.TT -> resourceHelper.gs(R.string.uel_tt) + Action.NEW_PROFILE -> resourceHelper.gs(R.string.uel_new_profile) + Action.CLONE_PROFILE -> resourceHelper.gs(R.string.uel_clone_profile) + Action.STORE_PROFILE -> resourceHelper.gs(R.string.uel_store_profile) + Action.PROFILE_SWITCH -> resourceHelper.gs(R.string.uel_profile_switch) + Action.PROFILE_SWITCH_CLONED -> resourceHelper.gs(R.string.uel_profile_switch_cloned) + Action.CLOSED_LOOP_MODE -> resourceHelper.gs(R.string.uel_closed_loop_mode) + Action.LGS_LOOP_MODE -> resourceHelper.gs(R.string.uel_lgs_loop_mode) + Action.OPEN_LOOP_MODE -> resourceHelper.gs(R.string.uel_open_loop_mode) + Action.LOOP_DISABLED -> resourceHelper.gs(R.string.uel_loop_disabled) + Action.LOOP_ENABLED -> resourceHelper.gs(R.string.uel_loop_enabled) + Action.RECONNECT -> resourceHelper.gs(R.string.uel_reconnect) + Action.DISCONNECT -> resourceHelper.gs(R.string.uel_disconnect) + Action.RESUME -> resourceHelper.gs(R.string.uel_resume) + Action.SUSPEND -> resourceHelper.gs(R.string.uel_suspend) + Action.HW_PUMP_ALLOWED -> resourceHelper.gs(R.string.uel_hw_pump_allowed) + Action.CLEAR_PAIRING_KEYS -> resourceHelper.gs(R.string.uel_clear_pairing_keys) + Action.ACCEPTS_TEMP_BASAL -> resourceHelper.gs(R.string.uel_accepts_temp_basal) + Action.CANCEL_TEMP_BASAL -> resourceHelper.gs(R.string.uel_cancel_temp_basal) + Action.CANCEL_BOLUS -> resourceHelper.gs(R.string.uel_cancel_bolus) + Action.CANCEL_EXTENDED_BOLUS -> resourceHelper.gs(R.string.uel_cancel_extended_bolus) + Action.CANCEL_TT -> resourceHelper.gs(R.string.uel_cancel_tt) + Action.CAREPORTAL -> resourceHelper.gs(R.string.uel_careportal) + Action.SITE_CHANGE -> resourceHelper.gs(R.string.uel_site_change) + Action.RESERVOIR_CHANGE -> resourceHelper.gs(R.string.uel_reservoir_change) + Action.CALIBRATION -> resourceHelper.gs(R.string.uel_calibration) + Action.PRIME_BOLUS -> resourceHelper.gs(R.string.uel_prime_bolus) + Action.TREATMENT -> resourceHelper.gs(R.string.uel_treatment) + Action.CAREPORTAL_NS_REFRESH -> resourceHelper.gs(R.string.uel_careportal_ns_refresh) + Action.PROFILE_SWITCH_NS_REFRESH -> resourceHelper.gs(R.string.uel_profile_switch_ns_refresh) + Action.TREATMENTS_NS_REFRESH -> resourceHelper.gs(R.string.uel_treatments_ns_refresh) + Action.TT_NS_REFRESH -> resourceHelper.gs(R.string.uel_tt_ns_refresh) + Action.AUTOMATION_REMOVED -> resourceHelper.gs(R.string.uel_automation_removed) + Action.BG_REMOVED -> resourceHelper.gs(R.string.uel_bg_removed) + Action.CAREPORTAL_REMOVED -> resourceHelper.gs(R.string.uel_careportal_removed) + Action.BOLUS_REMOVED -> resourceHelper.gs(R.string.uel_bolus_removed) + Action.CARBS_REMOVED -> resourceHelper.gs(R.string.uel_carbs_removed) + Action.TEMP_BASAL_REMOVED -> resourceHelper.gs(R.string.uel_temp_basal_removed) + Action.EXTENDED_BOLUS_REMOVED -> resourceHelper.gs(R.string.uel_extended_bolus_removed) + Action.FOOD -> resourceHelper.gs(R.string.uel_food) + Action.FOOD_REMOVED -> resourceHelper.gs(R.string.uel_food_removed) + Action.PROFILE_REMOVED -> resourceHelper.gs(R.string.uel_profile_removed) + Action.PROFILE_SWITCH_REMOVED -> resourceHelper.gs(R.string.uel_profile_switch_removed) + Action.RESTART_EVENTS_REMOVED -> resourceHelper.gs(R.string.uel_restart_events_removed) + Action.TREATMENT_REMOVED -> resourceHelper.gs(R.string.uel_treatment_removed) + Action.TT_REMOVED -> resourceHelper.gs(R.string.uel_tt_removed) + Action.NS_PAUSED -> resourceHelper.gs(R.string.uel_ns_paused) + Action.NS_RESUME -> resourceHelper.gs(R.string.uel_ns_resume) + Action.NS_QUEUE_CLEARED -> resourceHelper.gs(R.string.uel_ns_queue_cleared) + Action.NS_SETTINGS_COPIED -> resourceHelper.gs(R.string.uel_ns_settings_copied) + Action.ERROR_DIALOG_OK -> resourceHelper.gs(R.string.uel_error_dialog_ok) + Action.ERROR_DIALOG_MUTE -> resourceHelper.gs(R.string.uel_error_dialog_mute) + Action.ERROR_DIALOG_MUTE_5MIN -> resourceHelper.gs(R.string.uel_error_dialog_mute_5min) + Action.OBJECTIVE_STARTED -> resourceHelper.gs(R.string.uel_objective_started) + Action.OBJECTIVE_UNSTARTED -> resourceHelper.gs(R.string.uel_objective_unstarted) + Action.OBJECTIVES_SKIPPED -> resourceHelper.gs(R.string.uel_objectives_skipped) + Action.STAT_RESET -> resourceHelper.gs(R.string.uel_stat_reset) + Action.DELETE_LOGS -> resourceHelper.gs(R.string.uel_delete_logs) + Action.DELETE_FUTURE_TREATMENTS -> resourceHelper.gs(R.string.uel_delete_future_treatments) + Action.EXPORT_SETTINGS -> resourceHelper.gs(R.string.uel_export_settings) + Action.IMPORT_SETTINGS -> resourceHelper.gs(R.string.uel_import_settings) + Action.RESET_DATABASES -> resourceHelper.gs(R.string.uel_reset_databases) + Action.EXPORT_DATABASES -> resourceHelper.gs(R.string.uel_export_databases) + Action.IMPORT_DATABASES -> resourceHelper.gs(R.string.uel_import_databases) + Action.OTP_EXPORT -> resourceHelper.gs(R.string.uel_otp_export) + Action.OTP_RESET -> resourceHelper.gs(R.string.uel_otp_reset) + Action.EXPORT_CSV -> resourceHelper.gs(R.string.uel_export_csv) + Action.STOP_SMS -> resourceHelper.gs(R.string.uel_stop_sms) + Action.START_AAPS -> resourceHelper.gs(R.string.uel_start_aaps) + Action.EXIT_AAPS -> resourceHelper.gs(R.string.uel_exit_aaps) + Action.PLUGIN_ENABLED -> resourceHelper.gs(R.string.uel_plugin_enabled) + Action.PLUGIN_DISABLED -> resourceHelper.gs(R.string.uel_plugin_disabled) + Action.LOOP_CHANGE -> resourceHelper.gs(R.string.uel_loop_change) + Action.LOOP_REMOVED -> resourceHelper.gs(R.string.uel_loop_removed) + Action.UNKNOWN -> resourceHelper.gs(R.string.unknown) } - fun translate(units: ValueWithUnit?): String = when(units) { + fun translate(units: ValueWithUnit?): String = when (units) { is ValueWithUnit.Gram -> resourceHelper.gs(R.string.shortgram) is ValueWithUnit.Hour -> resourceHelper.gs(R.string.shorthour) is ValueWithUnit.Insulin -> resourceHelper.gs(R.string.insulin_unit_shortname) @@ -114,15 +114,15 @@ class Translator @Inject internal constructor( else -> "" } - fun translate(meterType: TherapyEvent.MeterType): String = when(meterType) { - TherapyEvent.MeterType.FINGER -> resourceHelper.gs(R.string.glucosetype_finger) - TherapyEvent.MeterType.SENSOR -> resourceHelper.gs(R.string.glucosetype_sensor) - TherapyEvent.MeterType.MANUAL -> resourceHelper.gs(R.string.manual) + fun translate(meterType: TherapyEvent.MeterType): String = when (meterType) { + TherapyEvent.MeterType.FINGER -> resourceHelper.gs(R.string.glucosetype_finger) + TherapyEvent.MeterType.SENSOR -> resourceHelper.gs(R.string.glucosetype_sensor) + TherapyEvent.MeterType.MANUAL -> resourceHelper.gs(R.string.manual) - else -> resourceHelper.gs(R.string.unknown) + else -> resourceHelper.gs(R.string.unknown) } - fun translate(type: TherapyEvent.Type): String = when(type) { + fun translate(type: TherapyEvent.Type): String = when (type) { TherapyEvent.Type.FINGER_STICK_BG_VALUE -> resourceHelper.gs(R.string.careportal_bgcheck) TherapyEvent.Type.SNACK_BOLUS -> resourceHelper.gs(R.string.careportal_snackbolus) TherapyEvent.Type.MEAL_BOLUS -> resourceHelper.gs(R.string.careportal_mealbolus) @@ -170,21 +170,30 @@ class Translator @Inject internal constructor( */ TherapyEvent.Type.NONE -> resourceHelper.gs(R.string.unknown) - else -> resourceHelper.gs(R.string.unknown) - } - - fun translate(reason: TemporaryTarget.Reason): String = when(reason) { - TemporaryTarget.Reason.CUSTOM -> resourceHelper.gs(R.string.custom) - TemporaryTarget.Reason.HYPOGLYCEMIA -> resourceHelper.gs(R.string.hypo) - TemporaryTarget.Reason.EATING_SOON -> resourceHelper.gs(R.string.eatingsoon) - TemporaryTarget.Reason.ACTIVITY -> resourceHelper.gs(R.string.activity) - TemporaryTarget.Reason.AUTOMATION -> resourceHelper.gs(R.string.automation) - TemporaryTarget.Reason.WEAR -> resourceHelper.gs(R.string.wear) - else -> resourceHelper.gs(R.string.unknown) } - fun translate(source: Sources): String = when(source) { + fun translate(reason: TemporaryTarget.Reason): String = when (reason) { + TemporaryTarget.Reason.CUSTOM -> resourceHelper.gs(R.string.custom) + TemporaryTarget.Reason.HYPOGLYCEMIA -> resourceHelper.gs(R.string.hypo) + TemporaryTarget.Reason.EATING_SOON -> resourceHelper.gs(R.string.eatingsoon) + TemporaryTarget.Reason.ACTIVITY -> resourceHelper.gs(R.string.activity) + TemporaryTarget.Reason.AUTOMATION -> resourceHelper.gs(R.string.automation) + TemporaryTarget.Reason.WEAR -> resourceHelper.gs(R.string.wear) + + else -> resourceHelper.gs(R.string.unknown) + } + + fun translate(reason: OfflineEvent.Reason): String = when (reason) { + OfflineEvent.Reason.SUSPEND -> resourceHelper.gs(R.string.uel_suspend) + OfflineEvent.Reason.DISABLE_LOOP -> resourceHelper.gs(R.string.disableloop) + OfflineEvent.Reason.DISCONNECT_PUMP -> resourceHelper.gs(R.string.uel_disconnect) + OfflineEvent.Reason.OTHER -> resourceHelper.gs(R.string.uel_other) + + else -> resourceHelper.gs(R.string.unknown) + } + + fun translate(source: Sources): String = when (source) { /* Sources.TreatmentDialog -> TODO() Sources.InsulinDialog -> TODO() @@ -270,14 +279,14 @@ class Translator @Inject internal constructor( Sources.Stats -> TODO() Sources.Aaps -> TODO() */ - Sources.Automation -> resourceHelper.gs(R.string.automation) - Sources.Loop -> resourceHelper.gs(R.string.loop) - Sources.NSClient -> resourceHelper.gs(R.string.ns) - Sources.Pump -> resourceHelper.gs(R.string.pump) - Sources.SMS -> resourceHelper.gs(R.string.smb_shortname) - Sources.Wear -> resourceHelper.gs(R.string.wear) - Sources.Unknown -> resourceHelper.gs(R.string.unknown) + Sources.Automation -> resourceHelper.gs(R.string.automation) + Sources.Loop -> resourceHelper.gs(R.string.loop) + Sources.NSClient -> resourceHelper.gs(R.string.ns) + Sources.Pump -> resourceHelper.gs(R.string.pump) + Sources.SMS -> resourceHelper.gs(R.string.smb_shortname) + Sources.Wear -> resourceHelper.gs(R.string.wear) + Sources.Unknown -> resourceHelper.gs(R.string.unknown) - else -> source.name + else -> source.name } } \ No newline at end of file diff --git a/core/src/main/java/info/nightscout/androidaps/utils/userEntry/UserEntryPresentationHelper.kt b/core/src/main/java/info/nightscout/androidaps/utils/userEntry/UserEntryPresentationHelper.kt index 0faf232c17..6b46b1001d 100644 --- a/core/src/main/java/info/nightscout/androidaps/utils/userEntry/UserEntryPresentationHelper.kt +++ b/core/src/main/java/info/nightscout/androidaps/utils/userEntry/UserEntryPresentationHelper.kt @@ -123,6 +123,7 @@ class UserEntryPresentationHelper @Inject constructor( is ValueWithUnit.SimpleString -> valueWithUnit.value is ValueWithUnit.TherapyEventMeterType -> translator.translate(valueWithUnit.value) is ValueWithUnit.TherapyEventTTReason -> translator.translate(valueWithUnit.value) + is ValueWithUnit.OfflineEventReason -> translator.translate(valueWithUnit.value) is ValueWithUnit.TherapyEventType -> translator.translate(valueWithUnit.value) is ValueWithUnit.Timestamp -> dateUtil.dateAndTimeAndSecondsString(valueWithUnit.value) @@ -165,10 +166,10 @@ class UserEntryPresentationHelper @Inject constructor( ) + "\n" private fun getCsvEntry(entry: UserEntry): String { - val fullvalueWithUnitList = ArrayList(entry.values) + val fullValueWithUnitList = ArrayList(entry.values) val timestampRec = entry.timestamp.toString() val dateTimestampRev = dateUtil.dateAndTimeAndSecondsString(entry.timestamp) - val utcOffset = dateUtil.timeStringFromSeconds((entry.utcOffset/1000).toInt()) + val utcOffset = dateUtil.timeStringFromSeconds((entry.utcOffset / 1000).toInt()) val action = csvString(entry.action) var therapyEvent = "" val source = translator.translate(entry.source) @@ -184,7 +185,7 @@ class UserEntryPresentationHelper @Inject constructor( var minute = "" var noUnit = "" - for (valueWithUnit in fullvalueWithUnitList.filterNotNull()) { + for (valueWithUnit in fullValueWithUnitList.filterNotNull()) { when (valueWithUnit) { is ValueWithUnit.Gram -> gram = valueWithUnit.value.toString() is ValueWithUnit.Hour -> hour = valueWithUnit.value.toString() @@ -196,6 +197,7 @@ class UserEntryPresentationHelper @Inject constructor( is ValueWithUnit.SimpleString -> simpleString = simpleString.addWithSeparator(valueWithUnit.value) is ValueWithUnit.TherapyEventMeterType -> therapyEvent = therapyEvent.addWithSeparator(translator.translate(valueWithUnit.value)) is ValueWithUnit.TherapyEventTTReason -> therapyEvent = therapyEvent.addWithSeparator(translator.translate(valueWithUnit.value)) + is ValueWithUnit.OfflineEventReason -> therapyEvent = therapyEvent.addWithSeparator(translator.translate(valueWithUnit.value)) is ValueWithUnit.TherapyEventType -> therapyEvent = therapyEvent.addWithSeparator(translator.translate(valueWithUnit.value)) is ValueWithUnit.Timestamp -> timestamp = dateUtil.dateAndTimeAndSecondsString(valueWithUnit.value) @@ -215,9 +217,9 @@ class UserEntryPresentationHelper @Inject constructor( return "$timestampRec;$dateTimestampRev;$utcOffset;$action;$therapyEvent;$source;$note;$simpleString;$timestamp;$bg;$gram;$insulin;$unitPerHour;$percent;$hour;$minute;$noUnit" } - private fun csvString(action: Action): String = "\"" + translator.translate(action).replace("\"", "\"\"").replace("\n"," / ") + "\"" - private fun csvString(id: Int): String = if (id != 0) "\"" + resourceHelper.gs(id).replace("\"", "\"\"").replace("\n"," / ") + "\"" else "" - private fun csvString(s: String): String = if (s != "") "\"" + s.replace("\"", "\"\"").replace("\n"," / ") + "\"" else "" + private fun csvString(action: Action): String = "\"" + translator.translate(action).replace("\"", "\"\"").replace("\n", " / ") + "\"" + private fun csvString(id: Int): String = if (id != 0) "\"" + resourceHelper.gs(id).replace("\"", "\"\"").replace("\n", " / ") + "\"" else "" + private fun csvString(s: String): String = if (s != "") "\"" + s.replace("\"", "\"\"").replace("\n", " / ") + "\"" else "" private fun String.addWithSeparator(add: Any) = this + (if (this.isBlank()) "" else " / ") + add.toString() diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index 8742f92523..45df6b9b98 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -494,6 +494,9 @@ No Unit Export User Entries to Excel (csv) "%1$s;%2$s;%3$s;%4$s;%5$s;%6$s;%7$s;%8$s;%9$s;%10$s;%11$s;%12$s;%13$s;%14$s;%15$s;%16$s;%17$s" + LOOP CHANGED + LOOP REMOVED + OTHER %1$d day diff --git a/database/schemas/info.nightscout.androidaps.database.AppDatabase/21.json b/database/schemas/info.nightscout.androidaps.database.AppDatabase/21.json new file mode 100644 index 0000000000..383ec7b2ae --- /dev/null +++ b/database/schemas/info.nightscout.androidaps.database.AppDatabase/21.json @@ -0,0 +1,3593 @@ +{ + "formatVersion": 1, + "database": { + "version": 21, + "identityHash": "e3558dc3bb3136c37dba4f90c97e8bdd", + "entities": [ + { + "tableName": "apsResults", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `algorithm` TEXT NOT NULL, `glucoseStatusJson` TEXT NOT NULL, `currentTempJson` TEXT NOT NULL, `iobDataJson` TEXT NOT NULL, `profileJson` TEXT NOT NULL, `autosensDataJson` TEXT, `mealDataJson` TEXT NOT NULL, `isMicroBolusAllowed` INTEGER, `resultJson` TEXT NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `apsResults`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "algorithm", + "columnName": "algorithm", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "glucoseStatusJson", + "columnName": "glucoseStatusJson", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "currentTempJson", + "columnName": "currentTempJson", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "iobDataJson", + "columnName": "iobDataJson", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "profileJson", + "columnName": "profileJson", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "autosensDataJson", + "columnName": "autosensDataJson", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mealDataJson", + "columnName": "mealDataJson", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isMicroBolusAllowed", + "columnName": "isMicroBolusAllowed", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "resultJson", + "columnName": "resultJson", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_apsResults_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_apsResults_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_apsResults_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_apsResults_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + } + ], + "foreignKeys": [ + { + "table": "apsResults", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "boluses", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `amount` REAL NOT NULL, `type` TEXT NOT NULL, `isBasalInsulin` INTEGER NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, `insulinLabel` TEXT, `insulinEndTime` INTEGER, `peak` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `boluses`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isBasalInsulin", + "columnName": "isBasalInsulin", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "insulinConfiguration.insulinLabel", + "columnName": "insulinLabel", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "insulinConfiguration.insulinEndTime", + "columnName": "insulinEndTime", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "insulinConfiguration.peak", + "columnName": "peak", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_boluses_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_boluses_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_boluses_isValid", + "unique": false, + "columnNames": [ + "isValid" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_boluses_isValid` ON `${TABLE_NAME}` (`isValid`)" + }, + { + "name": "index_boluses_temporaryId", + "unique": false, + "columnNames": [ + "temporaryId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_boluses_temporaryId` ON `${TABLE_NAME}` (`temporaryId`)" + }, + { + "name": "index_boluses_pumpId", + "unique": false, + "columnNames": [ + "pumpId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_boluses_pumpId` ON `${TABLE_NAME}` (`pumpId`)" + }, + { + "name": "index_boluses_pumpSerial", + "unique": false, + "columnNames": [ + "pumpSerial" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_boluses_pumpSerial` ON `${TABLE_NAME}` (`pumpSerial`)" + }, + { + "name": "index_boluses_pumpType", + "unique": false, + "columnNames": [ + "pumpType" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_boluses_pumpType` ON `${TABLE_NAME}` (`pumpType`)" + }, + { + "name": "index_boluses_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_boluses_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_boluses_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_boluses_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + } + ], + "foreignKeys": [ + { + "table": "boluses", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "bolusCalculatorResults", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `targetBGLow` REAL NOT NULL, `targetBGHigh` REAL NOT NULL, `isf` REAL NOT NULL, `ic` REAL NOT NULL, `bolusIOB` REAL NOT NULL, `wasBolusIOBUsed` INTEGER NOT NULL, `basalIOB` REAL NOT NULL, `wasBasalIOBUsed` INTEGER NOT NULL, `glucoseValue` REAL NOT NULL, `wasGlucoseUsed` INTEGER NOT NULL, `glucoseDifference` REAL NOT NULL, `glucoseInsulin` REAL NOT NULL, `glucoseTrend` REAL NOT NULL, `wasTrendUsed` INTEGER NOT NULL, `trendInsulin` REAL NOT NULL, `cob` REAL NOT NULL, `wasCOBUsed` INTEGER NOT NULL, `cobInsulin` REAL NOT NULL, `carbs` REAL NOT NULL, `wereCarbsUsed` INTEGER NOT NULL, `carbsInsulin` REAL NOT NULL, `otherCorrection` REAL NOT NULL, `wasSuperbolusUsed` INTEGER NOT NULL, `superbolusInsulin` REAL NOT NULL, `wasTempTargetUsed` INTEGER NOT NULL, `totalInsulin` REAL NOT NULL, `percentageCorrection` INTEGER NOT NULL, `profileName` TEXT NOT NULL, `note` TEXT NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `bolusCalculatorResults`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "targetBGLow", + "columnName": "targetBGLow", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "targetBGHigh", + "columnName": "targetBGHigh", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "isf", + "columnName": "isf", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "ic", + "columnName": "ic", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "bolusIOB", + "columnName": "bolusIOB", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "wasBolusIOBUsed", + "columnName": "wasBolusIOBUsed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "basalIOB", + "columnName": "basalIOB", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "wasBasalIOBUsed", + "columnName": "wasBasalIOBUsed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "glucoseValue", + "columnName": "glucoseValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "wasGlucoseUsed", + "columnName": "wasGlucoseUsed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "glucoseDifference", + "columnName": "glucoseDifference", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "glucoseInsulin", + "columnName": "glucoseInsulin", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "glucoseTrend", + "columnName": "glucoseTrend", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "wasTrendUsed", + "columnName": "wasTrendUsed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "trendInsulin", + "columnName": "trendInsulin", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "cob", + "columnName": "cob", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "wasCOBUsed", + "columnName": "wasCOBUsed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "cobInsulin", + "columnName": "cobInsulin", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "carbs", + "columnName": "carbs", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "wereCarbsUsed", + "columnName": "wereCarbsUsed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "carbsInsulin", + "columnName": "carbsInsulin", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "otherCorrection", + "columnName": "otherCorrection", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "wasSuperbolusUsed", + "columnName": "wasSuperbolusUsed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "superbolusInsulin", + "columnName": "superbolusInsulin", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "wasTempTargetUsed", + "columnName": "wasTempTargetUsed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "totalInsulin", + "columnName": "totalInsulin", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "percentageCorrection", + "columnName": "percentageCorrection", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "profileName", + "columnName": "profileName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_bolusCalculatorResults_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_bolusCalculatorResults_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_bolusCalculatorResults_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_bolusCalculatorResults_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + }, + { + "name": "index_bolusCalculatorResults_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_bolusCalculatorResults_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_bolusCalculatorResults_isValid", + "unique": false, + "columnNames": [ + "isValid" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_bolusCalculatorResults_isValid` ON `${TABLE_NAME}` (`isValid`)" + } + ], + "foreignKeys": [ + { + "table": "bolusCalculatorResults", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "carbs", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `duration` INTEGER NOT NULL, `amount` REAL NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `carbs`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "duration", + "columnName": "duration", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_carbs_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_carbs_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_carbs_isValid", + "unique": false, + "columnNames": [ + "isValid" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_carbs_isValid` ON `${TABLE_NAME}` (`isValid`)" + }, + { + "name": "index_carbs_nightscoutId", + "unique": false, + "columnNames": [ + "nightscoutId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_carbs_nightscoutId` ON `${TABLE_NAME}` (`nightscoutId`)" + }, + { + "name": "index_carbs_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_carbs_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_carbs_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_carbs_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + } + ], + "foreignKeys": [ + { + "table": "carbs", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "effectiveProfileSwitches", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `basalBlocks` TEXT NOT NULL, `isfBlocks` TEXT NOT NULL, `icBlocks` TEXT NOT NULL, `targetBlocks` TEXT NOT NULL, `glucoseUnit` TEXT NOT NULL, `originalProfileName` TEXT NOT NULL, `originalCustomizedName` TEXT NOT NULL, `originalTimeshift` INTEGER NOT NULL, `originalPercentage` INTEGER NOT NULL, `originalDuration` INTEGER NOT NULL, `originalEnd` INTEGER NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, `insulinLabel` TEXT NOT NULL, `insulinEndTime` INTEGER NOT NULL, `peak` INTEGER NOT NULL, FOREIGN KEY(`referenceId`) REFERENCES `effectiveProfileSwitches`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "basalBlocks", + "columnName": "basalBlocks", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isfBlocks", + "columnName": "isfBlocks", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "icBlocks", + "columnName": "icBlocks", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "targetBlocks", + "columnName": "targetBlocks", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "glucoseUnit", + "columnName": "glucoseUnit", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "originalProfileName", + "columnName": "originalProfileName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "originalCustomizedName", + "columnName": "originalCustomizedName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "originalTimeshift", + "columnName": "originalTimeshift", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "originalPercentage", + "columnName": "originalPercentage", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "originalDuration", + "columnName": "originalDuration", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "originalEnd", + "columnName": "originalEnd", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "insulinConfiguration.insulinLabel", + "columnName": "insulinLabel", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "insulinConfiguration.insulinEndTime", + "columnName": "insulinEndTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "insulinConfiguration.peak", + "columnName": "peak", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_effectiveProfileSwitches_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_effectiveProfileSwitches_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_effectiveProfileSwitches_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_effectiveProfileSwitches_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_effectiveProfileSwitches_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_effectiveProfileSwitches_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + }, + { + "name": "index_effectiveProfileSwitches_isValid", + "unique": false, + "columnNames": [ + "isValid" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_effectiveProfileSwitches_isValid` ON `${TABLE_NAME}` (`isValid`)" + } + ], + "foreignKeys": [ + { + "table": "effectiveProfileSwitches", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "extendedBoluses", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `duration` INTEGER NOT NULL, `amount` REAL NOT NULL, `isEmulatingTempBasal` INTEGER NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `extendedBoluses`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "duration", + "columnName": "duration", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "isEmulatingTempBasal", + "columnName": "isEmulatingTempBasal", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_extendedBoluses_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_extendedBoluses_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_extendedBoluses_isValid", + "unique": false, + "columnNames": [ + "isValid" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_extendedBoluses_isValid` ON `${TABLE_NAME}` (`isValid`)" + }, + { + "name": "index_extendedBoluses_endId", + "unique": false, + "columnNames": [ + "endId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_extendedBoluses_endId` ON `${TABLE_NAME}` (`endId`)" + }, + { + "name": "index_extendedBoluses_pumpSerial", + "unique": false, + "columnNames": [ + "pumpSerial" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_extendedBoluses_pumpSerial` ON `${TABLE_NAME}` (`pumpSerial`)" + }, + { + "name": "index_extendedBoluses_pumpId", + "unique": false, + "columnNames": [ + "pumpId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_extendedBoluses_pumpId` ON `${TABLE_NAME}` (`pumpId`)" + }, + { + "name": "index_extendedBoluses_pumpType", + "unique": false, + "columnNames": [ + "pumpType" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_extendedBoluses_pumpType` ON `${TABLE_NAME}` (`pumpType`)" + }, + { + "name": "index_extendedBoluses_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_extendedBoluses_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_extendedBoluses_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_extendedBoluses_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + } + ], + "foreignKeys": [ + { + "table": "extendedBoluses", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "glucoseValues", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `raw` REAL, `value` REAL NOT NULL, `trendArrow` TEXT NOT NULL, `noise` REAL, `sourceSensor` TEXT NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `glucoseValues`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "raw", + "columnName": "raw", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "trendArrow", + "columnName": "trendArrow", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "noise", + "columnName": "noise", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "sourceSensor", + "columnName": "sourceSensor", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_glucoseValues_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_glucoseValues_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_glucoseValues_nightscoutId", + "unique": false, + "columnNames": [ + "nightscoutId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_glucoseValues_nightscoutId` ON `${TABLE_NAME}` (`nightscoutId`)" + }, + { + "name": "index_glucoseValues_sourceSensor", + "unique": false, + "columnNames": [ + "sourceSensor" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_glucoseValues_sourceSensor` ON `${TABLE_NAME}` (`sourceSensor`)" + }, + { + "name": "index_glucoseValues_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_glucoseValues_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_glucoseValues_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_glucoseValues_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + } + ], + "foreignKeys": [ + { + "table": "glucoseValues", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "profileSwitches", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `basalBlocks` TEXT NOT NULL, `isfBlocks` TEXT NOT NULL, `icBlocks` TEXT NOT NULL, `targetBlocks` TEXT NOT NULL, `glucoseUnit` TEXT NOT NULL, `profileName` TEXT NOT NULL, `timeshift` INTEGER NOT NULL, `percentage` INTEGER NOT NULL, `duration` INTEGER NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, `insulinLabel` TEXT NOT NULL, `insulinEndTime` INTEGER NOT NULL, `peak` INTEGER NOT NULL, FOREIGN KEY(`referenceId`) REFERENCES `profileSwitches`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "basalBlocks", + "columnName": "basalBlocks", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isfBlocks", + "columnName": "isfBlocks", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "icBlocks", + "columnName": "icBlocks", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "targetBlocks", + "columnName": "targetBlocks", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "glucoseUnit", + "columnName": "glucoseUnit", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "profileName", + "columnName": "profileName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timeshift", + "columnName": "timeshift", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "percentage", + "columnName": "percentage", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "duration", + "columnName": "duration", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "insulinConfiguration.insulinLabel", + "columnName": "insulinLabel", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "insulinConfiguration.insulinEndTime", + "columnName": "insulinEndTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "insulinConfiguration.peak", + "columnName": "peak", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_profileSwitches_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_profileSwitches_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_profileSwitches_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_profileSwitches_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + }, + { + "name": "index_profileSwitches_isValid", + "unique": false, + "columnNames": [ + "isValid" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_profileSwitches_isValid` ON `${TABLE_NAME}` (`isValid`)" + }, + { + "name": "index_profileSwitches_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_profileSwitches_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_profileSwitches_nightscoutId", + "unique": false, + "columnNames": [ + "nightscoutId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_profileSwitches_nightscoutId` ON `${TABLE_NAME}` (`nightscoutId`)" + } + ], + "foreignKeys": [ + { + "table": "profileSwitches", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "temporaryBasals", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `type` TEXT NOT NULL, `isAbsolute` INTEGER NOT NULL, `rate` REAL NOT NULL, `duration` INTEGER NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `temporaryBasals`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isAbsolute", + "columnName": "isAbsolute", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "rate", + "columnName": "rate", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "duration", + "columnName": "duration", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_temporaryBasals_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_temporaryBasals_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_temporaryBasals_isValid", + "unique": false, + "columnNames": [ + "isValid" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_temporaryBasals_isValid` ON `${TABLE_NAME}` (`isValid`)" + }, + { + "name": "index_temporaryBasals_nightscoutId", + "unique": false, + "columnNames": [ + "nightscoutId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_temporaryBasals_nightscoutId` ON `${TABLE_NAME}` (`nightscoutId`)" + }, + { + "name": "index_temporaryBasals_pumpType", + "unique": false, + "columnNames": [ + "pumpType" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_temporaryBasals_pumpType` ON `${TABLE_NAME}` (`pumpType`)" + }, + { + "name": "index_temporaryBasals_endId", + "unique": false, + "columnNames": [ + "endId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_temporaryBasals_endId` ON `${TABLE_NAME}` (`endId`)" + }, + { + "name": "index_temporaryBasals_pumpSerial", + "unique": false, + "columnNames": [ + "pumpSerial" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_temporaryBasals_pumpSerial` ON `${TABLE_NAME}` (`pumpSerial`)" + }, + { + "name": "index_temporaryBasals_temporaryId", + "unique": false, + "columnNames": [ + "temporaryId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_temporaryBasals_temporaryId` ON `${TABLE_NAME}` (`temporaryId`)" + }, + { + "name": "index_temporaryBasals_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_temporaryBasals_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_temporaryBasals_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_temporaryBasals_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + } + ], + "foreignKeys": [ + { + "table": "temporaryBasals", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "temporaryTargets", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `reason` TEXT NOT NULL, `highTarget` REAL NOT NULL, `lowTarget` REAL NOT NULL, `duration` INTEGER NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `temporaryTargets`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "reason", + "columnName": "reason", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "highTarget", + "columnName": "highTarget", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "lowTarget", + "columnName": "lowTarget", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "duration", + "columnName": "duration", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_temporaryTargets_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_temporaryTargets_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_temporaryTargets_isValid", + "unique": false, + "columnNames": [ + "isValid" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_temporaryTargets_isValid` ON `${TABLE_NAME}` (`isValid`)" + }, + { + "name": "index_temporaryTargets_nightscoutId", + "unique": false, + "columnNames": [ + "nightscoutId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_temporaryTargets_nightscoutId` ON `${TABLE_NAME}` (`nightscoutId`)" + }, + { + "name": "index_temporaryTargets_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_temporaryTargets_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_temporaryTargets_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_temporaryTargets_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + } + ], + "foreignKeys": [ + { + "table": "temporaryTargets", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "therapyEvents", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `duration` INTEGER NOT NULL, `type` TEXT NOT NULL, `note` TEXT, `enteredBy` TEXT, `glucose` REAL, `glucoseType` TEXT, `glucoseUnit` TEXT NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `therapyEvents`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "duration", + "columnName": "duration", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "enteredBy", + "columnName": "enteredBy", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "glucose", + "columnName": "glucose", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "glucoseType", + "columnName": "glucoseType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "glucoseUnit", + "columnName": "glucoseUnit", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_therapyEvents_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_therapyEvents_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_therapyEvents_type", + "unique": false, + "columnNames": [ + "type" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_therapyEvents_type` ON `${TABLE_NAME}` (`type`)" + }, + { + "name": "index_therapyEvents_nightscoutId", + "unique": false, + "columnNames": [ + "nightscoutId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_therapyEvents_nightscoutId` ON `${TABLE_NAME}` (`nightscoutId`)" + }, + { + "name": "index_therapyEvents_isValid", + "unique": false, + "columnNames": [ + "isValid" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_therapyEvents_isValid` ON `${TABLE_NAME}` (`isValid`)" + }, + { + "name": "index_therapyEvents_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_therapyEvents_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_therapyEvents_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_therapyEvents_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + } + ], + "foreignKeys": [ + { + "table": "therapyEvents", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "totalDailyDoses", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `basalAmount` REAL NOT NULL, `bolusAmount` REAL NOT NULL, `totalAmount` REAL NOT NULL, `carbs` REAL NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `totalDailyDoses`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "basalAmount", + "columnName": "basalAmount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "bolusAmount", + "columnName": "bolusAmount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "totalAmount", + "columnName": "totalAmount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "carbs", + "columnName": "carbs", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_totalDailyDoses_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_totalDailyDoses_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_totalDailyDoses_pumpId", + "unique": false, + "columnNames": [ + "pumpId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_totalDailyDoses_pumpId` ON `${TABLE_NAME}` (`pumpId`)" + }, + { + "name": "index_totalDailyDoses_pumpType", + "unique": false, + "columnNames": [ + "pumpType" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_totalDailyDoses_pumpType` ON `${TABLE_NAME}` (`pumpType`)" + }, + { + "name": "index_totalDailyDoses_pumpSerial", + "unique": false, + "columnNames": [ + "pumpSerial" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_totalDailyDoses_pumpSerial` ON `${TABLE_NAME}` (`pumpSerial`)" + }, + { + "name": "index_totalDailyDoses_isValid", + "unique": false, + "columnNames": [ + "isValid" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_totalDailyDoses_isValid` ON `${TABLE_NAME}` (`isValid`)" + }, + { + "name": "index_totalDailyDoses_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_totalDailyDoses_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_totalDailyDoses_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_totalDailyDoses_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + } + ], + "foreignKeys": [ + { + "table": "totalDailyDoses", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "apsResultLinks", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `apsResultId` INTEGER NOT NULL, `smbId` INTEGER, `tbrId` INTEGER, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`apsResultId`) REFERENCES `apsResults`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`smbId`) REFERENCES `boluses`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`tbrId`) REFERENCES `temporaryBasals`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`referenceId`) REFERENCES `apsResultLinks`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "apsResultId", + "columnName": "apsResultId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "smbId", + "columnName": "smbId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "tbrId", + "columnName": "tbrId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_apsResultLinks_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_apsResultLinks_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_apsResultLinks_apsResultId", + "unique": false, + "columnNames": [ + "apsResultId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_apsResultLinks_apsResultId` ON `${TABLE_NAME}` (`apsResultId`)" + }, + { + "name": "index_apsResultLinks_smbId", + "unique": false, + "columnNames": [ + "smbId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_apsResultLinks_smbId` ON `${TABLE_NAME}` (`smbId`)" + }, + { + "name": "index_apsResultLinks_tbrId", + "unique": false, + "columnNames": [ + "tbrId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_apsResultLinks_tbrId` ON `${TABLE_NAME}` (`tbrId`)" + } + ], + "foreignKeys": [ + { + "table": "apsResults", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "apsResultId" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "boluses", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "smbId" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "temporaryBasals", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "tbrId" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "apsResultLinks", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "multiwaveBolusLinks", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `bolusId` INTEGER NOT NULL, `extendedBolusId` INTEGER NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`bolusId`) REFERENCES `boluses`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`extendedBolusId`) REFERENCES `extendedBoluses`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`referenceId`) REFERENCES `multiwaveBolusLinks`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "bolusId", + "columnName": "bolusId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "extendedBolusId", + "columnName": "extendedBolusId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_multiwaveBolusLinks_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_multiwaveBolusLinks_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_multiwaveBolusLinks_bolusId", + "unique": false, + "columnNames": [ + "bolusId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_multiwaveBolusLinks_bolusId` ON `${TABLE_NAME}` (`bolusId`)" + }, + { + "name": "index_multiwaveBolusLinks_extendedBolusId", + "unique": false, + "columnNames": [ + "extendedBolusId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_multiwaveBolusLinks_extendedBolusId` ON `${TABLE_NAME}` (`extendedBolusId`)" + } + ], + "foreignKeys": [ + { + "table": "boluses", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "bolusId" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "extendedBoluses", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "extendedBolusId" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "multiwaveBolusLinks", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "preferenceChanges", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `key` TEXT NOT NULL, `value` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "versionChanges", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `versionCode` INTEGER NOT NULL, `versionName` TEXT NOT NULL, `gitRemote` TEXT, `commitHash` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "versionCode", + "columnName": "versionCode", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "versionName", + "columnName": "versionName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gitRemote", + "columnName": "gitRemote", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "commitHash", + "columnName": "commitHash", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "userEntry", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `action` TEXT NOT NULL, `source` TEXT NOT NULL, `note` TEXT NOT NULL, `values` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "action", + "columnName": "action", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "source", + "columnName": "source", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "values", + "columnName": "values", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_userEntry_source", + "unique": false, + "columnNames": [ + "source" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_userEntry_source` ON `${TABLE_NAME}` (`source`)" + }, + { + "name": "index_userEntry_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_userEntry_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "foods", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `name` TEXT NOT NULL, `category` TEXT, `subCategory` TEXT, `portion` REAL NOT NULL, `carbs` INTEGER NOT NULL, `fat` INTEGER, `protein` INTEGER, `energy` INTEGER, `unit` TEXT NOT NULL, `gi` INTEGER, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `foods`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "subCategory", + "columnName": "subCategory", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "portion", + "columnName": "portion", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "carbs", + "columnName": "carbs", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fat", + "columnName": "fat", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "protein", + "columnName": "protein", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "energy", + "columnName": "energy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "unit", + "columnName": "unit", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gi", + "columnName": "gi", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_foods_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_foods_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_foods_nightscoutId", + "unique": false, + "columnNames": [ + "nightscoutId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_foods_nightscoutId` ON `${TABLE_NAME}` (`nightscoutId`)" + }, + { + "name": "index_foods_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_foods_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_foods_isValid", + "unique": false, + "columnNames": [ + "isValid" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_foods_isValid` ON `${TABLE_NAME}` (`isValid`)" + } + ], + "foreignKeys": [ + { + "table": "foods", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "deviceStatus", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `device` TEXT, `pump` TEXT, `enacted` TEXT, `suggested` TEXT, `iob` TEXT, `uploaderBattery` INTEGER NOT NULL, `configuration` TEXT, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "device", + "columnName": "device", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pump", + "columnName": "pump", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "enacted", + "columnName": "enacted", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "suggested", + "columnName": "suggested", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "iob", + "columnName": "iob", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uploaderBattery", + "columnName": "uploaderBattery", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "configuration", + "columnName": "configuration", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_deviceStatus_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_deviceStatus_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_deviceStatus_nightscoutId", + "unique": false, + "columnNames": [ + "nightscoutId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_deviceStatus_nightscoutId` ON `${TABLE_NAME}` (`nightscoutId`)" + }, + { + "name": "index_deviceStatus_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_deviceStatus_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "offlineEvents", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `reason` TEXT NOT NULL, `duration` INTEGER NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `offlineEvents`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dateCreated", + "columnName": "dateCreated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isValid", + "columnName": "isValid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "referenceId", + "columnName": "referenceId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "utcOffset", + "columnName": "utcOffset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "reason", + "columnName": "reason", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "duration", + "columnName": "duration", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutSystemId", + "columnName": "nightscoutSystemId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.nightscoutId", + "columnName": "nightscoutId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpType", + "columnName": "pumpType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpSerial", + "columnName": "pumpSerial", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.temporaryId", + "columnName": "temporaryId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.pumpId", + "columnName": "pumpId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.startId", + "columnName": "startId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "interfaceIDs_backing.endId", + "columnName": "endId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_offlineEvents_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_offlineEvents_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_offlineEvents_isValid", + "unique": false, + "columnNames": [ + "isValid" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_offlineEvents_isValid` ON `${TABLE_NAME}` (`isValid`)" + }, + { + "name": "index_offlineEvents_nightscoutId", + "unique": false, + "columnNames": [ + "nightscoutId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_offlineEvents_nightscoutId` ON `${TABLE_NAME}` (`nightscoutId`)" + }, + { + "name": "index_offlineEvents_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_offlineEvents_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_offlineEvents_timestamp", + "unique": false, + "columnNames": [ + "timestamp" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_offlineEvents_timestamp` ON `${TABLE_NAME}` (`timestamp`)" + } + ], + "foreignKeys": [ + { + "table": "offlineEvents", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "referenceId" + ], + "referencedColumns": [ + "id" + ] + } + ] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'e3558dc3bb3136c37dba4f90c97e8bdd')" + ] + } +} \ No newline at end of file diff --git a/database/src/main/java/info/nightscout/androidaps/database/AppDatabase.kt b/database/src/main/java/info/nightscout/androidaps/database/AppDatabase.kt index 163590525c..948aa705d6 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/AppDatabase.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/AppDatabase.kt @@ -6,14 +6,14 @@ import androidx.room.TypeConverters import info.nightscout.androidaps.database.daos.* import info.nightscout.androidaps.database.entities.* -const val DATABASE_VERSION = 20 +const val DATABASE_VERSION = 21 @Database(version = DATABASE_VERSION, entities = [APSResult::class, Bolus::class, BolusCalculatorResult::class, Carbs::class, EffectiveProfileSwitch::class, ExtendedBolus::class, GlucoseValue::class, ProfileSwitch::class, TemporaryBasal::class, TemporaryTarget::class, TherapyEvent::class, TotalDailyDose::class, APSResultLink::class, MultiwaveBolusLink::class, PreferenceChange::class, VersionChange::class, UserEntry::class, - Food::class, DeviceStatus::class], + Food::class, DeviceStatus::class, OfflineEvent::class], exportSchema = true) @TypeConverters(Converters::class) internal abstract class AppDatabase : RoomDatabase() { @@ -56,4 +56,6 @@ internal abstract class AppDatabase : RoomDatabase() { abstract val deviceStatusDao: DeviceStatusDao + abstract val offlineEventDao: OfflineEventDao + } \ No newline at end of file diff --git a/database/src/main/java/info/nightscout/androidaps/database/AppRepository.kt b/database/src/main/java/info/nightscout/androidaps/database/AppRepository.kt index 93d92392d1..da7449f887 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/AppRepository.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/AppRepository.kt @@ -768,6 +768,64 @@ open class AppRepository @Inject internal constructor( database.extendedBolusDao.getLastId() .subscribeOn(Schedulers.io()) .toWrappedSingle() + + // OFFLINE EVENT + /* + * returns a Pair of the next entity to sync and the ID of the "update". + * The update id might either be the entry id itself if it is a new entry - or the id + * of the update ("historic") entry. The sync counter should be incremented to that id if it was synced successfully. + * + * It is a Maybe as there might be no next element. + * */ + fun getNextSyncElementOfflineEvent(id: Long): Maybe> = + database.offlineEventDao.getNextModifiedOrNewAfter(id) + .flatMap { nextIdElement -> + val nextIdElemReferenceId = nextIdElement.referenceId + if (nextIdElemReferenceId == null) { + Maybe.just(nextIdElement to nextIdElement.id) + } else { + database.offlineEventDao.getCurrentFromHistoric(nextIdElemReferenceId) + .map { it to nextIdElement.id } + } + } + + fun compatGetOfflineEventData(): Single> = + database.offlineEventDao.getOfflineEventData() + .subscribeOn(Schedulers.io()) + + fun getOfflineEventDataFromTime(timestamp: Long, ascending: Boolean): Single> = + database.offlineEventDao.getOfflineEventDataFromTime(timestamp) + .map { if (!ascending) it.reversed() else it } + .subscribeOn(Schedulers.io()) + + fun getOfflineEventDataIncludingInvalidFromTime(timestamp: Long, ascending: Boolean): Single> = + database.offlineEventDao.getOfflineEventDataIncludingInvalidFromTime(timestamp) + .map { if (!ascending) it.reversed() else it } + .subscribeOn(Schedulers.io()) + + fun getOfflineEventDataFromTimeToTime(start: Long, end: Long, ascending: Boolean): Single> = + database.offlineEventDao.getOfflineEventDataFromTimeToTime(start, end) + .map { if (!ascending) it.reversed() else it } + .subscribeOn(Schedulers.io()) + + fun getModifiedOfflineEventsDataFromId(lastId: Long): Single> = + database.offlineEventDao.getModifiedFrom(lastId) + .subscribeOn(Schedulers.io()) + + fun getOfflineEventActiveAt(timestamp: Long): Single> = + database.offlineEventDao.getOfflineEventActiveAt(timestamp) + .subscribeOn(Schedulers.io()) + .toWrappedSingle() + + fun deleteAllOfflineEventEntries() = + database.offlineEventDao.deleteAllEntries() + + fun getLastOfflineEventIdWrapped(): Single> = + database.offlineEventDao.getLastId() + .subscribeOn(Schedulers.io()) + .toWrappedSingle() + + } @Suppress("USELESS_CAST") diff --git a/database/src/main/java/info/nightscout/androidaps/database/Converters.kt b/database/src/main/java/info/nightscout/androidaps/database/Converters.kt index ee645d06e4..371976cc5c 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/Converters.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/Converters.kt @@ -183,4 +183,9 @@ class Converters { return list } -} \ No newline at end of file + @TypeConverter + fun fromOfflineEventReason(reason: OfflineEvent.Reason?) = reason?.name + + @TypeConverter + fun toOfflineEventReason(reason: String?) = reason?.let { OfflineEvent.Reason.valueOf(it) } +} diff --git a/database/src/main/java/info/nightscout/androidaps/database/DatabaseModule.kt b/database/src/main/java/info/nightscout/androidaps/database/DatabaseModule.kt index ec1f24e00b..4f2b1be88d 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/DatabaseModule.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/DatabaseModule.kt @@ -26,13 +26,11 @@ open class DatabaseModule { // .addMigrations(migration6to7) // .addMigrations(migration7to8) // .addMigrations(migration11to12) + .addMigrations(migration20to21) .addCallback(object : Callback() { override fun onOpen(db: SupportSQLiteDatabase) { super.onOpen(db) - db.execSQL("CREATE INDEX IF NOT EXISTS `index_temporaryBasals_end` ON `temporaryBasals` (`timestamp` + `duration`)") - db.execSQL("CREATE INDEX IF NOT EXISTS `index_extendedBoluses_end` ON `extendedBoluses` (`timestamp` + `duration`)") - db.execSQL("CREATE INDEX IF NOT EXISTS `index_temporaryTargets_end` ON `temporaryTargets` (`timestamp` + `duration`)") - db.execSQL("CREATE INDEX IF NOT EXISTS `index_carbs_end` ON `carbs` (`timestamp` + `duration`)") + createCustomIndexes(db) } }) .fallbackToDestructiveMigration() @@ -41,43 +39,34 @@ open class DatabaseModule { @Qualifier annotation class DbFileName - private val migration5to6 = object : Migration(5, 6) { + private fun createCustomIndexes(database: SupportSQLiteDatabase) { + database.execSQL("CREATE INDEX IF NOT EXISTS `index_temporaryBasals_end` ON `temporaryBasals` (`timestamp` + `duration`)") + database.execSQL("CREATE INDEX IF NOT EXISTS `index_extendedBoluses_end` ON `extendedBoluses` (`timestamp` + `duration`)") + database.execSQL("CREATE INDEX IF NOT EXISTS `index_temporaryTargets_end` ON `temporaryTargets` (`timestamp` + `duration`)") + database.execSQL("CREATE INDEX IF NOT EXISTS `index_carbs_end` ON `carbs` (`timestamp` + `duration`)") + database.execSQL("CREATE INDEX IF NOT EXISTS `index_offlineEvents_end` ON `offlineEvents` (`timestamp` + `duration`)") + } + + private fun dropCustomIndexes(database: SupportSQLiteDatabase) { + database.execSQL("DROP INDEX IF EXISTS `index_temporaryBasals_end`") + database.execSQL("DROP INDEX IF EXISTS `index_extendedBoluses_end`") + database.execSQL("DROP INDEX IF EXISTS `index_temporaryTargets_end`") + database.execSQL("DROP INDEX IF EXISTS `index_carbs_end`") + database.execSQL("DROP INDEX IF EXISTS `index_offlineEvents_end`") + } + + private val migration20to21 = object : Migration(20,21) { override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("DROP TABLE IF EXISTS userEntry") - database.execSQL("CREATE TABLE userEntry (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `action` TEXT NOT NULL, `s` TEXT NOT NULL, `values` TEXT NOT NULL)") + database.execSQL("DROP TABLE IF EXISTS offlineEvents") + database.execSQL("CREATE TABLE IF NOT EXISTS `offlineEvents` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `reason` TEXT NOT NULL, `duration` INTEGER NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `offlineEvents`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )") + database.execSQL("CREATE INDEX IF NOT EXISTS `index_offlineEvents_id` ON offlineEvents (`id`)") + database.execSQL("CREATE INDEX IF NOT EXISTS `index_offlineEvents_isValid` ON offlineEvents (`isValid`)") + database.execSQL("CREATE INDEX IF NOT EXISTS `index_offlineEvents_nightscoutId` ON offlineEvents (`nightscoutId`)") + database.execSQL("CREATE INDEX IF NOT EXISTS `index_offlineEvents_referenceId` ON offlineEvents (`referenceId`)") + database.execSQL("CREATE INDEX IF NOT EXISTS `index_offlineEvents_timestamp` ON offlineEvents (`timestamp`)") + // Custom indexes must be dropped on migration to pass room schema checking after upgrade + dropCustomIndexes(database) } } - private val migration6to7 = object : Migration(6, 7) { - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("DROP TABLE IF EXISTS foods") - database.execSQL("CREATE TABLE IF NOT EXISTS foods (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `name` TEXT NOT NULL, `category` TEXT, `subCategory` TEXT, `portion` REAL NOT NULL, `carbs` INTEGER NOT NULL, `fat` INTEGER, `protein` INTEGER, `energy` INTEGER, `unit` TEXT NOT NULL, `gi` INTEGER, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `foods`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )") - database.execSQL("CREATE INDEX IF NOT EXISTS `index_foods_referenceId` ON `foods` (`referenceId`)") - } - } - - private val migration7to8 = object : Migration(7, 8) { - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("DROP TABLE IF EXISTS bolusCalculatorResults") - database.execSQL("CREATE TABLE IF NOT EXISTS bolusCalculatorResults (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `targetBGLow` REAL NOT NULL, `targetBGHigh` REAL NOT NULL, `isf` REAL NOT NULL, `ic` REAL NOT NULL, `bolusIOB` REAL NOT NULL, `wasBolusIOBUsed` INTEGER NOT NULL, `basalIOB` REAL NOT NULL, `wasBasalIOBUsed` INTEGER NOT NULL, `glucoseValue` REAL NOT NULL, `wasGlucoseUsed` INTEGER NOT NULL, `glucoseDifference` REAL NOT NULL, `glucoseInsulin` REAL NOT NULL, `glucoseTrend` REAL NOT NULL, `wasTrendUsed` INTEGER NOT NULL, `trendInsulin` REAL NOT NULL, `cob` REAL NOT NULL, `wasCOBUsed` INTEGER NOT NULL, `cobInsulin` REAL NOT NULL, `carbs` REAL NOT NULL, `wereCarbsUsed` INTEGER NOT NULL, `carbsInsulin` REAL NOT NULL, `otherCorrection` REAL NOT NULL, `wasSuperbolusUsed` INTEGER NOT NULL, `superbolusInsulin` REAL NOT NULL, `wasTempTargetUsed` INTEGER NOT NULL, `totalInsulin` REAL NOT NULL, `percentageCorrection` INTEGER NOT NULL, `profileName` TEXT NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `bolusCalculatorResults`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )") - database.execSQL("CREATE INDEX IF NOT EXISTS `index_bolusCalculatorResults_referenceId` ON bolusCalculatorResults (`referenceId`)") - database.execSQL("CREATE INDEX IF NOT EXISTS `index_bolusCalculatorResults_timestamp` ON bolusCalculatorResults (`timestamp`)") - - database.execSQL("DROP TABLE IF EXISTS mealLinks") - database.execSQL("CREATE TABLE IF NOT EXISTS mealLinks (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `bolusId` INTEGER, `carbsId` INTEGER, `bolusCalcResultId` INTEGER, `superbolusTempBasalId` INTEGER, `noteId` INTEGER, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`bolusId`) REFERENCES `boluses`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`carbsId`) REFERENCES `carbs`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`bolusCalcResultId`) REFERENCES `bolusCalculatorResults`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`superbolusTempBasalId`) REFERENCES `temporaryBasals`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`noteId`) REFERENCES `therapyEvents`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`referenceId`) REFERENCES `mealLinks`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )") - database.execSQL("CREATE INDEX IF NOT EXISTS `index_mealLinks_referenceId` ON mealLinks (`referenceId`)") - database.execSQL("CREATE INDEX IF NOT EXISTS `index_mealLinks_bolusId` ON `mealLinks (`bolusId`)") - database.execSQL("CREATE INDEX IF NOT EXISTS `index_mealLinks_carbsId` ON mealLinks (`carbsId`)") - database.execSQL("CREATE INDEX IF NOT EXISTS `index_mealLinks_bolusCalcResultId` ON mealLinks (`bolusCalcResultId`)") - database.execSQL("CREATE INDEX IF NOT EXISTS `index_mealLinks_superbolusTempBasalId` ON mealLinks (`superbolusTempBasalId`)") - database.execSQL("CREATE INDEX IF NOT EXISTS `index_mealLinks_noteId` ON mealLinks (`noteId`)") - } - } - - private val migration11to12 = object : Migration(11,12) { - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("DROP TABLE IF EXISTS userEntry") - database.execSQL("CREATE TABLE IF NOT EXISTS userEntry (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `action` TEXT NOT NULL, `source` TEXT NOT NULL, `note` TEXT NOT NULL, `values` TEXT NOT NULL)") - } - } } \ No newline at end of file diff --git a/database/src/main/java/info/nightscout/androidaps/database/DelegatedAppDatabase.kt b/database/src/main/java/info/nightscout/androidaps/database/DelegatedAppDatabase.kt index 22ffbb4154..f436dc99e7 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/DelegatedAppDatabase.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/DelegatedAppDatabase.kt @@ -2,6 +2,7 @@ package info.nightscout.androidaps.database import info.nightscout.androidaps.database.daos.* import info.nightscout.androidaps.database.daos.delegated.* +import info.nightscout.androidaps.database.entities.OfflineEvent import info.nightscout.androidaps.database.interfaces.DBEntry internal class DelegatedAppDatabase(val changes: MutableList, val database: AppDatabase) { @@ -25,5 +26,6 @@ internal class DelegatedAppDatabase(val changes: MutableList, val datab val preferenceChangeDao: PreferenceChangeDao = DelegatedPreferenceChangeDao(changes, database.preferenceChangeDao) val foodDao: FoodDao = DelegatedFoodDao(changes, database.foodDao) val deviceStatusDao: DeviceStatusDao = DelegatedDeviceStatusDao(changes, database.deviceStatusDao) + val offlineEventDao: OfflineEventDao = DelegatedOfflineEventDao(changes, database.offlineEventDao) fun clearAllTables() = database.clearAllTables() } \ No newline at end of file diff --git a/database/src/main/java/info/nightscout/androidaps/database/TableNames.kt b/database/src/main/java/info/nightscout/androidaps/database/TableNames.kt index a22a280a2a..6cfc7e1de0 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/TableNames.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/TableNames.kt @@ -14,6 +14,7 @@ const val TABLE_MULTIWAVE_BOLUS_LINKS = "multiwaveBolusLinks" const val TABLE_PROFILE_SWITCHES = "profileSwitches" const val TABLE_TEMPORARY_BASALS = "temporaryBasals" const val TABLE_TEMPORARY_TARGETS = "temporaryTargets" +const val TABLE_OFFLINE_EVENTS = "offlineEvents" const val TABLE_TOTAL_DAILY_DOSES = "totalDailyDoses" const val TABLE_THERAPY_EVENTS = "therapyEvents" const val TABLE_PREFERENCE_CHANGES = "preferenceChanges" diff --git a/database/src/main/java/info/nightscout/androidaps/database/daos/OfflineEventDao.kt b/database/src/main/java/info/nightscout/androidaps/database/daos/OfflineEventDao.kt new file mode 100644 index 0000000000..68d60cd058 --- /dev/null +++ b/database/src/main/java/info/nightscout/androidaps/database/daos/OfflineEventDao.kt @@ -0,0 +1,51 @@ +package info.nightscout.androidaps.database.daos + +import androidx.room.Dao +import androidx.room.Query +import info.nightscout.androidaps.database.TABLE_OFFLINE_EVENTS +import info.nightscout.androidaps.database.entities.OfflineEvent +import io.reactivex.Maybe +import io.reactivex.Single + +@Suppress("FunctionName") +@Dao +internal interface OfflineEventDao : TraceableDao { + + @Query("SELECT * FROM $TABLE_OFFLINE_EVENTS WHERE id = :id") + override fun findById(id: Long): OfflineEvent? + + @Query("DELETE FROM $TABLE_OFFLINE_EVENTS") + override fun deleteAllEntries() + + @Query("SELECT id FROM $TABLE_OFFLINE_EVENTS ORDER BY id DESC limit 1") + fun getLastId(): Maybe + + @Query("SELECT * FROM $TABLE_OFFLINE_EVENTS WHERE nightscoutId = :nsId AND referenceId IS NULL") + fun findByNSId(nsId: String): OfflineEvent? + + @Query("SELECT * FROM $TABLE_OFFLINE_EVENTS WHERE timestamp <= :timestamp AND (timestamp + duration) > :timestamp AND referenceId IS NULL AND isValid = 1 ORDER BY timestamp DESC LIMIT 1") + fun getOfflineEventActiveAt(timestamp: Long): Maybe + + @Query("SELECT * FROM $TABLE_OFFLINE_EVENTS WHERE timestamp >= :timestamp AND isValid = 1 AND referenceId IS NULL ORDER BY timestamp ASC") + fun getOfflineEventDataFromTime(timestamp: Long): Single> + + @Query("SELECT * FROM $TABLE_OFFLINE_EVENTS WHERE timestamp >= :timestamp AND referenceId IS NULL ORDER BY timestamp ASC") + fun getOfflineEventDataIncludingInvalidFromTime(timestamp: Long): Single> + + @Query("SELECT * FROM $TABLE_OFFLINE_EVENTS WHERE timestamp BETWEEN :start AND :end AND isValid = 1 AND referenceId IS NULL ORDER BY timestamp ASC") + fun getOfflineEventDataFromTimeToTime(start: Long, end: Long): Single> + + @Query("SELECT * FROM $TABLE_OFFLINE_EVENTS WHERE isValid = 1 AND referenceId IS NULL ORDER BY timestamp ASC") + fun getOfflineEventData(): Single> + + // This query will be used with v3 to get all changed records + @Query("SELECT * FROM $TABLE_OFFLINE_EVENTS WHERE id > :id AND referenceId IS NULL OR id IN (SELECT DISTINCT referenceId FROM $TABLE_OFFLINE_EVENTS WHERE id > :id) ORDER BY id ASC") + fun getModifiedFrom(id: Long): Single> + + // for WS we need 1 record only + @Query("SELECT * FROM $TABLE_OFFLINE_EVENTS WHERE id > :id ORDER BY id ASC limit 1") + fun getNextModifiedOrNewAfter(id: Long): Maybe + + @Query("SELECT * FROM $TABLE_OFFLINE_EVENTS WHERE id = :referenceId") + fun getCurrentFromHistoric(referenceId: Long): Maybe +} \ No newline at end of file diff --git a/database/src/main/java/info/nightscout/androidaps/database/daos/delegated/DelegatedOfflineEventDao.kt b/database/src/main/java/info/nightscout/androidaps/database/daos/delegated/DelegatedOfflineEventDao.kt new file mode 100644 index 0000000000..fc2bc94c30 --- /dev/null +++ b/database/src/main/java/info/nightscout/androidaps/database/daos/delegated/DelegatedOfflineEventDao.kt @@ -0,0 +1,18 @@ +package info.nightscout.androidaps.database.daos.delegated + +import info.nightscout.androidaps.database.daos.OfflineEventDao +import info.nightscout.androidaps.database.entities.OfflineEvent +import info.nightscout.androidaps.database.interfaces.DBEntry + +internal class DelegatedOfflineEventDao(changes: MutableList, private val dao: OfflineEventDao) : DelegatedDao(changes), OfflineEventDao by dao { + + override fun insertNewEntry(entry: OfflineEvent): Long { + changes.add(entry) + return dao.insertNewEntry(entry) + } + + override fun updateExistingEntry(entry: OfflineEvent): Long { + changes.add(entry) + return dao.updateExistingEntry(entry) + } +} \ No newline at end of file diff --git a/database/src/main/java/info/nightscout/androidaps/database/entities/OfflineEvent.kt b/database/src/main/java/info/nightscout/androidaps/database/entities/OfflineEvent.kt new file mode 100644 index 0000000000..c2af9d5edb --- /dev/null +++ b/database/src/main/java/info/nightscout/androidaps/database/entities/OfflineEvent.kt @@ -0,0 +1,64 @@ +package info.nightscout.androidaps.database.entities + +import androidx.room.Embedded +import androidx.room.Entity +import androidx.room.ForeignKey +import androidx.room.Index +import androidx.room.PrimaryKey +import info.nightscout.androidaps.database.TABLE_OFFLINE_EVENTS +import info.nightscout.androidaps.database.embedments.InterfaceIDs +import info.nightscout.androidaps.database.interfaces.DBEntryWithTimeAndDuration +import info.nightscout.androidaps.database.interfaces.TraceableDBEntry +import java.util.* + +@Entity(tableName = TABLE_OFFLINE_EVENTS, + foreignKeys = [ForeignKey( + entity = OfflineEvent::class, + parentColumns = ["id"], + childColumns = ["referenceId"])], + indices = [ + Index("id"), + Index("isValid"), + Index("nightscoutId"), + Index("referenceId"), + Index("timestamp") + ]) +data class OfflineEvent( + @PrimaryKey(autoGenerate = true) + override var id: Long = 0, + override var version: Int = 0, + override var dateCreated: Long = -1, + override var isValid: Boolean = true, + override var referenceId: Long? = null, + @Embedded + override var interfaceIDs_backing: InterfaceIDs? = InterfaceIDs(), + override var timestamp: Long, + override var utcOffset: Long = TimeZone.getDefault().getOffset(timestamp).toLong(), + var reason: Reason, + override var duration: Long // in millis +) : TraceableDBEntry, DBEntryWithTimeAndDuration { + + fun contentEqualsTo(other: OfflineEvent): Boolean = + timestamp == other.timestamp && + utcOffset == other.utcOffset && + reason == other.reason && + duration == other.duration && + isValid == other.isValid + + fun isRecordDeleted(other: OfflineEvent): Boolean = + isValid && !other.isValid + + enum class Reason { + DISCONNECT_PUMP, + SUSPEND, + DISABLE_LOOP, + SUPER_BOLUS, + OTHER + ; + + companion object { + + fun fromString(reason: String?) = values().firstOrNull { it.name == reason } ?: OTHER + } + } +} \ No newline at end of file diff --git a/database/src/main/java/info/nightscout/androidaps/database/entities/UserEntry.kt b/database/src/main/java/info/nightscout/androidaps/database/entities/UserEntry.kt index 1672944892..00e9a3e25d 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/entities/UserEntry.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/entities/UserEntry.kt @@ -43,6 +43,8 @@ data class UserEntry( OPEN_LOOP_MODE (ColorGroup.Loop), LOOP_DISABLED (ColorGroup.Loop), LOOP_ENABLED (ColorGroup.Loop), + LOOP_CHANGE (ColorGroup.Loop), + LOOP_REMOVED (ColorGroup.Loop), RECONNECT (ColorGroup.Pump), DISCONNECT (ColorGroup.Pump), RESUME (ColorGroup.Loop), diff --git a/database/src/main/java/info/nightscout/androidaps/database/entities/ValueWithUnit.kt b/database/src/main/java/info/nightscout/androidaps/database/entities/ValueWithUnit.kt index 7cd94956f0..55709888f7 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/entities/ValueWithUnit.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/entities/ValueWithUnit.kt @@ -1,7 +1,5 @@ package info.nightscout.androidaps.database.entities -import androidx.annotation.StringRes - sealed class ValueWithUnit { object UNKNOWN : ValueWithUnit() // formerly None used as fallback @@ -34,6 +32,8 @@ sealed class ValueWithUnit { data class TherapyEventTTReason(val value: TemporaryTarget.Reason) : ValueWithUnit() + data class OfflineEventReason(val value: OfflineEvent.Reason) : ValueWithUnit() + fun value(): Any? { return when(this) { is Gram -> this.value @@ -47,6 +47,7 @@ sealed class ValueWithUnit { is SimpleString -> this.value is TherapyEventMeterType -> this.value is TherapyEventTTReason -> this.value + is OfflineEventReason -> this.value is TherapyEventType -> this.value is Timestamp -> this.value is UnitPerHour -> this.value diff --git a/database/src/main/java/info/nightscout/androidaps/database/transactions/CancelCurrentOfflineEventIfAnyTransaction.kt b/database/src/main/java/info/nightscout/androidaps/database/transactions/CancelCurrentOfflineEventIfAnyTransaction.kt new file mode 100644 index 0000000000..6d5e1c1794 --- /dev/null +++ b/database/src/main/java/info/nightscout/androidaps/database/transactions/CancelCurrentOfflineEventIfAnyTransaction.kt @@ -0,0 +1,25 @@ +package info.nightscout.androidaps.database.transactions + +import info.nightscout.androidaps.database.entities.OfflineEvent +import info.nightscout.androidaps.database.interfaces.end + +class CancelCurrentOfflineEventIfAnyTransaction( + val timestamp: Long +) : Transaction() { + + override fun run(): TransactionResult { + val result = TransactionResult() + val current = database.offlineEventDao.getOfflineEventActiveAt(timestamp).blockingGet() + if (current != null) { + current.end = timestamp + database.offlineEventDao.updateExistingEntry(current) + result.updated.add(current) + } + return result + } + + class TransactionResult { + + val updated = mutableListOf() + } +} \ No newline at end of file diff --git a/database/src/main/java/info/nightscout/androidaps/database/transactions/InsertAndCancelCurrentOfflineEventTransaction.kt b/database/src/main/java/info/nightscout/androidaps/database/transactions/InsertAndCancelCurrentOfflineEventTransaction.kt new file mode 100644 index 0000000000..f4dd63fe81 --- /dev/null +++ b/database/src/main/java/info/nightscout/androidaps/database/transactions/InsertAndCancelCurrentOfflineEventTransaction.kt @@ -0,0 +1,30 @@ +package info.nightscout.androidaps.database.transactions + +import info.nightscout.androidaps.database.entities.OfflineEvent +import info.nightscout.androidaps.database.interfaces.end + +class InsertAndCancelCurrentOfflineEventTransaction( + val offlineEvent: OfflineEvent +) : Transaction() { + + constructor(timestamp: Long, duration: Long, reason: OfflineEvent.Reason) : + this(OfflineEvent(timestamp = timestamp, reason = reason, duration = duration)) + + override fun run(): TransactionResult { + val result = TransactionResult() + val current = database.offlineEventDao.getOfflineEventActiveAt(offlineEvent.timestamp).blockingGet() + if (current != null) { + current.end = offlineEvent.timestamp + database.offlineEventDao.updateExistingEntry(current) + result.updated.add(current) + } + database.offlineEventDao.insertNewEntry(offlineEvent) + result.inserted.add(offlineEvent) + return result + } + + class TransactionResult { + val inserted = mutableListOf() + val updated = mutableListOf() + } +} \ No newline at end of file diff --git a/database/src/main/java/info/nightscout/androidaps/database/transactions/InsertTemporaryTargetAndCancelCurrentTransaction.kt b/database/src/main/java/info/nightscout/androidaps/database/transactions/InsertAndCancelCurrentTemporaryTargetTransaction.kt similarity index 90% rename from database/src/main/java/info/nightscout/androidaps/database/transactions/InsertTemporaryTargetAndCancelCurrentTransaction.kt rename to database/src/main/java/info/nightscout/androidaps/database/transactions/InsertAndCancelCurrentTemporaryTargetTransaction.kt index c100d0c358..87867309b6 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/transactions/InsertTemporaryTargetAndCancelCurrentTransaction.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/transactions/InsertAndCancelCurrentTemporaryTargetTransaction.kt @@ -3,9 +3,9 @@ package info.nightscout.androidaps.database.transactions import info.nightscout.androidaps.database.entities.TemporaryTarget import info.nightscout.androidaps.database.interfaces.end -class InsertTemporaryTargetAndCancelCurrentTransaction( +class InsertAndCancelCurrentTemporaryTargetTransaction( val temporaryTarget: TemporaryTarget -) : Transaction() { +) : Transaction() { constructor(timestamp: Long, duration: Long, reason: TemporaryTarget.Reason, lowTarget: Double, highTarget: Double) : this(TemporaryTarget(timestamp = timestamp, reason = reason, lowTarget = lowTarget, highTarget = highTarget, duration = duration)) diff --git a/database/src/main/java/info/nightscout/androidaps/database/transactions/InvalidateOfflineEventTransaction.kt b/database/src/main/java/info/nightscout/androidaps/database/transactions/InvalidateOfflineEventTransaction.kt new file mode 100644 index 0000000000..9db9b8e84a --- /dev/null +++ b/database/src/main/java/info/nightscout/androidaps/database/transactions/InvalidateOfflineEventTransaction.kt @@ -0,0 +1,10 @@ +package info.nightscout.androidaps.database.transactions + +class InvalidateOfflineEventTransaction(val id: Long) : Transaction() { + override fun run() { + val offlineEvent = database.offlineEventDao.findById(id) + ?: throw IllegalArgumentException("There is no such OfflineEvent with the specified ID.") + offlineEvent.isValid = false + database.offlineEventDao.updateExistingEntry(offlineEvent) + } +} \ No newline at end of file diff --git a/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsOfflineEventTransaction.kt b/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsOfflineEventTransaction.kt new file mode 100644 index 0000000000..d7a741436c --- /dev/null +++ b/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncNsOfflineEventTransaction.kt @@ -0,0 +1,73 @@ +package info.nightscout.androidaps.database.transactions + +import info.nightscout.androidaps.database.entities.OfflineEvent +import info.nightscout.androidaps.database.interfaces.end +import kotlin.math.abs + +/** + * Sync the OfflineEvent from NS + */ +class SyncNsOfflineEventTransaction(private val offlineEvent: OfflineEvent, private val invalidateByNsOnly: Boolean) : Transaction() { + + override fun run(): TransactionResult { + val result = TransactionResult() + + if (offlineEvent.duration != 0L) { + // not ending event + val current: OfflineEvent? = + offlineEvent.interfaceIDs.nightscoutId?.let { + database.offlineEventDao.findByNSId(it) + } + + if (current != null) { + // nsId exists, allow only invalidation + if (current.isValid && !offlineEvent.isValid) { + current.isValid = false + database.offlineEventDao.updateExistingEntry(current) + result.invalidated.add(current) + } + return result + } + + if (invalidateByNsOnly) return result + + // not known nsId + val running = database.offlineEventDao.getOfflineEventActiveAt(offlineEvent.timestamp).blockingGet() + if (running != null && abs(running.timestamp - offlineEvent.timestamp) < 1000 && running.interfaceIDs.nightscoutId == null) { // allow missing milliseconds + // the same record, update nsId only + running.interfaceIDs.nightscoutId = offlineEvent.interfaceIDs.nightscoutId + database.offlineEventDao.updateExistingEntry(running) + result.updatedNsId.add(running) + } else if (running != null) { + // another running record. end current and insert new + running.end = offlineEvent.timestamp + database.offlineEventDao.updateExistingEntry(running) + database.offlineEventDao.insertNewEntry(offlineEvent) + result.ended.add(running) + result.inserted.add(offlineEvent) + } else { + database.offlineEventDao.insertNewEntry(offlineEvent) + result.inserted.add(offlineEvent) + } + return result + + } else { + // ending event + val running = database.offlineEventDao.getOfflineEventActiveAt(offlineEvent.timestamp).blockingGet() + if (running != null) { + running.end = offlineEvent.timestamp + database.offlineEventDao.updateExistingEntry(running) + result.ended.add(running) + } + } + return result + } + + class TransactionResult { + + val updatedNsId = mutableListOf() + val inserted = mutableListOf() + val invalidated = mutableListOf() + val ended = mutableListOf() + } +} \ No newline at end of file diff --git a/database/src/main/java/info/nightscout/androidaps/database/transactions/UpdateNsIdOfflineEventTransaction.kt b/database/src/main/java/info/nightscout/androidaps/database/transactions/UpdateNsIdOfflineEventTransaction.kt new file mode 100644 index 0000000000..ebb19ce486 --- /dev/null +++ b/database/src/main/java/info/nightscout/androidaps/database/transactions/UpdateNsIdOfflineEventTransaction.kt @@ -0,0 +1,12 @@ +package info.nightscout.androidaps.database.transactions + +import info.nightscout.androidaps.database.entities.OfflineEvent + +class UpdateNsIdOfflineEventTransaction(val offlineEvent: OfflineEvent) : Transaction() { + + override fun run() { + val current = database.offlineEventDao.findById(offlineEvent.id) + if (current != null && current.interfaceIDs.nightscoutId != offlineEvent.interfaceIDs.nightscoutId) + database.offlineEventDao.updateExistingEntry(offlineEvent) + } +} \ No newline at end of file diff --git a/database/src/main/java/info/nightscout/androidaps/database/transactions/UpdateTemporaryTargetTransaction.kt b/database/src/main/java/info/nightscout/androidaps/database/transactions/UpdateTemporaryTargetTransaction.kt deleted file mode 100644 index f07a57fce2..0000000000 --- a/database/src/main/java/info/nightscout/androidaps/database/transactions/UpdateTemporaryTargetTransaction.kt +++ /dev/null @@ -1,9 +0,0 @@ -package info.nightscout.androidaps.database.transactions - -import info.nightscout.androidaps.database.entities.TemporaryTarget - -class UpdateTemporaryTargetTransaction(val temporaryTarget: TemporaryTarget) : Transaction() { - override fun run() { - database.temporaryTargetDao.updateExistingEntry(temporaryTarget) - } -} \ No newline at end of file