From a2fbab02f9662be6acb3fd67d5304c3fabbc9a5b Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Fri, 19 Mar 2021 18:49:34 +0100 Subject: [PATCH] food -> Room --- .../info/nightscout/androidaps/MainApp.kt | 1 + .../androidaps/db/CompatDBHelper.kt | 6 + .../dependencyInjection/DataClassesModule.kt | 2 - .../dependencyInjection/WorkersModule.kt | 2 + .../androidaps/events/EventNsFood.kt | 20 - .../androidaps/plugins/general/food/Food.java | 145 - .../plugins/general/food/FoodFragment.kt | 110 +- .../plugins/general/food/FoodPlugin.kt | 89 +- .../plugins/general/food/FoodService.java | 359 -- .../general/maintenance/ImportExportPrefs.kt | 10 +- .../maintenance/MaintenanceFragment.kt | 3 - .../nsclient/services/NSClientService.java | 46 +- .../androidaps/receivers/DataReceiver.kt | 28 +- .../nightscout/androidaps/services/Intents.kt | 1 + app/src/main/res/layout/food_fragment.xml | 14 +- app/src/main/res/layout/food_item.xml | 36 +- app/src/main/res/values/strings.xml | 2 +- .../interfaces/ImportExportPrefsInterface.kt | 2 +- .../nightscout/androidaps/utils/JsonHelper.kt | 17 +- .../utils/extensions/FoodExtension.kt | 40 + .../7.json | 2924 +++++++++++++++++ .../androidaps/database/AppDatabase.kt | 7 +- .../androidaps/database/AppRepository.kt | 9 + .../androidaps/database/DatabaseModule.kt | 6 + .../database/DelegatedAppDatabase.kt | 1 + .../androidaps/database/TableNames.kt | 1 + .../androidaps/database/daos/FoodDao.kt | 25 + .../androidaps/database/daos/TraceableDao.kt | 1 - .../daos/delegated/DelegatedFoodDao.kt | 18 + .../androidaps/database/entities/Food.kt | 73 + .../androidaps/database/entities/UserEntry.kt | 1 + .../InsertOrUpdateFoodTransaction.kt | 28 + .../transactions/InvalidateFoodTransaction.kt | 11 + .../transactions/SyncFoodTransaction.kt | 42 + 34 files changed, 3437 insertions(+), 643 deletions(-) delete mode 100644 app/src/main/java/info/nightscout/androidaps/events/EventNsFood.kt delete mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/general/food/Food.java delete mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/general/food/FoodService.java create mode 100644 core/src/main/java/info/nightscout/androidaps/utils/extensions/FoodExtension.kt create mode 100644 database/schemas/info.nightscout.androidaps.database.AppDatabase/7.json create mode 100644 database/src/main/java/info/nightscout/androidaps/database/daos/FoodDao.kt create mode 100644 database/src/main/java/info/nightscout/androidaps/database/daos/delegated/DelegatedFoodDao.kt create mode 100644 database/src/main/java/info/nightscout/androidaps/database/entities/Food.kt create mode 100644 database/src/main/java/info/nightscout/androidaps/database/transactions/InsertOrUpdateFoodTransaction.kt create mode 100644 database/src/main/java/info/nightscout/androidaps/database/transactions/InvalidateFoodTransaction.kt create mode 100644 database/src/main/java/info/nightscout/androidaps/database/transactions/SyncFoodTransaction.kt diff --git a/app/src/main/java/info/nightscout/androidaps/MainApp.kt b/app/src/main/java/info/nightscout/androidaps/MainApp.kt index da263b17de..420f27427d 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainApp.kt +++ b/app/src/main/java/info/nightscout/androidaps/MainApp.kt @@ -109,6 +109,7 @@ class MainApp : DaggerApplication() { filter.addAction(Intents.ACTION_NEW_PROFILE) filter.addAction(Intents.ACTION_NEW_MBG) filter.addAction(Intents.ACTION_NEW_CAL) + filter.addAction(Intents.ACTION_FOOD) LocalBroadcastManager.getInstance(this).registerReceiver(DataReceiver(), filter) filter = IntentFilter() filter.addAction(Intent.ACTION_TIME_CHANGED) 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 08f7cb01ad..aef3c28a3b 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/CompatDBHelper.kt +++ b/app/src/main/java/info/nightscout/androidaps/db/CompatDBHelper.kt @@ -1,9 +1,11 @@ package info.nightscout.androidaps.db import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.entities.Food import info.nightscout.androidaps.database.entities.GlucoseValue import info.nightscout.androidaps.database.entities.TemporaryTarget import info.nightscout.androidaps.database.entities.TherapyEvent +import info.nightscout.androidaps.events.EventFoodDatabaseChanged import info.nightscout.androidaps.events.EventNewBG import info.nightscout.androidaps.events.EventTempTargetChange import info.nightscout.androidaps.events.EventTherapyEventChange @@ -44,5 +46,9 @@ class CompatDBHelper @Inject constructor( aapsLogger.debug(LTag.DATABASE, "Firing EventTherapyEventChange") rxBus.send(EventTherapyEventChange()) } + it.filterIsInstance().firstOrNull()?.let { + aapsLogger.debug(LTag.DATABASE, "Firing EventFoodDatabaseChanged") + rxBus.send(EventFoodDatabaseChanged()) + } } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/DataClassesModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/DataClassesModule.kt index 98f15e668c..de4df287a4 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/DataClassesModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/DataClassesModule.kt @@ -3,7 +3,6 @@ package info.nightscout.androidaps.dependencyInjection import dagger.Module import dagger.android.ContributesAndroidInjector import info.nightscout.androidaps.db.DatabaseHelper -import info.nightscout.androidaps.plugins.general.food.FoodService import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus import info.nightscout.androidaps.plugins.treatments.TreatmentService import info.nightscout.androidaps.utils.wizard.BolusWizard @@ -17,7 +16,6 @@ abstract class DataClassesModule { @ContributesAndroidInjector abstract fun DatabaseHelperInjector(): DatabaseHelper @ContributesAndroidInjector abstract fun treatmentServiceInjector(): TreatmentService - @ContributesAndroidInjector abstract fun foodServiceInjector(): FoodService @ContributesAndroidInjector abstract fun bolusWizardInjector(): BolusWizard @ContributesAndroidInjector abstract fun quickWizardEntryInjector(): QuickWizardEntry diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/WorkersModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/WorkersModule.kt index 5f48be718c..a82965d391 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/WorkersModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/WorkersModule.kt @@ -2,6 +2,7 @@ package info.nightscout.androidaps.dependencyInjection import dagger.Module import dagger.android.ContributesAndroidInjector +import info.nightscout.androidaps.plugins.general.food.FoodPlugin import info.nightscout.androidaps.plugins.general.nsclient.NSClientWorker import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin @@ -22,4 +23,5 @@ abstract class WorkersModule { @ContributesAndroidInjector abstract fun contributesNSProfileWorker(): NSProfilePlugin.NSProfileWorker @ContributesAndroidInjector abstract fun contributesSmsCommunicatorWorker(): SmsCommunicatorPlugin.SmsCommunicatorWorker @ContributesAndroidInjector abstract fun contributesNSClientWorker(): NSClientWorker + @ContributesAndroidInjector abstract fun contributesFoodWorker(): FoodPlugin.FoodWorker } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventNsFood.kt b/app/src/main/java/info/nightscout/androidaps/events/EventNsFood.kt deleted file mode 100644 index 67bc23b168..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventNsFood.kt +++ /dev/null @@ -1,20 +0,0 @@ -package info.nightscout.androidaps.events - -import org.json.JSONArray - -/** - * Event which is published with data fetched from NightScout specific for the - * Food-class. - * - * Payload is the from NS retrieved JSON-String which should be handled by all - * subscriber. - */ - -class EventNsFood(val mode: Int, val foods: JSONArray) : Event() { - - companion object { - val ADD = 0 - val UPDATE = 1 - val REMOVE = 2 - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/food/Food.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/food/Food.java deleted file mode 100644 index 61c5f0b16f..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/food/Food.java +++ /dev/null @@ -1,145 +0,0 @@ -package info.nightscout.androidaps.plugins.general.food; - -import androidx.annotation.NonNull; - -import com.j256.ormlite.field.DatabaseField; -import com.j256.ormlite.table.DatabaseTable; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.Objects; - -import info.nightscout.androidaps.utils.JsonHelper; - -/** - * Created by mike on 20.09.2017. - */ - -@DatabaseTable(tableName = Food.TABLE_FOODS) -public class Food { - static final String TABLE_FOODS = "Foods"; - - @DatabaseField(id = true) - public long key; - - @DatabaseField - public boolean isValid = true; - - @DatabaseField - public String _id; // NS _id - - @DatabaseField - public String name; - - @DatabaseField - public String category; - - @DatabaseField - public String subcategory; - - // Example: - // name="juice" portion=250 units="ml" carbs=12 - // means 250ml of juice has 12g of carbs - - @DatabaseField - public double portion; // common portion in "units" - - @DatabaseField - public int carbs; // in grams - - @DatabaseField - public int fat = 0; // in grams - - @DatabaseField - public int protein = 0; // in grams - - @DatabaseField - public int energy = 0; // in kJ - - @DatabaseField - public String units = "g"; - - @DatabaseField - public int gi; // not used yet - - private Food() { - key = System.currentTimeMillis(); - } - - public static Food createFromJson(JSONObject json) throws JSONException { - Food food = new Food(); - if ("food".equals(JsonHelper.safeGetString(json, "type"))) { - food._id = JsonHelper.safeGetString(json, "_id"); - food.category = JsonHelper.safeGetString(json, "category"); - food.subcategory = JsonHelper.safeGetString(json, "subcategory"); - food.name = JsonHelper.safeGetString(json, "name"); - food.units = JsonHelper.safeGetString(json, "unit"); - food.portion = JsonHelper.safeGetDouble(json, "portion"); - food.carbs = JsonHelper.safeGetInt(json, "carbs"); - food.gi = JsonHelper.safeGetInt(json, "gi"); - food.energy = JsonHelper.safeGetInt(json, "energy"); - food.protein = JsonHelper.safeGetInt(json, "protein"); - food.fat = JsonHelper.safeGetInt(json, "fat"); - } - - return food; - } - - public boolean isEqual(Food other) { - if (portion != other.portion) - return false; - if (carbs != other.carbs) - return false; - if (fat != other.fat) - return false; - if (protein != other.protein) - return false; - if (energy != other.energy) - return false; - if (gi != other.gi) - return false; - if (!Objects.equals(_id, other._id)) - return false; - if (!Objects.equals(name, other.name)) - return false; - if (!Objects.equals(category, other.category)) - return false; - if (!Objects.equals(subcategory, other.subcategory)) - return false; - return Objects.equals(units, other.units); - } - - public void copyFrom(Food other) { - isValid = other.isValid; - _id = other._id; - name = other.name; - category = other.category; - subcategory = other.subcategory; - portion = other.portion; - carbs = other.carbs; - fat = other.fat; - protein = other.protein; - energy = other.energy; - units = other.units; - gi = other.gi; - } - - @Override @NonNull - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("_id=" + _id + ";"); - sb.append("isValid=" + isValid + ";"); - sb.append("name=" + name + ";"); - sb.append("category=" + category + ";"); - sb.append("subcategory=" + subcategory + ";"); - sb.append("portion=" + portion + ";"); - sb.append("carbs=" + carbs + ";"); - sb.append("protein=" + protein + ";"); - sb.append("energy=" + energy + ";"); - sb.append("units=" + units + ";"); - sb.append("gi=" + gi + ";"); - - return sb.toString(); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/food/FoodFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/food/FoodFragment.kt index f9ce2e17bb..10ff172727 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/food/FoodFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/food/FoodFragment.kt @@ -1,7 +1,6 @@ package info.nightscout.androidaps.plugins.general.food import android.annotation.SuppressLint -import android.content.DialogInterface import android.graphics.Paint import android.os.Bundle import android.text.Editable @@ -15,20 +14,30 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import dagger.android.support.DaggerFragment import info.nightscout.androidaps.R -import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.entities.Food +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.transactions.InvalidateFoodTransaction import info.nightscout.androidaps.databinding.FoodFragmentBinding import info.nightscout.androidaps.databinding.FoodItemBinding import info.nightscout.androidaps.events.EventFoodDatabaseChanged +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.bus.RxBusWrapper -import info.nightscout.androidaps.plugins.general.food.FoodFragment.RecyclerViewAdapter.FoodsViewHolder import info.nightscout.androidaps.plugins.general.nsclient.NSUpload +import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.alertDialogs.OKDialog +import info.nightscout.androidaps.utils.extensions.toVisibility import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers +import io.reactivex.Completable import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxkotlin.subscribeBy import java.util.* +import java.util.concurrent.TimeUnit import javax.inject.Inject import kotlin.collections.ArrayList @@ -36,10 +45,11 @@ class FoodFragment : DaggerFragment() { @Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var rxBus: RxBusWrapper + @Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var fabricPrivacy: FabricPrivacy - @Inject lateinit var foodPlugin: FoodPlugin @Inject lateinit var nsUpload: NSUpload + @Inject lateinit var repository: AppRepository @Inject lateinit var uel: UserEntryLogger private val disposable = CompositeDisposable() @@ -62,8 +72,23 @@ class FoodFragment : DaggerFragment() { binding.recyclerview.setHasFixedSize(true) binding.recyclerview.layoutManager = LinearLayoutManager(view.context) - binding.recyclerview.adapter = RecyclerViewAdapter(foodPlugin.service?.foodData - ?: ArrayList()) + + binding.refreshFromNightscout.setOnClickListener { + context?.let { context -> + OKDialog.showConfirmation(context, resourceHelper.gs(R.string.refresheventsfromnightscout) + " ?", { + uel.log(Action.FOOD_FROM_NS) + disposable += Completable.fromAction { repository.deleteAllFoods() } + .subscribeOn(aapsSchedulers.io) + .observeOn(aapsSchedulers.main) + .subscribeBy( + onError = { aapsLogger.error("Error removing foods", it) }, + onComplete = { rxBus.send(EventFoodDatabaseChanged()) } + ) + + rxBus.send(EventNSClientRestart()) + }) + } + } binding.clearfilter.setOnClickListener { binding.filter.setText("") @@ -99,10 +124,6 @@ class FoodFragment : DaggerFragment() { override fun afterTextChanged(s: Editable) {} }) - loadData() - fillCategories() - fillSubcategories() - filterData() } @Synchronized @@ -111,9 +132,23 @@ class FoodFragment : DaggerFragment() { disposable.add(rxBus .toObservable(EventFoodDatabaseChanged::class.java) .observeOn(aapsSchedulers.main) - .subscribe({ updateGui() }, fabricPrivacy::logException) + .debounce(1L, TimeUnit.SECONDS) + .subscribe({ swapAdapter() }, fabricPrivacy::logException) ) - updateGui() + swapAdapter() + } + + private fun swapAdapter() { + disposable += repository + .getFoodData() + .observeOn(aapsSchedulers.main) + .subscribe { list -> + unfiltered = list + fillCategories() + fillSubcategories() + filterData() + binding.recyclerview.swapAdapter(RecyclerViewAdapter(filtered), true) + } } @Synchronized @@ -128,14 +163,11 @@ class FoodFragment : DaggerFragment() { _binding = null } - private fun loadData() { - unfiltered = foodPlugin.service?.foodData ?: ArrayList() - } - private fun fillCategories() { val catSet: MutableSet = HashSet() for (f in unfiltered) { - if (f.category != null && f.category != "") catSet.add(f.category) + val category = f.category + if (!category.isNullOrBlank()) catSet.add(category) } // make it unique val categories = ArrayList(catSet) @@ -151,7 +183,10 @@ class FoodFragment : DaggerFragment() { val subCatSet: MutableSet = HashSet() if (categoryFilter != resourceHelper.gs(R.string.none)) { for (f in unfiltered) { - if (f.category != null && f.category == categoryFilter) if (f.subcategory != null && f.subcategory != "") subCatSet.add(f.subcategory) + if (f.category != null && f.category == categoryFilter) { + val subCategory = f.subCategory + if (!subCategory.isNullOrEmpty()) subCatSet.add(subCategory) + } } } // make it unique @@ -169,21 +204,19 @@ class FoodFragment : DaggerFragment() { val subcategoryFilter = binding.subcategory.selectedItem.toString() val newFiltered = ArrayList() for (f in unfiltered) { - if (f.name == null || f.category == null || f.subcategory == null) continue - if (subcategoryFilter != resourceHelper.gs(R.string.none) && f.subcategory != subcategoryFilter) continue + if (f.category == null || f.subCategory == null) continue + if (subcategoryFilter != resourceHelper.gs(R.string.none) && f.subCategory != subcategoryFilter) continue if (categoryFilter != resourceHelper.gs(R.string.none) && f.category != categoryFilter) continue if (textFilter != "" && !f.name.toLowerCase(Locale.getDefault()).contains(textFilter.toLowerCase(Locale.getDefault()))) continue newFiltered.add(f) } filtered = newFiltered - updateGui() - } - - private fun updateGui() { binding.recyclerview.swapAdapter(RecyclerViewAdapter(filtered), true) } - inner class RecyclerViewAdapter internal constructor(var foodList: List) : RecyclerView.Adapter() { + fun Int?.isNotZero(): Boolean = this != null && this != 0 + + inner class RecyclerViewAdapter internal constructor(private var foodList: List) : RecyclerView.Adapter() { override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): FoodsViewHolder { val v = LayoutInflater.from(viewGroup.context).inflate(R.layout.food_item, viewGroup, false) @@ -193,16 +226,16 @@ class FoodFragment : DaggerFragment() { @SuppressLint("SetTextI18n") override fun onBindViewHolder(holder: FoodsViewHolder, position: Int) { val food = foodList[position] - holder.binding.nsSign.visibility = if (food._id != null) View.VISIBLE else View.GONE + holder.binding.nsSign.visibility = (food.interfaceIDs.nightscoutId != null).toVisibility() holder.binding.name.text = food.name - holder.binding.portion.text = food.portion.toString() + food.units + holder.binding.portion.text = food.portion.toString() + food.unit holder.binding.carbs.text = food.carbs.toString() + resourceHelper.gs(R.string.shortgramm) holder.binding.fat.text = resourceHelper.gs(R.string.shortfat) + ": " + food.fat + resourceHelper.gs(R.string.shortgramm) - if (food.fat == 0) holder.binding.fat.visibility = View.INVISIBLE + holder.binding.fat.visibility = food.fat.isNotZero().toVisibility() holder.binding.protein.text = resourceHelper.gs(R.string.shortprotein) + ": " + food.protein + resourceHelper.gs(R.string.shortgramm) - if (food.protein == 0) holder.binding.protein.visibility = View.INVISIBLE + holder.binding.protein.visibility = food.protein.isNotZero().toVisibility() holder.binding.energy.text = resourceHelper.gs(R.string.shortenergy) + ": " + food.energy + resourceHelper.gs(R.string.shortkilojoul) - if (food.energy == 0) holder.binding.energy.visibility = View.INVISIBLE + holder.binding.energy.visibility = food.energy.isNotZero().toVisibility() holder.binding.remove.tag = food } @@ -216,12 +249,17 @@ class FoodFragment : DaggerFragment() { binding.remove.setOnClickListener { v: View -> val food = v.tag as Food activity?.let { activity -> - OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.confirmation), resourceHelper.gs(R.string.removerecord) + "\n" + food.name, DialogInterface.OnClickListener { _: DialogInterface?, _: Int -> + OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.removerecord) + "\n" + food.name, { uel.log(Action.FOOD_REMOVED, food.name) - if (food._id != null && food._id != "") { - nsUpload.removeFoodFromNS(food._id) - } - foodPlugin.service?.delete(food) + disposable += repository.runTransactionForResult(InvalidateFoodTransaction(food.id)) + .subscribe({ + val id = food.interfaceIDs.nightscoutId + if (NSUpload.isIdValid(id)) nsUpload.removeFoodFromNS(id) + // no create at the moment + // else uploadQueue.removeID("dbAdd", food.timestamp.toString()) + }, { + aapsLogger.error(LTag.BGSOURCE, "Error while invalidating food", it) + }) }, null) } } @@ -229,4 +267,4 @@ class FoodFragment : DaggerFragment() { } } } -} +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/food/FoodPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/food/FoodPlugin.kt index d44550a11d..6b5125eb0a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/food/FoodPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/food/FoodPlugin.kt @@ -1,12 +1,28 @@ package info.nightscout.androidaps.plugins.general.food +import android.content.Context +import androidx.work.Worker +import androidx.work.WorkerParameters import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.entities.Food +import info.nightscout.androidaps.database.transactions.SyncFoodTransaction import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginDescription import info.nightscout.androidaps.interfaces.PluginType import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.receivers.BundleStore +import info.nightscout.androidaps.receivers.DataReceiver +import info.nightscout.androidaps.utils.JsonHelper +import info.nightscout.androidaps.utils.extensions.foodFromJson 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 org.json.JSONArray +import org.json.JSONObject import javax.inject.Inject import javax.inject.Singleton @@ -25,10 +41,75 @@ class FoodPlugin @Inject constructor( aapsLogger, resourceHelper, injector ) { - var service: FoodService? = null + private val disposable = CompositeDisposable() - override fun onStart() { - super.onStart() - service = FoodService(injector) + // cannot be inner class because of needed injection + class FoodWorker( + context: Context, + params: WorkerParameters + ) : Worker(context, params) { + + @Inject lateinit var injector: HasAndroidInjector + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var repository: AppRepository + @Inject lateinit var sp: SP + @Inject lateinit var bundleStore: BundleStore + @Inject lateinit var foodPlugin: FoodPlugin + + init { + (context.applicationContext as HasAndroidInjector).androidInjector().inject(this) + } + + override fun doWork(): Result { + aapsLogger.debug(LTag.DATAFOOD, "Received Food Data: $inputData}") + val bundle = bundleStore.pickup(inputData.getLong(DataReceiver.STORE_KEY, -1)) + ?: return Result.failure() + + var ret = Result.success() + + val foodsString = bundle.getString("foods") ?: return Result.failure() + val foods = JSONArray(foodsString) + for (index in 0 until foods.length()) { + val jsonFood: JSONObject = foods.getJSONObject(index) + + if (JsonHelper.safeGetString(jsonFood, "type") != "food") continue + + when (JsonHelper.safeGetString(jsonFood, "action")) { + "remove" -> { + val delFood = Food( + name = "", + portion = 0.0, + carbs = 0, + isValid = false + ).also { it.interfaceIDs.nightscoutId = JsonHelper.safeGetString(jsonFood, "_id") } + + foodPlugin.disposable += repository.runTransactionForResult(SyncFoodTransaction(delFood)).subscribe({ result -> + result.invalidated.forEach { aapsLogger.debug(LTag.DATAFOOD, "Invalidated food ${it.interfaceIDs.nightscoutId}") } + }, { + aapsLogger.error(LTag.DATAFOOD, "Error while removing food", it) + ret = Result.failure() + }) + } + + else -> { + val food = foodFromJson(jsonFood) + if (food != null) { + foodPlugin.disposable += repository.runTransactionForResult(SyncFoodTransaction(food)).subscribe({ result -> + result.inserted.forEach { aapsLogger.debug(LTag.DATAFOOD, "Inserted food $it") } + result.updated.forEach { aapsLogger.debug(LTag.DATAFOOD, "Updated food $it") } + result.invalidated.forEach { aapsLogger.debug(LTag.DATAFOOD, "Invalidated food $it") } + }, { + aapsLogger.error(LTag.DATAFOOD, "Error while adding/updating food", it) + ret = Result.failure() + }) + } else { + aapsLogger.error(LTag.DATAFOOD, "Error parsing food", jsonFood.toString()) + ret = Result.failure() + } + } + } + } + return ret + } } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/food/FoodService.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/food/FoodService.java deleted file mode 100644 index ceb72727a1..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/food/FoodService.java +++ /dev/null @@ -1,359 +0,0 @@ -package info.nightscout.androidaps.plugins.general.food; - -import android.content.Intent; -import android.os.IBinder; - -import androidx.annotation.Nullable; - -import com.j256.ormlite.android.apptools.OpenHelperManager; -import com.j256.ormlite.android.apptools.OrmLiteBaseService; -import com.j256.ormlite.dao.Dao; -import com.j256.ormlite.dao.DaoManager; -import com.j256.ormlite.support.ConnectionSource; -import com.j256.ormlite.table.TableUtils; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -import dagger.android.HasAndroidInjector; -import info.nightscout.androidaps.db.DatabaseHelper; -import info.nightscout.androidaps.db.ICallback; -import info.nightscout.androidaps.events.Event; -import info.nightscout.androidaps.events.EventFoodDatabaseChanged; -import info.nightscout.androidaps.events.EventNsFood; -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.bus.RxBusWrapper; -import info.nightscout.androidaps.utils.FabricPrivacy; -import info.nightscout.androidaps.utils.rx.AapsSchedulers; -import io.reactivex.disposables.CompositeDisposable; - -/** - * Created by mike on 24.09.2017. - */ - -public class FoodService extends OrmLiteBaseService { - @Inject AAPSLogger aapsLogger; - @Inject RxBusWrapper rxBus; - @Inject FabricPrivacy fabricPrivacy; - @Inject AapsSchedulers aapsSchedulers; - - private final CompositeDisposable disposable = new CompositeDisposable(); - - private static final ScheduledExecutorService foodEventWorker = Executors.newSingleThreadScheduledExecutor(); - private static ScheduledFuture scheduledFoodEventPost = null; - - public FoodService(HasAndroidInjector injector) { - injector.androidInjector().inject(this); - onCreate(); - dbInitialize(); - disposable.add(rxBus - .toObservable(EventNsFood.class) - .observeOn(aapsSchedulers.getIo()) - .subscribe(event -> { - int mode = event.getMode(); - JSONArray array = event.getFoods(); - if (mode == EventNsFood.Companion.getADD() || mode == EventNsFood.Companion.getUPDATE()) - this.createFoodFromJsonIfNotExists(array); - else - this.deleteNS(array); - }, fabricPrivacy::logException) - ); - } - - /** - * This method is a simple re-implementation of the database create and up/downgrade functionality - * in SQLiteOpenHelper#getDatabaseLocked method. - *

- * It is implemented to be able to late initialize separate plugins of the application. - */ - protected void dbInitialize() { - DatabaseHelper helper = OpenHelperManager.getHelper(this, DatabaseHelper.class); - int newVersion = helper.getNewVersion(); - int oldVersion = helper.getOldVersion(); - - if (oldVersion > newVersion) { - onDowngrade(this.getConnectionSource(), oldVersion, newVersion); - } else { - onUpgrade(this.getConnectionSource(), oldVersion, newVersion); - } - } - - public Dao getDao() { - try { - return DaoManager.createDao(this.getConnectionSource(), Food.class); - } catch (SQLException e) { - aapsLogger.error("Cannot create Dao for Food.class"); - } - - return null; - } - - @Override - public void onCreate() { - super.onCreate(); - try { - aapsLogger.info(LTag.DATAFOOD, "onCreate"); - TableUtils.createTableIfNotExists(this.getConnectionSource(), Food.class); - } catch (SQLException e) { - aapsLogger.error("Can't create database", e); - throw new RuntimeException(e); - } - } - - public void onUpgrade(ConnectionSource connectionSource, int oldVersion, int newVersion) { - aapsLogger.info(LTag.DATAFOOD, "onUpgrade"); -// this.resetFood(); - } - - public void onDowngrade(ConnectionSource connectionSource, int oldVersion, int newVersion) { - // this method is not supported right now - } - - public void resetFood() { - try { - TableUtils.dropTable(this.getConnectionSource(), Food.class, true); - TableUtils.createTableIfNotExists(this.getConnectionSource(), Food.class); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - scheduleFoodChange(); - } - - - /** - * A place to centrally register events to be posted, if any data changed. - * This should be implemented in an abstract service-class. - *

- * We do need to make sure, that ICallback is extended to be able to handle multiple - * events, or handle a list of events. - *

- * on some methods the earliestDataChange event is handled separatly, in that it is checked if it is - * set to null by another event already (eg. scheduleExtendedBolusChange). - * - * @param event - * @param eventWorker - * @param callback - */ - private void scheduleEvent(final Event event, ScheduledExecutorService eventWorker, - final ICallback callback) { - - class PostRunnable implements Runnable { - public void run() { - aapsLogger.debug(LTag.DATAFOOD, "Firing EventFoodChange"); - rxBus.send(event); - callback.setPost(null); - } - } - // prepare task for execution in 1 sec - // cancel waiting task to prevent sending multiple posts - if (callback.getPost() != null) - callback.getPost().cancel(false); - Runnable task = new PostRunnable(); - final int sec = 1; - callback.setPost(eventWorker.schedule(task, sec, TimeUnit.SECONDS)); - } - - /** - * Schedule a foodChange Event. - */ - public void scheduleFoodChange() { - this.scheduleEvent(new EventFoodDatabaseChanged(), foodEventWorker, new ICallback() { - @Override - public void setPost(ScheduledFuture post) { - scheduledFoodEventPost = post; - } - - @Override - public ScheduledFuture getPost() { - return scheduledFoodEventPost; - } - }); - } - - public List getFoodData() { - try { - return this.getDao().queryForAll(); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - - return new ArrayList<>(); - } - - /* - { - "_id": "551ee3ad368e06e80856e6a9", - "type": "food", - "category": "Zakladni", - "subcategory": "Napoje", - "name": "Mleko", - "portion": 250, - "carbs": 12, - "gi": 1, - "created_at": "2015-04-14T06:59:16.500Z", - "unit": "ml" - } - */ - public void createFoodFromJsonIfNotExists(JSONObject json) { - try { - Food food = Food.createFromJson(json); - this.createFoodFromJsonIfNotExists(food); - } catch (JSONException e) { - aapsLogger.error("Unhandled exception", e); - } - } - - public void createFoodFromJsonIfNotExists(JSONArray array) { - try { - for (int n = 0; n < array.length(); n++) { - JSONObject json = array.getJSONObject(n); - Food food = Food.createFromJson(json); - this.createFoodFromJsonIfNotExists(food); - } - } catch (JSONException e) { - aapsLogger.error("Unhandled exception", e); - } - } - - public void createFoodFromJsonIfNotExists(Food food) { - this.createOrUpdateByNS(food); - } - - public void deleteNS(JSONObject json) { - try { - String _id = json.getString("_id"); - this.deleteByNSId(_id); - } catch (JSONException | SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - } - - public void deleteNS(JSONArray array) { - try { - for (int n = 0; n < array.length(); n++) { - JSONObject json = array.getJSONObject(n); - this.deleteNS(json); - } - } catch (JSONException e) { - aapsLogger.error("Unhandled exception", e); - } - } - - /** - * deletes an entry by its NS Id. - *

- * Basically a convenience method for findByNSId and delete. - * - * @param _id - */ - public void deleteByNSId(String _id) throws SQLException { - Food stored = this.findByNSId(_id); - if (stored != null) { - aapsLogger.debug(LTag.DATAFOOD, "Removing Food record from database: " + stored.toString()); - this.delete(stored); - } - } - - /** - * deletes the food and sends the foodChange Event - *

- * should be moved ot a Service - * - * @param food - */ - public void delete(Food food) { - try { - this.getDao().delete(food); - this.scheduleFoodChange(); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - } - - /** - * Create of update a food record by the NS (Nightscout) Id. - * - * @param food - * @return - */ - public boolean createOrUpdateByNS(Food food) { - // find by NS _id - if (food._id != null && !food._id.equals("")) { - Food old = this.findByNSId(food._id); - - if (old != null) { - if (!old.isEqual(food)) { - this.delete(old); // need to delete/create because date may change too - old.copyFrom(food); - this.create(old); - return true; - } else { - return false; - } - } else { - this.createOrUpdate(food); - return true; - } - } - - return false; - } - - public void createOrUpdate(Food food) { - try { - this.getDao().createOrUpdate(food); - aapsLogger.debug(LTag.DATAFOOD, "Created or Updated: " + food.toString()); - } catch (SQLException e) { - aapsLogger.error("Unable to createOrUpdate Food", e); - } - this.scheduleFoodChange(); - } - - public void create(Food food) { - try { - this.getDao().create(food); - aapsLogger.debug(LTag.DATAFOOD, "New record: " + food.toString()); - } catch (SQLException e) { - aapsLogger.error("Unable to create Food", e); - } - this.scheduleFoodChange(); - } - - /** - * finds food by its NS Id. - * - * @param _id - * @return - */ - @Nullable - public Food findByNSId(String _id) { - try { - List list = this.getDao().queryForEq("_id", _id); - - if (list.size() == 1) { // really? if there are more then one result, then we do not return anything... - return list.get(0); - } - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return null; - } - - @Nullable - @Override - public IBinder onBind(Intent intent) { - return null; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefs.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefs.kt index 8e9456ffda..9acdf53e8a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefs.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefs.kt @@ -108,6 +108,7 @@ class ImportExportPrefs @Inject constructor( return metadata } + @Suppress("SpellCheckingInspection") private fun detectUserName(context: Context): String { // based on https://medium.com/@pribble88/how-to-get-an-android-device-nickname-4b4700b3068c val n1 = Settings.System.getString(context.contentResolver, "bluetooth_name") @@ -346,7 +347,7 @@ class ImportExportPrefs @Inject constructor( private fun restartAppAfterImport(context: Context) { sp.putBoolean(R.string.key_setupwizard_processed, true) - OKDialog.show(context, resourceHelper.gs(R.string.setting_imported), resourceHelper.gs(R.string.restartingapp), Runnable { + OKDialog.show(context, resourceHelper.gs(R.string.setting_imported), resourceHelper.gs(R.string.restartingapp)) { uel.log(Action.IMPORT_SETTINGS) log.debug(LTag.CORE, "Exiting") rxBus.send(EventAppExit()) @@ -355,14 +356,13 @@ class ImportExportPrefs @Inject constructor( } System.runFinalization() exitProcess(0) - }) + } } - override fun exportUserEntriesCsv(activity: FragmentActivity, listEntries: Single>) { - val entries = listEntries.blockingGet() + override fun exportUserEntriesCsv(activity: FragmentActivity, singleEntries: Single>) { + val entries = singleEntries.blockingGet() prefFileList.ensureExportDirExists() val newFile = prefFileList.newExportXmlFile() - //log.debug("XXXXX " + classicPrefsFormat.UserEntriesToCsv(entries)) try { classicPrefsFormat.saveCsv(newFile, entries) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenanceFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenanceFragment.kt index 882cfc389f..32b3001911 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenanceFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenanceFragment.kt @@ -16,7 +16,6 @@ import info.nightscout.androidaps.interfaces.ImportExportPrefsInterface 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.food.FoodPlugin import info.nightscout.androidaps.plugins.general.maintenance.activities.LogSettingActivity import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin import info.nightscout.androidaps.utils.alertDialogs.OKDialog @@ -34,7 +33,6 @@ class MaintenanceFragment : DaggerFragment() { @Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var treatmentsPlugin: TreatmentsPlugin - @Inject lateinit var foodPlugin: FoodPlugin @Inject lateinit var importExportPrefs: ImportExportPrefsInterface @Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var repository: AppRepository @@ -70,7 +68,6 @@ class MaintenanceFragment : DaggerFragment() { databaseHelper.resetDatabases() // should be handled by Plugin-Interface and // additional service interface and plugin registry - foodPlugin.service?.resetFood() treatmentsPlugin.service.resetTreatments() repository.clearDatabases() } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/services/NSClientService.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/services/NSClientService.java index 24006267f0..b4be8d61c7 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/services/NSClientService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/services/NSClientService.java @@ -34,7 +34,6 @@ import info.nightscout.androidaps.R; import info.nightscout.androidaps.db.DbRequest; import info.nightscout.androidaps.events.EventAppExit; import info.nightscout.androidaps.events.EventConfigBuilderChange; -import info.nightscout.androidaps.events.EventNsFood; import info.nightscout.androidaps.events.EventPreferenceChange; import info.nightscout.androidaps.interfaces.DatabaseHelperInterface; import info.nightscout.androidaps.interfaces.PluginType; @@ -636,39 +635,8 @@ public class NSClientService extends DaggerService { } if (data.has("food")) { JSONArray foods = data.getJSONArray("food"); - JSONArray removedFoods = new JSONArray(); - JSONArray updatedFoods = new JSONArray(); - JSONArray addedFoods = new JSONArray(); - if (foods.length() > 0) - rxBus.send(new EventNSClientNewLog("DATA", "received " + foods.length() + " foods")); - for (Integer index = 0; index < foods.length(); index++) { - JSONObject jsonFood = foods.getJSONObject(index); - - // remove from upload queue if Ack is failing - uploadQueue.removeID(jsonFood); - - String action = JsonHelper.safeGetString(jsonFood, "action"); - - if (action == null) { - addedFoods.put(jsonFood); - } else if (action.equals("update")) { - updatedFoods.put(jsonFood); - } else if (action.equals("remove")) { - removedFoods.put(jsonFood); - } - } - if (removedFoods.length() > 0) { - EventNsFood evt = new EventNsFood(EventNsFood.Companion.getREMOVE(), removedFoods); - rxBus.send(evt); - } - if (updatedFoods.length() > 0) { - EventNsFood evt = new EventNsFood(EventNsFood.Companion.getUPDATE(), updatedFoods); - rxBus.send(evt); - } - if (addedFoods.length() > 0) { - EventNsFood evt = new EventNsFood(EventNsFood.Companion.getADD(), addedFoods); - rxBus.send(evt); - } + if (foods.length() > 0) rxBus.send(new EventNSClientNewLog("DATA", "received " + foods.length() + " foods")); + handleFood(foods, isDelta); } if (data.has("mbgs")) { JSONArray mbgs = data.getJSONArray("mbgs"); @@ -881,6 +849,16 @@ public class NSClientService extends DaggerService { } } + public void handleFood(JSONArray foods, boolean isDelta) { + Bundle bundle = new Bundle(); + bundle.putString("foods", foods.toString()); + bundle.putBoolean("delta", isDelta); + Intent intent = new Intent(Intents.ACTION_FOOD); + intent.putExtras(bundle); + intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); + LocalBroadcastManager.getInstance(this).sendBroadcast(intent); + } + public void handleNewCal(JSONArray cals, boolean isDelta) { Bundle bundle = new Bundle(); bundle.putString("cals", cals.toString()); diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/DataReceiver.kt b/app/src/main/java/info/nightscout/androidaps/receivers/DataReceiver.kt index 3d4d04dbf7..862d470dfd 100644 --- a/app/src/main/java/info/nightscout/androidaps/receivers/DataReceiver.kt +++ b/app/src/main/java/info/nightscout/androidaps/receivers/DataReceiver.kt @@ -12,6 +12,7 @@ import dagger.android.DaggerBroadcastReceiver import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.BundleLogger import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.general.food.FoodPlugin import info.nightscout.androidaps.plugins.general.nsclient.NSClientWorker import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin @@ -21,7 +22,6 @@ import info.nightscout.androidaps.utils.extensions.copyDouble import info.nightscout.androidaps.utils.extensions.copyInt import info.nightscout.androidaps.utils.extensions.copyLong import info.nightscout.androidaps.utils.extensions.copyString -import org.json.JSONObject import javax.inject.Inject open class DataReceiver : DaggerBroadcastReceiver() { @@ -38,37 +38,37 @@ open class DataReceiver : DaggerBroadcastReceiver() { when (intent.action) { - Intents.ACTION_NEW_BG_ESTIMATE -> + Intents.ACTION_NEW_BG_ESTIMATE -> OneTimeWorkRequest.Builder(XdripPlugin.XdripWorker::class.java) .setInputData(bundleInputData(bundle, intent)).build() - Intents.POCTECH_BG -> + Intents.POCTECH_BG -> OneTimeWorkRequest.Builder(PoctechPlugin.PoctechWorker::class.java) .setInputData(Data.Builder().also { it.copyString("data", bundle) }.build()).build() - Intents.GLIMP_BG -> + Intents.GLIMP_BG -> OneTimeWorkRequest.Builder(GlimpPlugin.GlimpWorker::class.java) .setInputData(Data.Builder().also { it.copyDouble("mySGV", bundle) it.copyString("myTrend", bundle) it.copyLong("myTimestamp", bundle) }.build()).build() - Intents.TOMATO_BG -> + Intents.TOMATO_BG -> OneTimeWorkRequest.Builder(TomatoPlugin.TomatoWorker::class.java) .setInputData(Data.Builder().also { it.copyDouble("com.fanqies.tomatofn.Extras.BgEstimate", bundle) it.copyLong("com.fanqies.tomatofn.Extras.Time", bundle) }.build()).build() - Intents.ACTION_NEW_PROFILE -> + Intents.ACTION_NEW_PROFILE -> OneTimeWorkRequest.Builder(NSProfilePlugin.NSProfileWorker::class.java) .setInputData(bundleInputData(bundle, intent)).build() - Intents.ACTION_NEW_SGV -> + Intents.ACTION_NEW_SGV -> OneTimeWorkRequest.Builder(NSClientSourcePlugin.NSClientSourceWorker::class.java) .setInputData(Data.Builder().also { it.copyString("sgv", bundle, null) it.copyString("sgvs", bundle, null) }.build()).build() - Intents.NS_EMULATOR -> + Intents.NS_EMULATOR -> OneTimeWorkRequest.Builder(MM640gPlugin.MM640gWorker::class.java) .setInputData(Data.Builder().also { it.copyDouble(Intents.EXTRA_BG_ESTIMATE, bundle) @@ -81,23 +81,26 @@ open class DataReceiver : DaggerBroadcastReceiver() { Telephony.Sms.Intents.SMS_RECEIVED_ACTION -> OneTimeWorkRequest.Builder(SmsCommunicatorPlugin.SmsCommunicatorWorker::class.java) .setInputData(bundleInputData(bundle, intent)).build() - Intents.EVERSENSE_BG -> + Intents.EVERSENSE_BG -> OneTimeWorkRequest.Builder(EversensePlugin.EversenseWorker::class.java) .setInputData(bundleInputData(bundle, intent)).build() - Intents.DEXCOM_BG -> + Intents.DEXCOM_BG -> OneTimeWorkRequest.Builder(DexcomPlugin.DexcomWorker::class.java) .setInputData(bundleInputData(bundle, intent)).build() + Intents.ACTION_FOOD -> + OneTimeWorkRequest.Builder(FoodPlugin.FoodWorker::class.java) + .setInputData(bundleInputData(bundle, intent)).build() Intents.ACTION_NEW_TREATMENT, Intents.ACTION_CHANGED_TREATMENT, Intents.ACTION_REMOVED_TREATMENT, Intents.ACTION_NEW_CAL, - Intents.ACTION_NEW_MBG -> + Intents.ACTION_NEW_MBG -> OneTimeWorkRequest.Builder(NSClientWorker::class.java) .setInputData(bundleInputData(bundle, intent)).build() else -> null }?.let { request -> WorkManager.getInstance(context) - .enqueueUniqueWork(jobGroupName, ExistingWorkPolicy.APPEND_OR_REPLACE , request) + .enqueueUniqueWork(jobGroupName, ExistingWorkPolicy.APPEND_OR_REPLACE, request) } } @@ -105,6 +108,7 @@ open class DataReceiver : DaggerBroadcastReceiver() { Data.Builder().putLong(STORE_KEY, bundleStore.store(bundle)).putString(ACTION_KEY, intent.action).build() companion object { + const val STORE_KEY = "storeKey" const val ACTION_KEY = "action" } diff --git a/app/src/main/java/info/nightscout/androidaps/services/Intents.kt b/app/src/main/java/info/nightscout/androidaps/services/Intents.kt index 5e38fdb15b..7255548a03 100644 --- a/app/src/main/java/info/nightscout/androidaps/services/Intents.kt +++ b/app/src/main/java/info/nightscout/androidaps/services/Intents.kt @@ -13,6 +13,7 @@ interface Intents { const val ACTION_NEW_SGV = "info.nightscout.client.NEW_SGV" const val ACTION_NEW_MBG = "info.nightscout.client.NEW_MBG" const val ACTION_NEW_CAL = "info.nightscout.client.NEW_CAL" + const val ACTION_FOOD = "info.nightscout.client.FOOD" // xDrip -> App const val ACTION_NEW_BG_ESTIMATE = "com.eveningoutpost.dexdrip.BgEstimate" diff --git a/app/src/main/res/layout/food_fragment.xml b/app/src/main/res/layout/food_fragment.xml index f192bdf62e..c96ed1f3cd 100644 --- a/app/src/main/res/layout/food_fragment.xml +++ b/app/src/main/res/layout/food_fragment.xml @@ -4,7 +4,17 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - tools:context="info.nightscout.androidaps.plugins.treatments.fragments.TreatmentsExtendedBolusesFragment"> + tools:context="info.nightscout.androidaps.plugins.general.food.TreatmentsFoodFragment"> + + + card_view:cardBackgroundColor="?android:colorBackground"> + android:textStyle="bold" + tools:ignore="HardcodedText" /> + android:text="Portion" + tools:ignore="HardcodedText" /> + android:text="Carbs" + tools:ignore="HardcodedText" /> @@ -56,21 +57,24 @@ android:layout_width="70dp" android:layout_height="wrap_content" android:gravity="end" - android:text="Fat" /> + android:text="Fat" + tools:ignore="HardcodedText" /> + android:text="Protein" + tools:ignore="HardcodedText" /> + android:text="Energy" + tools:ignore="HardcodedText" /> + android:textColor="@color/colorSetTempButton" + tools:ignore="HardcodedText" /> + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dd550020b4..52a1be72c3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -317,7 +317,7 @@ Autosens data Script debug Use Autosens feature - Refresh events from NS + Refresh from NS Delete treatments in the future ACT CONF diff --git a/core/src/main/java/info/nightscout/androidaps/interfaces/ImportExportPrefsInterface.kt b/core/src/main/java/info/nightscout/androidaps/interfaces/ImportExportPrefsInterface.kt index b20afdd1df..e003e02fcf 100644 --- a/core/src/main/java/info/nightscout/androidaps/interfaces/ImportExportPrefsInterface.kt +++ b/core/src/main/java/info/nightscout/androidaps/interfaces/ImportExportPrefsInterface.kt @@ -14,5 +14,5 @@ interface ImportExportPrefsInterface { fun prefsFileExists(): Boolean fun verifyStoragePermissions(fragment: Fragment, onGranted: Runnable) fun exportSharedPreferences(f: Fragment) - fun exportUserEntriesCsv(activity: FragmentActivity, entries: Single>) + fun exportUserEntriesCsv(activity: FragmentActivity, singleEntries: Single>) } \ No newline at end of file diff --git a/core/src/main/java/info/nightscout/androidaps/utils/JsonHelper.kt b/core/src/main/java/info/nightscout/androidaps/utils/JsonHelper.kt index 65748dca7e..f3105b755e 100644 --- a/core/src/main/java/info/nightscout/androidaps/utils/JsonHelper.kt +++ b/core/src/main/java/info/nightscout/androidaps/utils/JsonHelper.kt @@ -5,7 +5,6 @@ import org.json.JSONObject object JsonHelper { - @JvmStatic fun safeGetObject(json: JSONObject?, fieldName: String, defaultValue: Any): Any { var result = defaultValue if (json != null && json.has(fieldName)) { @@ -17,7 +16,6 @@ object JsonHelper { return result } - @JvmStatic fun safeGetJSONObject(json: JSONObject?, fieldName: String, defaultValue: JSONObject?): JSONObject? { var result = defaultValue if (json != null && json.has(fieldName)) { @@ -53,7 +51,6 @@ object JsonHelper { return result } - @JvmStatic fun safeGetStringAllowNull(json: JSONObject?, fieldName: String, defaultValue: String?): String? { var result = defaultValue if (json != null && json.has(fieldName)) { @@ -77,7 +74,6 @@ object JsonHelper { return result } - @JvmStatic fun safeGetDoubleAllowNull(json: JSONObject?, fieldName: String): Double? { var result: Double? = null if (json != null && json.has(fieldName)) { @@ -89,7 +85,6 @@ object JsonHelper { return result } - @JvmStatic fun safeGetDouble(json: JSONObject?, fieldName: String, defaultValue: Double): Double { var result = defaultValue if (json != null && json.has(fieldName)) { @@ -105,7 +100,6 @@ object JsonHelper { fun safeGetInt(json: JSONObject?, fieldName: String): Int = safeGetInt(json, fieldName, 0) - @JvmStatic fun safeGetInt(json: JSONObject?, fieldName: String, defaultValue: Int): Int { var result = defaultValue if (json != null && json.has(fieldName)) { @@ -117,6 +111,17 @@ object JsonHelper { return result } + fun safeGetIntAllowNull(json: JSONObject?, fieldName: String): Int? { + var result : Int? = null + if (json != null && json.has(fieldName)) { + try { + result = json.getInt(fieldName) + } catch (ignored: JSONException) { + } + } + return result + } + @JvmStatic fun safeGetLong(json: JSONObject?, fieldName: String): Long { var result: Long = 0 diff --git a/core/src/main/java/info/nightscout/androidaps/utils/extensions/FoodExtension.kt b/core/src/main/java/info/nightscout/androidaps/utils/extensions/FoodExtension.kt new file mode 100644 index 0000000000..e2624775a7 --- /dev/null +++ b/core/src/main/java/info/nightscout/androidaps/utils/extensions/FoodExtension.kt @@ -0,0 +1,40 @@ +package info.nightscout.androidaps.utils.extensions + +import info.nightscout.androidaps.database.entities.Food +import info.nightscout.androidaps.plugins.general.nsclient.NSUpload +import info.nightscout.androidaps.utils.JsonHelper +import org.json.JSONObject + +fun foodFromJson(jsonObject: JSONObject): Food? { + if ("food" == JsonHelper.safeGetString(jsonObject, "type")) { + val name = JsonHelper.safeGetStringAllowNull(jsonObject, "name", null) ?: return null + val category = JsonHelper.safeGetStringAllowNull(jsonObject, "category", null) + val subCategory = JsonHelper.safeGetStringAllowNull(jsonObject, "subcategory", null) + val unit = JsonHelper.safeGetString(jsonObject, "unit", "") + val portion = JsonHelper.safeGetDoubleAllowNull(jsonObject, "portion") ?: return null + val carbs = JsonHelper.safeGetIntAllowNull(jsonObject, "carbs") ?: return null + val gi = JsonHelper.safeGetIntAllowNull(jsonObject, "gi") + val energy = JsonHelper.safeGetIntAllowNull(jsonObject, "energy") + val protein = JsonHelper.safeGetIntAllowNull(jsonObject, "protein") + val fat = JsonHelper.safeGetIntAllowNull(jsonObject, "fat") + val id = JsonHelper.safeGetStringAllowNull(jsonObject, "_id", null) ?: return null + val isValid = JsonHelper.safeGetBoolean(jsonObject, NSUpload.ISVALID, true) + + val food = Food( + name = name, + category = category, + subCategory = subCategory, + unit = unit, + portion = portion, + carbs = carbs, + gi = gi, + energy = energy, + protein = protein, + fat = fat, + isValid = isValid + ) + food.interfaceIDs.nightscoutId = id + return food + } + return null +} diff --git a/database/schemas/info.nightscout.androidaps.database.AppDatabase/7.json b/database/schemas/info.nightscout.androidaps.database.AppDatabase/7.json new file mode 100644 index 0000000000..cb6fc6e0bb --- /dev/null +++ b/database/schemas/info.nightscout.androidaps.database.AppDatabase/7.json @@ -0,0 +1,2924 @@ +{ + "formatVersion": 1, + "database": { + "version": 7, + "identityHash": "7a8795e82743748b627944dc589e8626", + "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, `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.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, `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.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_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, `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 )", + "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": "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.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`)" + } + ], + "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, `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.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_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, `duration` INTEGER NOT NULL, `basalBlocks` TEXT NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, 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": "duration", + "columnName": "duration", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "basalBlocks", + "columnName": "basalBlocks", + "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.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_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`)" + } + ], + "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, `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.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_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, `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.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_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, `profileName` TEXT NOT NULL, `glucoseUnit` TEXT NOT NULL, `basalBlocks` TEXT NOT NULL, `isfBlocks` TEXT NOT NULL, `icBlocks` TEXT NOT NULL, `targetBlocks` TEXT NOT NULL, `timeshift` INTEGER NOT NULL, `percentage` INTEGER NOT NULL, `duration` INTEGER NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `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": "profileName", + "columnName": "profileName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "glucoseUnit", + "columnName": "glucoseUnit", + "affinity": "TEXT", + "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": "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.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`)" + } + ], + "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, `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.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_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, `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.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_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, `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.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_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, `bolusAmount` REAL, `totalAmount` REAL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `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": false + }, + { + "fieldPath": "bolusAmount", + "columnName": "bolusAmount", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "totalAmount", + "columnName": "totalAmount", + "affinity": "REAL", + "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.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_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, `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.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": "mealLinks", + "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, `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 )", + "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": false + }, + { + "fieldPath": "carbsId", + "columnName": "carbsId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "bolusCalcResultId", + "columnName": "bolusCalcResultId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "superbolusTempBasalId", + "columnName": "superbolusTempBasalId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "noteId", + "columnName": "noteId", + "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.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_mealLinks_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_mealLinks_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + }, + { + "name": "index_mealLinks_bolusId", + "unique": false, + "columnNames": [ + "bolusId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_mealLinks_bolusId` ON `${TABLE_NAME}` (`bolusId`)" + }, + { + "name": "index_mealLinks_carbsId", + "unique": false, + "columnNames": [ + "carbsId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_mealLinks_carbsId` ON `${TABLE_NAME}` (`carbsId`)" + }, + { + "name": "index_mealLinks_bolusCalcResultId", + "unique": false, + "columnNames": [ + "bolusCalcResultId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_mealLinks_bolusCalcResultId` ON `${TABLE_NAME}` (`bolusCalcResultId`)" + }, + { + "name": "index_mealLinks_superbolusTempBasalId", + "unique": false, + "columnNames": [ + "superbolusTempBasalId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_mealLinks_superbolusTempBasalId` ON `${TABLE_NAME}` (`superbolusTempBasalId`)" + }, + { + "name": "index_mealLinks_noteId", + "unique": false, + "columnNames": [ + "noteId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_mealLinks_noteId` ON `${TABLE_NAME}` (`noteId`)" + } + ], + "foreignKeys": [ + { + "table": "boluses", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "bolusId" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "carbs", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "carbsId" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "bolusCalculatorResults", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "bolusCalcResultId" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "temporaryBasals", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "superbolusTempBasalId" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "therapyEvents", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "noteId" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "mealLinks", + "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, `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.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, `s` 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": "s", + "columnName": "s", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "values", + "columnName": "values", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "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, `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.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_referenceId", + "unique": false, + "columnNames": [ + "referenceId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_foods_referenceId` ON `${TABLE_NAME}` (`referenceId`)" + } + ], + "foreignKeys": [ + { + "table": "foods", + "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, '7a8795e82743748b627944dc589e8626')" + ] + } +} \ 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 3c12b1743e..43da5e939a 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/AppDatabase.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/AppDatabase.kt @@ -6,13 +6,14 @@ import androidx.room.TypeConverters import info.nightscout.androidaps.database.daos.* import info.nightscout.androidaps.database.entities.* -const val DATABASE_VERSION = 6 +const val DATABASE_VERSION = 7 @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, - MealLink::class, MultiwaveBolusLink::class, PreferenceChange::class, VersionChange::class, UserEntry::class], + MealLink::class, MultiwaveBolusLink::class, PreferenceChange::class, VersionChange::class, UserEntry::class, + Food::class], exportSchema = true) @TypeConverters(Converters::class) internal abstract class AppDatabase : RoomDatabase() { @@ -53,4 +54,6 @@ internal abstract class AppDatabase : RoomDatabase() { abstract val preferenceChangeDao: PreferenceChangeDao + abstract val foodDao: FoodDao + } \ 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 81240177cc..da47ce1d55 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/AppRepository.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/AppRepository.kt @@ -1,5 +1,6 @@ package info.nightscout.androidaps.database +import info.nightscout.androidaps.database.entities.Food import info.nightscout.androidaps.database.entities.GlucoseValue import info.nightscout.androidaps.database.entities.TemporaryTarget import info.nightscout.androidaps.database.entities.TherapyEvent @@ -170,6 +171,14 @@ class AppRepository @Inject internal constructor( database.therapyEventDao.compatGetTherapyEventDataFromToTime(from, to) .subscribeOn(Schedulers.io()) + // FOOD + fun getFoodData(): Single> = + database.foodDao.getFoodData() + .subscribeOn(Schedulers.io()) + + fun deleteAllFoods() = + database.foodDao.deleteAllEntries() + } @Suppress("USELESS_CAST") 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 e2f0c3f9d4..39c508b07a 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/DatabaseModule.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/DatabaseModule.kt @@ -34,4 +34,10 @@ open class DatabaseModule { 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)") } } + + private val migration6to7 = object : Migration(6, 7) { + override fun migrate(database: SupportSQLiteDatabase) { + 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 )") + } + } } \ 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 b82cc6f379..f9058a1d51 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/DelegatedAppDatabase.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/DelegatedAppDatabase.kt @@ -24,5 +24,6 @@ internal class DelegatedAppDatabase(val changes: MutableList, val datab val versionChangeDao: VersionChangeDao = DelegatedVersionChangeDao(changes, database.versionChangeDao) val userEntryDao: UserEntryDao = DelegatedUserEntryDao(changes, database.userEntryDao) val preferenceChangeDao: PreferenceChangeDao = DelegatedPreferenceChangeDao(changes, database.preferenceChangeDao) + val foodDao: FoodDao = DelegatedFoodDao(changes, database.foodDao) 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 fe7e6c3337..9d3a06cc55 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/TableNames.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/TableNames.kt @@ -8,6 +8,7 @@ const val TABLE_CARBS = "carbs" const val TABLE_EFFECTIVE_PROFILE_SWITCHES = "effectiveProfileSwitches" const val TABLE_EXTENDED_BOLUSES = "extendedBoluses" const val TABLE_GLUCOSE_VALUES = "glucoseValues" +const val TABLE_FOODS = "foods" const val TABLE_MEAL_LINKS = "mealLinks" const val TABLE_MULTIWAVE_BOLUS_LINKS = "multiwaveBolusLinks" const val TABLE_PROFILE_SWITCHES = "profileSwitches" diff --git a/database/src/main/java/info/nightscout/androidaps/database/daos/FoodDao.kt b/database/src/main/java/info/nightscout/androidaps/database/daos/FoodDao.kt new file mode 100644 index 0000000000..830a196f19 --- /dev/null +++ b/database/src/main/java/info/nightscout/androidaps/database/daos/FoodDao.kt @@ -0,0 +1,25 @@ +package info.nightscout.androidaps.database.daos + +import androidx.room.Dao +import androidx.room.Query +import info.nightscout.androidaps.database.TABLE_FOODS +import info.nightscout.androidaps.database.entities.Food +import io.reactivex.Single + +@Suppress("FunctionName") +@Dao +internal interface FoodDao : TraceableDao { + + @Query("SELECT * FROM $TABLE_FOODS WHERE id = :id") + override fun findById(id: Long): Food? + + @Query("DELETE FROM $TABLE_FOODS") + override fun deleteAllEntries() + + @Query("SELECT * FROM $TABLE_FOODS WHERE nightscoutId = :nsId AND referenceId IS NULL") + fun findByNSId(nsId: String): Food? + + @Query("SELECT * FROM $TABLE_FOODS WHERE isValid = 1 AND referenceId IS NULL ORDER BY id DESC") + fun getFoodData(): Single> + +} \ No newline at end of file diff --git a/database/src/main/java/info/nightscout/androidaps/database/daos/TraceableDao.kt b/database/src/main/java/info/nightscout/androidaps/database/daos/TraceableDao.kt index 7eed5e5528..b9f58db158 100644 --- a/database/src/main/java/info/nightscout/androidaps/database/daos/TraceableDao.kt +++ b/database/src/main/java/info/nightscout/androidaps/database/daos/TraceableDao.kt @@ -4,7 +4,6 @@ import androidx.room.Insert import androidx.room.Update import info.nightscout.androidaps.database.daos.workaround.TraceableDaoWorkaround import info.nightscout.androidaps.database.interfaces.TraceableDBEntry -import io.reactivex.Single internal interface TraceableDao : TraceableDaoWorkaround { diff --git a/database/src/main/java/info/nightscout/androidaps/database/daos/delegated/DelegatedFoodDao.kt b/database/src/main/java/info/nightscout/androidaps/database/daos/delegated/DelegatedFoodDao.kt new file mode 100644 index 0000000000..2d37a3e5be --- /dev/null +++ b/database/src/main/java/info/nightscout/androidaps/database/daos/delegated/DelegatedFoodDao.kt @@ -0,0 +1,18 @@ +package info.nightscout.androidaps.database.daos.delegated + +import info.nightscout.androidaps.database.daos.FoodDao +import info.nightscout.androidaps.database.entities.Food +import info.nightscout.androidaps.database.interfaces.DBEntry + +internal class DelegatedFoodDao(changes: MutableList, private val dao: FoodDao) : DelegatedDao(changes), FoodDao by dao { + + override fun insertNewEntry(food: Food): Long { + changes.add(food) + return dao.insertNewEntry(food) + } + + override fun updateExistingEntry(food: Food): Long { + changes.add(food) + return dao.updateExistingEntry(food) + } +} \ No newline at end of file diff --git a/database/src/main/java/info/nightscout/androidaps/database/entities/Food.kt b/database/src/main/java/info/nightscout/androidaps/database/entities/Food.kt new file mode 100644 index 0000000000..9c4cfc9129 --- /dev/null +++ b/database/src/main/java/info/nightscout/androidaps/database/entities/Food.kt @@ -0,0 +1,73 @@ +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_FOODS +import info.nightscout.androidaps.database.embedments.InterfaceIDs +import info.nightscout.androidaps.database.interfaces.TraceableDBEntry + +@Entity(tableName = TABLE_FOODS, + foreignKeys = [ForeignKey( + entity = Food::class, + parentColumns = ["id"], + childColumns = ["referenceId"])], + indices = [Index("referenceId")]) +data class Food( + @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? = null, + var name: String, + var category: String? = null, + var subCategory: String? = null, + // Example: + // name="juice" portion=250 units="ml" carbs=12 + // means 250ml of juice has 12g of carbs + + var portion: Double, // common portion in "units" + var carbs: Int, // in grams + var fat: Int? = null, // in grams + var protein: Int? = null, // in grams + var energy: Int? = null, // in kJ + var unit: String = "g", + var gi: Int? = null // not used yet + +) : TraceableDBEntry { + + fun isEqual(other: Food): Boolean { + if (isValid != other.isValid) return false + if (portion != other.portion) return false + if (carbs != other.carbs) return false + if (fat != other.fat) return false + if (protein != other.protein) return false + if (energy != other.energy) return false + if (gi != other.gi) return false + if (name != other.name) return false + if (category != other.category) return false + if (subCategory != other.subCategory) return false + return unit == other.unit + } + + fun copyFrom(other: Food) { + isValid = other.isValid + name = other.name + category = other.category + subCategory = other.subCategory + portion = other.portion + carbs = other.carbs + fat = other.fat + protein = other.protein + energy = other.energy + unit = other.unit + gi = other.gi + interfaceIDs.nightscoutId = other.interfaceIDs.nightscoutId + } + +} \ 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 52b4086d73..ff990de675 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 @@ -103,6 +103,7 @@ data class UserEntry( @SerializedName("TT_DELETED_FROM_NS") TT_DELETED_FROM_NS (ColorGroup.TT), @SerializedName("CAREPORTAL_DELETED_FROM_NS") CAREPORTAL_DELETED_FROM_NS (ColorGroup.Careportal), @SerializedName("CAREPORTAL_FROM_NS") CAREPORTAL_FROM_NS (ColorGroup.Careportal), + @SerializedName("FOOD_FROM_NS") FOOD_FROM_NS (ColorGroup.Careportal), @SerializedName("TT_FROM_NS") TT_FROM_NS (ColorGroup.TT), @SerializedName("TT_CANCELED_FROM_NS") TT_CANCELED_FROM_NS (ColorGroup.TT), @SerializedName("EXPORT_CSV") EXPORT_CSV (ColorGroup.Aaps), diff --git a/database/src/main/java/info/nightscout/androidaps/database/transactions/InsertOrUpdateFoodTransaction.kt b/database/src/main/java/info/nightscout/androidaps/database/transactions/InsertOrUpdateFoodTransaction.kt new file mode 100644 index 0000000000..7de6457542 --- /dev/null +++ b/database/src/main/java/info/nightscout/androidaps/database/transactions/InsertOrUpdateFoodTransaction.kt @@ -0,0 +1,28 @@ +package info.nightscout.androidaps.database.transactions + +import info.nightscout.androidaps.database.entities.Food + +/** + * Inserts or updates the Food + */ +class InsertOrUpdateFoodTransaction(private val food: Food) : Transaction() { + + override fun run(): TransactionResult { + val result = TransactionResult() + val current = database.foodDao.findById(food.id) + if (current == null) { + database.foodDao.insertNewEntry(food) + result.inserted.add(food) + } else { + database.foodDao.updateExistingEntry(food) + result.updated.add(food) + } + 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/InvalidateFoodTransaction.kt b/database/src/main/java/info/nightscout/androidaps/database/transactions/InvalidateFoodTransaction.kt new file mode 100644 index 0000000000..a1f7b545e9 --- /dev/null +++ b/database/src/main/java/info/nightscout/androidaps/database/transactions/InvalidateFoodTransaction.kt @@ -0,0 +1,11 @@ +package info.nightscout.androidaps.database.transactions + +class InvalidateFoodTransaction(val id: Long) : Transaction() { + + override fun run() { + val food = database.foodDao.findById(id) + ?: throw IllegalArgumentException("There is no such Food with the specified ID.") + food.isValid = false + database.foodDao.updateExistingEntry(food) + } +} \ No newline at end of file diff --git a/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncFoodTransaction.kt b/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncFoodTransaction.kt new file mode 100644 index 0000000000..bd2ab7b3cf --- /dev/null +++ b/database/src/main/java/info/nightscout/androidaps/database/transactions/SyncFoodTransaction.kt @@ -0,0 +1,42 @@ +package info.nightscout.androidaps.database.transactions + +import info.nightscout.androidaps.database.entities.Food + +/** + * Sync the TherapyEvents from NS + */ +class SyncFoodTransaction(private val food: Food) : Transaction() { + + override fun run(): TransactionResult { + val result = TransactionResult() + + val current: Food? = + food.interfaceIDs.nightscoutId?.let { + database.foodDao.findByNSId(it) + } + + if (current != null) { + // nsId exists, update if different + if (!current.isEqual(food)) { + current.copyFrom(food) + database.foodDao.updateExistingEntry(current) + if (food.isValid && current.isValid) result.updated.add(current) + else if (!food.isValid && current.isValid) result.invalidated.add(current) + } + return result + } + + // not known nsId, add + database.foodDao.insertNewEntry(food) + result.inserted.add(food) + return result + + } + + class TransactionResult { + + val updated = mutableListOf() + val inserted = mutableListOf() + val invalidated = mutableListOf() + } +} \ No newline at end of file