database module

This commit is contained in:
Milos Kozak 2021-02-04 18:22:58 +01:00
parent 230cc943af
commit b04862f602
91 changed files with 2395 additions and 3 deletions

View file

@ -235,6 +235,7 @@ dependencies {
wearApp project(':wear')
implementation project(':core')
implementation project(':database')
implementation project(':dana')
implementation project(':danars')
implementation project(':danar')

View file

@ -2,12 +2,12 @@
buildscript {
ext {
kotlin_version = '1.4.21'
kotlin_version = '1.4.30'
coreVersion = '1.3.2'
rxjava_version = '2.2.19'
rxjava_version = '2.2.20'
rxandroid_version = '2.1.1'
rxkotlin_version = '2.4.0'
room_version = '2.2.5'
room_version = '2.2.6'
lifecycle_version = '2.2.0'
dagger_version = '2.31.2'
coroutinesVersion = '1.3.7'

1
database/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

60
database/build.gradle Normal file
View file

@ -0,0 +1,60 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 23
targetSdkVersion 28
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug {
}
firebaseDisable {
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation "androidx.core:core-ktx:$coreVersion"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "io.reactivex.rxjava2:rxjava:$rxjava_version"
implementation "io.reactivex.rxjava2:rxandroid:$rxandroid_version"
implementation("io.reactivex.rxjava2:rxkotlin:$rxkotlin_version")
implementation "com.google.code.gson:gson:2.8.6"
api "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-ktx:$room_version"
implementation "androidx.room:room-rxjava2:$room_version"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
implementation "com.google.dagger:dagger-android:$dagger_version"
implementation "com.google.dagger:dagger-android-support:$dagger_version"
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
annotationProcessor "com.google.dagger:dagger-android-processor:$dagger_version"
kapt "com.google.dagger:dagger-android-processor:$dagger_version"
kapt "com.google.dagger:dagger-compiler:$dagger_version"
}

View file

21
database/proguard-rules.pro vendored Normal file
View file

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View file

@ -0,0 +1,25 @@
package info.nightscout.database
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("info.nightscout.database.test", appContext.packageName)
}
}

View file

@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="info.nightscout.androidaps.database" />

View file

@ -0,0 +1,55 @@
package info.nightscout.androidaps.database
import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import info.nightscout.androidaps.database.daos.*
import info.nightscout.androidaps.database.entities.*
import info.nightscout.androidaps.database.entities.APSResultLink
import info.nightscout.androidaps.database.entities.MealLink
import info.nightscout.androidaps.database.entities.MultiwaveBolusLink
const val DATABASE_VERSION = 1
@Database(version = DATABASE_VERSION, entities = arrayOf(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))
@TypeConverters(Converters::class)
internal abstract class AppDatabase : RoomDatabase() {
abstract val glucoseValueDao: GlucoseValueDao
abstract val therapyEventDao: TherapyEventDao
abstract val temporaryBasalDao: TemporaryBasalDao
abstract val bolusDao: BolusDao
abstract val extendedBolusDao: ExtendedBolusDao
abstract val multiwaveBolusLinkDao: MultiwaveBolusLinkDao
abstract val totalDailyDoseDao: TotalDailyDoseDao
abstract val carbsDao: CarbsDao
abstract val mealLinkDao: MealLinkDao
abstract val temporaryTargetDao: TemporaryTargetDao
abstract val apsResultLinkDao: APSResultLinkDao
abstract val bolusCalculatorResultDao: BolusCalculatorResultDao
abstract val effectiveProfileSwitchDao: EffectiveProfileSwitchDao
abstract val profileSwitchDao: ProfileSwitchDao
abstract val apsResultDao: APSResultDao
abstract val versionChangeDao: VersionChangeDao
abstract val preferenceChangeDao: PreferenceChangeDao
}

View file

@ -0,0 +1,118 @@
package info.nightscout.androidaps.database
import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.database.entities.TemporaryTarget
import info.nightscout.androidaps.database.interfaces.DBEntry
import info.nightscout.androidaps.database.transactions.Transaction
import io.reactivex.Completable
import io.reactivex.Maybe
import io.reactivex.Observable
import io.reactivex.Single
import io.reactivex.schedulers.Schedulers
import io.reactivex.subjects.PublishSubject
import java.util.concurrent.Callable
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class AppRepository @Inject internal constructor(
internal val database: AppDatabase
) {
private val changeSubject = PublishSubject.create<List<DBEntry>>()
fun changeObservable(): Observable<List<DBEntry>> = changeSubject.subscribeOn(Schedulers.io())
val databaseVersion = DATABASE_VERSION
/**
* Executes a transaction ignoring its result
* Runs on IO scheduler
*/
fun <T> runTransaction(transaction: Transaction<T>): Completable {
val changes = mutableListOf<DBEntry>()
return Completable.fromCallable {
database.runInTransaction {
transaction.database = DelegatedAppDatabase(changes, database)
transaction.run()
}
}.subscribeOn(Schedulers.io()).doOnComplete {
changeSubject.onNext(changes)
}
}
/**
* Executes a transaction and returns its result
* Runs on IO scheduler
*/
fun <T> runTransactionForResult(transaction: Transaction<T>): Single<T> {
val changes = mutableListOf<DBEntry>()
return Single.fromCallable {
database.runInTransaction(Callable<T> {
transaction.database = DelegatedAppDatabase(changes, database)
transaction.run()
})
}.subscribeOn(Schedulers.io()).doOnSuccess {
changeSubject.onNext(changes)
}
}
fun clearDatabases() = database.clearAllTables()
//BG READINGS -- only valid records
fun compatGetBgReadingsDataFromTime(timestamp: Long, ascending: Boolean) =
database.glucoseValueDao.compatGetBgReadingsDataFromTime(timestamp)
.map { if (!ascending) it.reversed() else it }
.subscribeOn(Schedulers.io())
fun compatGetBgReadingsDataFromTime(start: Long, end: Long, ascending: Boolean) =
database.glucoseValueDao.compatGetBgReadingsDataFromTime(start, end)
.map { if (!ascending) it.reversed() else it }
.subscribeOn(Schedulers.io())
//BG READINGS -- including invalid/history records
fun findBgReadingByNSIdSingle(nsId: String): Single<ValueWrapper<GlucoseValue>> =
database.glucoseValueDao.findByNSIdMaybe(nsId).toWrappedSingle()
fun getModifiedBgReadingsDataFromId(lastId: Long) =
database.glucoseValueDao.getModifiedFrom(lastId)
.subscribeOn(Schedulers.io())
fun getBgReadingsCorrespondingLastHistoryRecord(lastId: Long) =
database.glucoseValueDao.getLastHistoryRecord(lastId)
@Suppress("unused") // debug purpose only
fun getAllBgReadingsStartingFrom(lastId: Long) =
database.glucoseValueDao.getAllStartingFrom(lastId)
.subscribeOn(Schedulers.io())
// TEMP TARGETS
fun compatGetTemporaryTargetData() =
database.temporaryTargetDao.compatGetTemporaryTargetData()
fun compatGetTemporaryTargetDataFromTime(timestamp: Long, ascending: Boolean) =
database.temporaryTargetDao.compatGetTemporaryTargetDataFromTime(timestamp)
.map { if (!ascending) it.reversed() else it }
.subscribeOn(Schedulers.io())
fun findTemporaryTargetByNSIdSingle(nsId: String): Single<ValueWrapper<TemporaryTarget>> =
database.temporaryTargetDao.findByNSIdMaybe(nsId).toWrappedSingle()
fun getModifiedTemporaryTargetsDataFromId(lastId: Long) =
database.temporaryTargetDao.getModifiedFrom(lastId)
.subscribeOn(Schedulers.io())
fun getTemporaryTargetsCorrespondingLastHistoryRecord(lastId: Long) =
database.temporaryTargetDao.getLastHistoryRecord(lastId)
}
inline fun <reified T> Maybe<T>.toWrappedSingle(): Single<ValueWrapper<T>> =
this.map { ValueWrapper.Existing(it) as ValueWrapper<T> }
.switchIfEmpty(Maybe.just(ValueWrapper.Absent()))
.toSingle()
sealed class ValueWrapper<T> {
data class Existing<T>(val value: T) : ValueWrapper<T>()
class Absent<T> : ValueWrapper<T>()
}

View file

@ -0,0 +1,142 @@
package info.nightscout.androidaps.database
import androidx.room.TypeConverter
import info.nightscout.androidaps.database.data.Block
import info.nightscout.androidaps.database.data.TargetBlock
import info.nightscout.androidaps.database.embedments.InterfaceIDs
import info.nightscout.androidaps.database.entities.*
import org.json.JSONArray
import org.json.JSONObject
class Converters {
@TypeConverter
fun fromBolusType(bolusType: Bolus.Type?) = bolusType?.name
@TypeConverter
fun toBolusType(bolusType: String?) = bolusType?.let { Bolus.Type.valueOf(it) }
@TypeConverter
fun fromTrendArrow(trendArrow: GlucoseValue.TrendArrow?) = trendArrow?.name
@TypeConverter
fun toTrendArrow(trendArrow: String?) = trendArrow?.let { GlucoseValue.TrendArrow.valueOf(it) }
@TypeConverter
fun fromSourceSensor(sourceSensor: GlucoseValue.SourceSensor?) = sourceSensor?.name
@TypeConverter
fun toSourceSensor(sourceSensor: String?) = sourceSensor?.let { GlucoseValue.SourceSensor.valueOf(it) }
@TypeConverter
fun fromTBRType(tbrType: TemporaryBasal.Type?) = tbrType?.name
@TypeConverter
fun toTBRType(tbrType: String?) = tbrType?.let { TemporaryBasal.Type.valueOf(it) }
@TypeConverter
fun fromTempTargetReason(tempTargetReason: TemporaryTarget.Reason?) = tempTargetReason?.name
@TypeConverter
fun toTempTargetReason(tempTargetReason: String?) = tempTargetReason?.let { TemporaryTarget.Reason.valueOf(it) }
@TypeConverter
fun fromTherapyEventType(therapyEventType: TherapyEvent.Type?) = therapyEventType?.name
@TypeConverter
fun toTherapyEventType(therapyEventType: String?) = therapyEventType?.let { TherapyEvent.Type.valueOf(it) }
@TypeConverter
fun fromGlucoseUnit(glucoseUnit: ProfileSwitch.GlucoseUnit?) = glucoseUnit?.name
@TypeConverter
fun toGlucoseUnit(glucoseUnit: String?) = glucoseUnit?.let { ProfileSwitch.GlucoseUnit.valueOf(it) }
@TypeConverter
fun fromPumpType(pumpType: InterfaceIDs.PumpType?) = pumpType?.name
@TypeConverter
fun toPumpType(pumpType: String?) = pumpType?.let { InterfaceIDs.PumpType.valueOf(it) }
@TypeConverter
fun fromAlgorithm(algorithm: APSResult.Algorithm?) = algorithm?.name
@TypeConverter
fun toAlgorithm(algorithm: String?) = algorithm?.let { APSResult.Algorithm.valueOf(it) }
@TypeConverter
fun fromListOfBlocks(blocks: List<Block>?): String? {
if (blocks == null) return null
val jsonArray = JSONArray()
blocks.forEach {
val jsonObject = JSONObject()
jsonObject.put("duration", it.duration)
jsonObject.put("amount", it.amount)
jsonArray.put(jsonObject)
}
return jsonArray.toString()
}
@TypeConverter
fun toListOfBlocks(jsonString: String?): List<Block>? {
if (jsonString == null) return null
val jsonArray = JSONArray(jsonString)
val list = mutableListOf<Block>()
for (i in 0 until jsonArray.length()) {
val jsonObject = jsonArray.getJSONObject(i)
list.add(Block(jsonObject.getLong("duration"), jsonObject.getDouble("amount")))
}
return list
}
@TypeConverter
fun anyToString(value: Any?) = when (value) {
null -> null
is String -> "S$value"
is Int -> "I$value"
is Long -> "L$value"
is Boolean -> "B$value"
is Float -> "F$value"
else -> throw IllegalArgumentException("Type not supported")
}
@TypeConverter
fun stringToAny(value: String?): Any? = when {
value == null -> null
value.startsWith("S") -> value.substring(1)
value.startsWith("I") -> value.substring(1).toInt()
value.startsWith("L") -> value.substring(1).toLong()
value.startsWith("B") -> value.substring(1).toBoolean()
value.startsWith("F") -> value.substring(1).toFloat()
else -> throw IllegalArgumentException("Type not supported")
}
@TypeConverter
fun fromListOfTargetBlocks(blocks: List<TargetBlock>?): String? {
if (blocks == null) return null
val jsonArray = JSONArray()
blocks.forEach {
val jsonObject = JSONObject()
jsonObject.put("duration", it.duration)
jsonObject.put("lowTarget", it.lowTarget)
jsonObject.put("highTarget", it.highTarget)
jsonArray.put(jsonObject)
}
return jsonArray.toString()
}
@TypeConverter
fun toListOfTargetBlocks(jsonString: String?): List<TargetBlock>? {
if (jsonString == null) return null
val jsonArray = JSONArray(jsonString)
val list = mutableListOf<TargetBlock>()
for (i in 0 until jsonArray.length()) {
val jsonObject = jsonArray.getJSONObject(i)
list.add(TargetBlock(jsonObject.getLong("duration"),
jsonObject.getDouble("lowTarget"),
jsonObject.getDouble("highTarget")))
}
return list
}
}

View file

@ -0,0 +1,24 @@
package info.nightscout.androidaps.database
import android.content.Context
import androidx.room.Room
import dagger.Module
import dagger.Provides
import javax.inject.Qualifier
import javax.inject.Singleton
@Module
open class DatabaseModule {
@DbFileName
@Provides
fun dbFileName() = "androidaps.db"
@Provides
@Singleton
internal fun provideAppDatabase(context: Context, @DbFileName fileName: String) =
Room.databaseBuilder(context, AppDatabase::class.java, fileName).build()
@Qualifier
annotation class DbFileName
}

View file

@ -0,0 +1,27 @@
package info.nightscout.androidaps.database
import info.nightscout.androidaps.database.daos.*
import info.nightscout.androidaps.database.daos.delegated.*
import info.nightscout.androidaps.database.interfaces.DBEntry
internal class DelegatedAppDatabase(val changes: MutableList<DBEntry>, val database: AppDatabase) {
val glucoseValueDao: GlucoseValueDao = DelegatedGlucoseValueDao(changes, database.glucoseValueDao)
val therapyEventDao: TherapyEventDao = DelegatedTherapyEventDao(changes, database.therapyEventDao)
val temporaryBasalDao: TemporaryBasalDao = DelegatedTemporaryBasalDao(changes, database.temporaryBasalDao)
val bolusDao: BolusDao = DelegatedBolusDao(changes, database.bolusDao)
val extendedBolusDao: ExtendedBolusDao = DelegatedExtendedExtendedBolusDao(changes, database.extendedBolusDao)
val multiwaveBolusLinkDao: MultiwaveBolusLinkDao = DelegatedMultiwaveBolusLinkDao(changes, database.multiwaveBolusLinkDao)
val totalDailyDoseDao: TotalDailyDoseDao = DelegatedTotalDailyDoseDao(changes, database.totalDailyDoseDao)
val carbsDao: CarbsDao = DelegatedCarbsDao(changes, database.carbsDao)
val mealLinkDao: MealLinkDao = DelegatedMealLinkDao(changes, database.mealLinkDao)
val temporaryTargetDao: TemporaryTargetDao = DelegatedTemporaryTargetDao(changes, database.temporaryTargetDao)
val apsResultLinkDao: APSResultLinkDao = DelegatedAPSResultLinkLinkDao(changes, database.apsResultLinkDao)
val bolusCalculatorResultDao: BolusCalculatorResultDao = DelegatedBolusCalculatorResultDao(changes, database.bolusCalculatorResultDao)
val effectiveProfileSwitchDao: EffectiveProfileSwitchDao = DelegatedEffectiveProfileSwitchDao(changes, database.effectiveProfileSwitchDao)
val profileSwitchDao: ProfileSwitchDao = DelegatedProfileSwitchDao(changes, database.profileSwitchDao)
val apsResultDao: APSResultDao = DelegatedAPSResultDao(changes, database.apsResultDao)
val versionChangeDao: VersionChangeDao = DelegatedVersionChangeDao(changes, database.versionChangeDao)
val preferenceChangeDao: PreferenceChangeDao = DelegatedPreferenceChangeDao(changes, database.preferenceChangeDao)
fun clearAllTables() = database.clearAllTables()
}

View file

@ -0,0 +1,19 @@
package info.nightscout.androidaps.database
const val TABLE_APS_RESULTS = "apsResults"
const val TABLE_APS_RESULT_LINKS = "apsResultLinks"
const val TABLE_BOLUSES = "boluses"
const val TABLE_BOLUS_CALCULATOR_RESULTS = "bolusCalculatorResults"
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_MEAL_LINKS = "mealLinks"
const val TABLE_MULTIWAVE_BOLUS_LINKS = "multiwaveBolusLinks"
const val TABLE_PROFILE_SWITCHES = "profileSwitches"
const val TABLE_TEMPORARY_BASALS = "temporaryBasals"
const val TABLE_TEMPORARY_TARGETS = "temporaryTargets"
const val TABLE_TOTAL_DAILY_DOSES = "totalDailyDoses"
const val TABLE_THERAPY_EVENTS = "therapyEvents"
const val TABLE_PREFERENCE_CHANGES = "preferenceChanges"
const val TABLE_VERSION_CHANGES = "versionChanges"

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.database.daos
import androidx.room.Dao
import androidx.room.Query
import info.nightscout.androidaps.database.TABLE_APS_RESULTS
import info.nightscout.androidaps.database.entities.APSResult
import io.reactivex.Single
@Suppress("FunctionName")
@Dao
internal interface APSResultDao : TraceableDao<APSResult> {
@Query("SELECT * FROM $TABLE_APS_RESULTS WHERE id = :id")
override fun findById(id: Long): APSResult?
@Query("DELETE FROM $TABLE_APS_RESULTS")
override fun deleteAllEntries()
}

View file

@ -0,0 +1,19 @@
package info.nightscout.androidaps.database.daos
import androidx.room.Dao
import androidx.room.Query
import info.nightscout.androidaps.database.TABLE_APS_RESULTS
import info.nightscout.androidaps.database.TABLE_APS_RESULT_LINKS
import info.nightscout.androidaps.database.entities.APSResultLink
import io.reactivex.Single
@Suppress("FunctionName")
@Dao
internal interface APSResultLinkDao : TraceableDao<APSResultLink> {
@Query("SELECT * FROM $TABLE_APS_RESULT_LINKS WHERE id = :id")
override fun findById(id: Long): APSResultLink?
@Query("DELETE FROM $TABLE_APS_RESULTS")
override fun deleteAllEntries()
}

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.database.daos
import androidx.room.Dao
import androidx.room.Query
import info.nightscout.androidaps.database.TABLE_BOLUS_CALCULATOR_RESULTS
import info.nightscout.androidaps.database.entities.BolusCalculatorResult
import io.reactivex.Single
@Suppress("FunctionName")
@Dao
internal interface BolusCalculatorResultDao : TraceableDao<BolusCalculatorResult> {
@Query("SELECT * FROM $TABLE_BOLUS_CALCULATOR_RESULTS WHERE id = :id")
override fun findById(id: Long): BolusCalculatorResult?
@Query("DELETE FROM $TABLE_BOLUS_CALCULATOR_RESULTS")
override fun deleteAllEntries()
}

View file

@ -0,0 +1,19 @@
package info.nightscout.androidaps.database.daos
import androidx.room.Dao
import androidx.room.Query
import info.nightscout.androidaps.database.TABLE_BOLUSES
import info.nightscout.androidaps.database.embedments.InterfaceIDs
import info.nightscout.androidaps.database.entities.Bolus
import io.reactivex.Single
@Suppress("FunctionName")
@Dao
internal interface BolusDao : TraceableDao<Bolus> {
@Query("SELECT * FROM $TABLE_BOLUSES WHERE id = :id")
override fun findById(id: Long): Bolus?
@Query("DELETE FROM $TABLE_BOLUSES")
override fun deleteAllEntries()
}

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.database.daos
import androidx.room.Dao
import androidx.room.Query
import info.nightscout.androidaps.database.TABLE_CARBS
import info.nightscout.androidaps.database.entities.Carbs
import io.reactivex.Single
@Suppress("FunctionName")
@Dao
internal interface CarbsDao : TraceableDao<Carbs> {
@Query("SELECT * FROM $TABLE_CARBS WHERE id = :id")
override fun findById(id: Long): Carbs?
@Query("DELETE FROM $TABLE_CARBS")
override fun deleteAllEntries()
}

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.database.daos
import androidx.room.Dao
import androidx.room.Query
import info.nightscout.androidaps.database.TABLE_EFFECTIVE_PROFILE_SWITCHES
import info.nightscout.androidaps.database.entities.EffectiveProfileSwitch
import io.reactivex.Single
@Suppress("FunctionName")
@Dao
internal interface EffectiveProfileSwitchDao : TraceableDao<EffectiveProfileSwitch> {
@Query("SELECT * FROM $TABLE_EFFECTIVE_PROFILE_SWITCHES WHERE id = :id")
override fun findById(id: Long): EffectiveProfileSwitch?
@Query("DELETE FROM $TABLE_EFFECTIVE_PROFILE_SWITCHES")
override fun deleteAllEntries()
}

View file

@ -0,0 +1,20 @@
package info.nightscout.androidaps.database.daos
import androidx.room.Dao
import androidx.room.Query
import info.nightscout.androidaps.database.TABLE_EXTENDED_BOLUSES
import info.nightscout.androidaps.database.embedments.InterfaceIDs
import info.nightscout.androidaps.database.entities.ExtendedBolus
import io.reactivex.Flowable
import io.reactivex.Single
@Suppress("FunctionName")
@Dao
internal interface ExtendedBolusDao : TraceableDao<ExtendedBolus> {
@Query("SELECT * FROM $TABLE_EXTENDED_BOLUSES WHERE id = :id")
override fun findById(id: Long): ExtendedBolus?
@Query("DELETE FROM $TABLE_EXTENDED_BOLUSES")
override fun deleteAllEntries()
}

View file

@ -0,0 +1,42 @@
package info.nightscout.androidaps.database.daos
import androidx.room.Dao
import androidx.room.Query
import info.nightscout.androidaps.database.TABLE_GLUCOSE_VALUES
import info.nightscout.androidaps.database.entities.GlucoseValue
import io.reactivex.Maybe
import io.reactivex.Single
@Dao
internal interface GlucoseValueDao : TraceableDao<GlucoseValue> {
@Query("SELECT * FROM $TABLE_GLUCOSE_VALUES WHERE id = :id")
override fun findById(id: Long): GlucoseValue?
@Query("DELETE FROM $TABLE_GLUCOSE_VALUES")
override fun deleteAllEntries()
@Query("SELECT * FROM $TABLE_GLUCOSE_VALUES WHERE nightscoutId = :nsId AND referenceId IS NULL")
fun findByNSIdMaybe(nsId: String): Maybe<GlucoseValue>
@Query("SELECT * FROM $TABLE_GLUCOSE_VALUES WHERE timestamp = :timestamp AND sourceSensor = :sourceSensor AND referenceId IS NULL")
fun findByTimestampAndSensor(timestamp: Long, sourceSensor: GlucoseValue.SourceSensor): GlucoseValue?
@Query("SELECT * FROM $TABLE_GLUCOSE_VALUES WHERE timestamp >= :timestamp AND isValid = 1 AND referenceId IS NULL AND value >= 39 ORDER BY timestamp ASC")
fun compatGetBgReadingsDataFromTime(timestamp: Long): Single<List<GlucoseValue>>
@Query("SELECT * FROM $TABLE_GLUCOSE_VALUES WHERE timestamp BETWEEN :start AND :end AND isValid = 1 AND referenceId IS NULL AND value >= 39 ORDER BY timestamp ASC")
fun compatGetBgReadingsDataFromTime(start: Long, end: Long): Single<List<GlucoseValue>>
@Query("SELECT * FROM $TABLE_GLUCOSE_VALUES WHERE id > :lastId AND referenceId IS NULL ORDER BY timestamp ASC")
fun getDataFromId(lastId: Long): Single<List<GlucoseValue>>
@Query("SELECT * FROM $TABLE_GLUCOSE_VALUES WHERE id >= :id")
fun getAllStartingFrom(id: Long): Single<List<GlucoseValue>>
@Query("SELECT * FROM $TABLE_GLUCOSE_VALUES WHERE referenceId = :id ORDER BY id DESC LIMIT 1")
fun getLastHistoryRecord(id: Long): GlucoseValue?
@Query("SELECT * FROM $TABLE_GLUCOSE_VALUES WHERE id > :id AND referenceId IS NULL OR id IN (SELECT DISTINCT referenceId FROM $TABLE_GLUCOSE_VALUES WHERE id > :id) ORDER BY id ASC")
fun getModifiedFrom(id: Long): Single<List<GlucoseValue>>
}

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.database.daos
import androidx.room.Dao
import androidx.room.Query
import info.nightscout.androidaps.database.TABLE_MEAL_LINKS
import info.nightscout.androidaps.database.entities.MealLink
import io.reactivex.Single
@Suppress("FunctionName")
@Dao
internal interface MealLinkDao : TraceableDao<MealLink> {
@Query("SELECT * FROM $TABLE_MEAL_LINKS WHERE id = :id")
override fun findById(id: Long): MealLink?
@Query("DELETE FROM $TABLE_MEAL_LINKS")
override fun deleteAllEntries()
}

View file

@ -0,0 +1,19 @@
package info.nightscout.androidaps.database.daos
import androidx.room.Dao
import androidx.room.Query
import info.nightscout.androidaps.database.TABLE_MEAL_LINKS
import info.nightscout.androidaps.database.TABLE_MULTIWAVE_BOLUS_LINKS
import info.nightscout.androidaps.database.entities.MultiwaveBolusLink
import io.reactivex.Single
@Suppress("FunctionName")
@Dao
internal interface MultiwaveBolusLinkDao : TraceableDao<MultiwaveBolusLink> {
@Query("SELECT * FROM $TABLE_MULTIWAVE_BOLUS_LINKS WHERE id = :id")
override fun findById(id: Long): MultiwaveBolusLink?
@Query("DELETE FROM $TABLE_MEAL_LINKS")
override fun deleteAllEntries()
}

View file

@ -0,0 +1,16 @@
package info.nightscout.androidaps.database.daos
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import info.nightscout.androidaps.database.TABLE_PREFERENCE_CHANGES
import info.nightscout.androidaps.database.entities.PreferenceChange
import io.reactivex.Single
@Dao
interface PreferenceChangeDao {
@Insert
fun insert(preferenceChange: PreferenceChange)
}

View file

@ -0,0 +1,37 @@
package info.nightscout.androidaps.database.daos
import androidx.room.Dao
import androidx.room.Query
import info.nightscout.androidaps.database.TABLE_PROFILE_SWITCHES
import info.nightscout.androidaps.database.data.checkSanity
import info.nightscout.androidaps.database.daos.workaround.ProfileSwitchDaoWorkaround
import info.nightscout.androidaps.database.entities.ProfileSwitch
import io.reactivex.Flowable
import io.reactivex.Single
@Suppress("FunctionName")
@Dao
internal interface ProfileSwitchDao : ProfileSwitchDaoWorkaround {
@Query("SELECT * FROM $TABLE_PROFILE_SWITCHES WHERE id = :id")
override fun findById(id: Long): ProfileSwitch?
@Query("DELETE FROM $TABLE_PROFILE_SWITCHES")
override fun deleteAllEntries()
}
internal fun ProfileSwitchDao.insertNewEntryImpl(entry: ProfileSwitch): Long {
if (!entry.basalBlocks.checkSanity()) throw IllegalArgumentException("Sanity check failed for basal blocks.")
if (!entry.icBlocks.checkSanity()) throw IllegalArgumentException("Sanity check failed for IC blocks.")
if (!entry.isfBlocks.checkSanity()) throw IllegalArgumentException("Sanity check failed for ISF blocks.")
if (!entry.targetBlocks.checkSanity()) throw IllegalArgumentException("Sanity check failed for target blocks.")
return (this as TraceableDao<ProfileSwitch>).insertNewEntryImpl(entry)
}
internal fun ProfileSwitchDao.updateExistingEntryImpl(entry: ProfileSwitch): Long {
if (!entry.basalBlocks.checkSanity()) throw IllegalArgumentException("Sanity check failed for basal blocks.")
if (!entry.icBlocks.checkSanity()) throw IllegalArgumentException("Sanity check failed for IC blocks.")
if (!entry.isfBlocks.checkSanity()) throw IllegalArgumentException("Sanity check failed for ISF blocks.")
if (!entry.targetBlocks.checkSanity()) throw IllegalArgumentException("Sanity check failed for target blocks.")
return (this as TraceableDao<ProfileSwitch>).updateExistingEntryImpl(entry)
}

View file

@ -0,0 +1,21 @@
package info.nightscout.androidaps.database.daos
import androidx.room.Dao
import androidx.room.Query
import info.nightscout.androidaps.database.TABLE_TEMPORARY_BASALS
import info.nightscout.androidaps.database.embedments.InterfaceIDs
import info.nightscout.androidaps.database.entities.TemporaryBasal
import io.reactivex.Flowable
import io.reactivex.Maybe
import io.reactivex.Single
@Suppress("FunctionName")
@Dao
internal interface TemporaryBasalDao : TraceableDao<TemporaryBasal> {
@Query("SELECT * FROM $TABLE_TEMPORARY_BASALS WHERE id = :id")
override fun findById(id: Long): TemporaryBasal?
@Query("DELETE FROM $TABLE_TEMPORARY_BASALS")
override fun deleteAllEntries()
}

View file

@ -0,0 +1,38 @@
package info.nightscout.androidaps.database.daos
import androidx.room.Dao
import androidx.room.Query
import info.nightscout.androidaps.database.TABLE_TEMPORARY_TARGETS
import info.nightscout.androidaps.database.entities.TemporaryTarget
import io.reactivex.Maybe
import io.reactivex.Observable
import io.reactivex.Single
@Suppress("FunctionName")
@Dao
internal interface TemporaryTargetDao : TraceableDao<TemporaryTarget> {
@Query("SELECT * FROM $TABLE_TEMPORARY_TARGETS WHERE id = :id")
override fun findById(id: Long): TemporaryTarget?
@Query("DELETE FROM $TABLE_TEMPORARY_TARGETS")
override fun deleteAllEntries()
@Query("SELECT * FROM $TABLE_TEMPORARY_TARGETS WHERE nightscoutId = :nsId AND referenceId IS NULL")
fun findByNSIdMaybe(nsId: String): Maybe<TemporaryTarget>
@Query("SELECT * FROM $TABLE_TEMPORARY_TARGETS WHERE timestamp <= :timestamp AND (timestamp + duration) > :timestamp AND referenceId IS NULL AND isValid = 1 ORDER BY timestamp DESC LIMIT 1")
fun getTemporaryTargetActiveAt(timestamp: Long): TemporaryTarget?
@Query("SELECT * FROM $TABLE_TEMPORARY_TARGETS WHERE timestamp >= :timestamp AND isValid = 1 AND referenceId IS NULL ORDER BY timestamp ASC")
fun compatGetTemporaryTargetDataFromTime(timestamp: Long): Single<List<TemporaryTarget>>
@Query("SELECT * FROM $TABLE_TEMPORARY_TARGETS WHERE isValid = 1 AND referenceId IS NULL ORDER BY timestamp ASC")
fun compatGetTemporaryTargetData(): List<TemporaryTarget>
@Query("SELECT * FROM $TABLE_TEMPORARY_TARGETS WHERE referenceId = :id ORDER BY id DESC LIMIT 1")
fun getLastHistoryRecord(id: Long): TemporaryTarget?
@Query("SELECT * FROM $TABLE_TEMPORARY_TARGETS WHERE id > :id AND referenceId IS NULL OR id IN (SELECT DISTINCT referenceId FROM $TABLE_TEMPORARY_TARGETS WHERE id > :id) ORDER BY id ASC")
fun getModifiedFrom(id: Long): Single<List<TemporaryTarget>>
}

View file

@ -0,0 +1,24 @@
package info.nightscout.androidaps.database.daos
import androidx.room.Dao
import androidx.room.Query
import info.nightscout.androidaps.database.TABLE_THERAPY_EVENTS
import info.nightscout.androidaps.database.embedments.InterfaceIDs
import info.nightscout.androidaps.database.entities.TherapyEvent
import io.reactivex.Flowable
import io.reactivex.Maybe
import io.reactivex.Single
@Dao
internal interface TherapyEventDao : TraceableDao<TherapyEvent> {
@Query("SELECT * FROM $TABLE_THERAPY_EVENTS WHERE id = :id")
override fun findById(id: Long): TherapyEvent?
@Query("DELETE FROM $TABLE_THERAPY_EVENTS")
override fun deleteAllEntries()
@Query("SELECT * FROM $TABLE_THERAPY_EVENTS WHERE type = :type AND timestamp = :timestamp AND referenceId IS NULL")
fun findByTimestamp(type: TherapyEvent.Type, timestamp: Long): TherapyEvent?
}

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.database.daos
import androidx.room.Dao
import androidx.room.Query
import info.nightscout.androidaps.database.TABLE_TOTAL_DAILY_DOSES
import info.nightscout.androidaps.database.entities.TotalDailyDose
import io.reactivex.Single
@Suppress("FunctionName")
@Dao
internal interface TotalDailyDoseDao : TraceableDao<TotalDailyDose> {
@Query("SELECT * FROM $TABLE_TOTAL_DAILY_DOSES WHERE id = :id")
override fun findById(id: Long): TotalDailyDose?
@Query("DELETE FROM $TABLE_TOTAL_DAILY_DOSES")
override fun deleteAllEntries()
}

View file

@ -0,0 +1,60 @@
package info.nightscout.androidaps.database.daos
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<T : TraceableDBEntry> : TraceableDaoWorkaround<T> {
fun findById(id: Long): T?
fun deleteAllEntries()
//fun getAllStartingFrom(id: Long): Single<List<T>>
@Insert
fun insert(entry: T): Long
@Update
fun update(entry: T)
}
/**
* Inserts a new entry
* @return The ID of the newly generated entry
*/
//@Transaction
internal fun <T : TraceableDBEntry> TraceableDao<T>.insertNewEntryImpl(entry: T): Long {
if (entry.id != 0L) throw IllegalArgumentException("ID must be 0.")
if (entry.version != 0) throw IllegalArgumentException("Version must be 0.")
if (entry.referenceId != null) throw IllegalArgumentException("Reference ID must be null.")
if (!entry.foreignKeysValid) throw IllegalArgumentException("One or more foreign keys are invalid (e.g. 0 value).")
val lastModified = System.currentTimeMillis()
entry.dateCreated = lastModified
val id = insert(entry)
entry.id = id
return id
}
/**
* Updates an existing entry
* @return The ID of the newly generated HISTORIC entry
*/
//@Transaction
internal fun <T : TraceableDBEntry> TraceableDao<T>.updateExistingEntryImpl(entry: T): Long {
if (entry.id == 0L) throw IllegalArgumentException("ID must not be 0.")
if (entry.referenceId != null) throw IllegalArgumentException("Reference ID must be null.")
if (!entry.foreignKeysValid) throw IllegalArgumentException("One or more foreign keys are invalid (e.g. 0 value).")
val lastModified = System.currentTimeMillis()
entry.dateCreated = lastModified
val current = findById(entry.id)
?: throw IllegalArgumentException("The entry with the specified ID does not exist.")
if (current.referenceId != null) throw IllegalArgumentException("The entry with the specified ID is historic and cannot be updated.")
entry.version = current.version + 1
update(entry)
current.referenceId = entry.id
current.id = 0
return insert(current)
}

View file

@ -0,0 +1,19 @@
package info.nightscout.androidaps.database.daos
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import info.nightscout.androidaps.database.TABLE_VERSION_CHANGES
import info.nightscout.androidaps.database.entities.VersionChange
import io.reactivex.Single
@Dao
interface VersionChangeDao {
@Insert
fun insert(versionChange: VersionChange)
@Query("SELECT * FROM $TABLE_VERSION_CHANGES ORDER BY id DESC LIMIT 1")
fun getMostRecentVersionChange(): VersionChange?
}

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.database.daos.delegated
import info.nightscout.androidaps.database.daos.APSResultDao
import info.nightscout.androidaps.database.entities.APSResult
import info.nightscout.androidaps.database.interfaces.DBEntry
internal class DelegatedAPSResultDao(changes: MutableList<DBEntry>, private val dao: APSResultDao) : DelegatedDao(changes), APSResultDao by dao {
override fun insertNewEntry(entry: APSResult): Long {
changes.add(entry)
return dao.insertNewEntry(entry)
}
override fun updateExistingEntry(entry: APSResult): Long {
changes.add(entry)
return dao.updateExistingEntry(entry)
}
}

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.database.daos.delegated
import info.nightscout.androidaps.database.daos.APSResultLinkDao
import info.nightscout.androidaps.database.entities.APSResultLink
import info.nightscout.androidaps.database.interfaces.DBEntry
internal class DelegatedAPSResultLinkLinkDao(changes: MutableList<DBEntry>, private val dao: APSResultLinkDao) : DelegatedDao(changes), APSResultLinkDao by dao {
override fun insertNewEntry(entry: APSResultLink): Long {
changes.add(entry)
return dao.insertNewEntry(entry)
}
override fun updateExistingEntry(entry: APSResultLink): Long {
changes.add(entry)
return dao.updateExistingEntry(entry)
}
}

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.database.daos.delegated
import info.nightscout.androidaps.database.daos.BolusCalculatorResultDao
import info.nightscout.androidaps.database.entities.BolusCalculatorResult
import info.nightscout.androidaps.database.interfaces.DBEntry
internal class DelegatedBolusCalculatorResultDao(changes: MutableList<DBEntry>, private val dao: BolusCalculatorResultDao) : DelegatedDao(changes), BolusCalculatorResultDao by dao {
override fun insertNewEntry(entry: BolusCalculatorResult): Long {
changes.add(entry)
return dao.insertNewEntry(entry)
}
override fun updateExistingEntry(entry: BolusCalculatorResult): Long {
changes.add(entry)
return dao.updateExistingEntry(entry)
}
}

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.database.daos.delegated
import info.nightscout.androidaps.database.daos.BolusDao
import info.nightscout.androidaps.database.entities.Bolus
import info.nightscout.androidaps.database.interfaces.DBEntry
internal class DelegatedBolusDao(changes: MutableList<DBEntry>, private val dao: BolusDao) : DelegatedDao(changes), BolusDao by dao {
override fun insertNewEntry(entry: Bolus): Long {
changes.add(entry)
return dao.insertNewEntry(entry)
}
override fun updateExistingEntry(entry: Bolus): Long {
changes.add(entry)
return dao.updateExistingEntry(entry)
}
}

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.database.daos.delegated
import info.nightscout.androidaps.database.daos.CarbsDao
import info.nightscout.androidaps.database.entities.Carbs
import info.nightscout.androidaps.database.interfaces.DBEntry
internal class DelegatedCarbsDao(changes: MutableList<DBEntry>, private val dao: CarbsDao) : DelegatedDao(changes), CarbsDao by dao {
override fun insertNewEntry(entry: Carbs): Long {
changes.add(entry)
return dao.insertNewEntry(entry)
}
override fun updateExistingEntry(entry: Carbs): Long {
changes.add(entry)
return dao.updateExistingEntry(entry)
}
}

View file

@ -0,0 +1,8 @@
package info.nightscout.androidaps.database.daos.delegated
import info.nightscout.androidaps.database.interfaces.DBEntry
/**
* A DAO that adds updated or inserted entries to a list
*/
internal abstract class DelegatedDao(protected val changes: MutableList<DBEntry>)

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.database.daos.delegated
import info.nightscout.androidaps.database.daos.EffectiveProfileSwitchDao
import info.nightscout.androidaps.database.entities.EffectiveProfileSwitch
import info.nightscout.androidaps.database.interfaces.DBEntry
internal class DelegatedEffectiveProfileSwitchDao(changes: MutableList<DBEntry>, private val dao: EffectiveProfileSwitchDao) : DelegatedDao(changes), EffectiveProfileSwitchDao by dao {
override fun insertNewEntry(entry: EffectiveProfileSwitch): Long {
changes.add(entry)
return super.insertNewEntry(entry)
}
override fun updateExistingEntry(entry: EffectiveProfileSwitch): Long {
changes.add(entry)
return super.updateExistingEntry(entry)
}
}

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.database.daos.delegated
import info.nightscout.androidaps.database.daos.ExtendedBolusDao
import info.nightscout.androidaps.database.entities.ExtendedBolus
import info.nightscout.androidaps.database.interfaces.DBEntry
internal class DelegatedExtendedExtendedBolusDao(changes: MutableList<DBEntry>, private val dao: ExtendedBolusDao) : DelegatedDao(changes), ExtendedBolusDao by dao {
override fun insertNewEntry(entry: ExtendedBolus): Long {
changes.add(entry)
return dao.insertNewEntry(entry)
}
override fun updateExistingEntry(entry: ExtendedBolus): Long {
changes.add(entry)
return dao.updateExistingEntry(entry)
}
}

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.database.daos.delegated
import info.nightscout.androidaps.database.daos.GlucoseValueDao
import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.database.interfaces.DBEntry
internal class DelegatedGlucoseValueDao(changes: MutableList<DBEntry>, private val dao: GlucoseValueDao) : DelegatedDao(changes), GlucoseValueDao by dao {
override fun insertNewEntry(entry: GlucoseValue): Long {
changes.add(entry)
return dao.insertNewEntry(entry)
}
override fun updateExistingEntry(entry: GlucoseValue): Long {
changes.add(entry)
return dao.updateExistingEntry(entry)
}
}

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.database.daos.delegated
import info.nightscout.androidaps.database.daos.MealLinkDao
import info.nightscout.androidaps.database.entities.MealLink
import info.nightscout.androidaps.database.interfaces.DBEntry
internal class DelegatedMealLinkDao(changes: MutableList<DBEntry>, private val dao: MealLinkDao) : DelegatedDao(changes), MealLinkDao by dao {
override fun insertNewEntry(entry: MealLink): Long {
changes.add(entry)
return dao.insertNewEntry(entry)
}
override fun updateExistingEntry(entry: MealLink): Long {
changes.add(entry)
return dao.updateExistingEntry(entry)
}
}

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.database.daos.delegated
import info.nightscout.androidaps.database.daos.MultiwaveBolusLinkDao
import info.nightscout.androidaps.database.entities.MultiwaveBolusLink
import info.nightscout.androidaps.database.interfaces.DBEntry
internal class DelegatedMultiwaveBolusLinkDao(changes: MutableList<DBEntry>, private val dao: MultiwaveBolusLinkDao) : DelegatedDao(changes), MultiwaveBolusLinkDao by dao {
override fun insertNewEntry(entry: MultiwaveBolusLink): Long {
changes.add(entry)
return dao.insertNewEntry(entry)
}
override fun updateExistingEntry(entry: MultiwaveBolusLink): Long {
changes.add(entry)
return dao.updateExistingEntry(entry)
}
}

View file

@ -0,0 +1,14 @@
package info.nightscout.androidaps.database.daos.delegated
import info.nightscout.androidaps.database.daos.PreferenceChangeDao
import info.nightscout.androidaps.database.entities.PreferenceChange
import info.nightscout.androidaps.database.interfaces.DBEntry
internal class DelegatedPreferenceChangeDao(changes: MutableList<DBEntry>, private val dao: PreferenceChangeDao) : DelegatedDao(changes), PreferenceChangeDao by dao {
override fun insert(preferenceChange: PreferenceChange) {
changes.add(preferenceChange)
return dao.insert(preferenceChange)
}
}

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.database.daos.delegated
import info.nightscout.androidaps.database.daos.ProfileSwitchDao
import info.nightscout.androidaps.database.entities.ProfileSwitch
import info.nightscout.androidaps.database.interfaces.DBEntry
internal class DelegatedProfileSwitchDao(changes: MutableList<DBEntry>, private val dao: ProfileSwitchDao) : DelegatedDao(changes), ProfileSwitchDao by dao {
override fun insertNewEntry(entry: ProfileSwitch): Long {
changes.add(entry)
return dao.insertNewEntry(entry)
}
override fun updateExistingEntry(entry: ProfileSwitch): Long {
changes.add(entry)
return dao.updateExistingEntry(entry)
}
}

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.database.daos.delegated
import info.nightscout.androidaps.database.daos.TemporaryBasalDao
import info.nightscout.androidaps.database.entities.TemporaryBasal
import info.nightscout.androidaps.database.interfaces.DBEntry
internal class DelegatedTemporaryBasalDao(changes: MutableList<DBEntry>, private val dao: TemporaryBasalDao) : DelegatedDao(changes), TemporaryBasalDao by dao {
override fun insertNewEntry(entry: TemporaryBasal): Long {
changes.add(entry)
return dao.insertNewEntry(entry)
}
override fun updateExistingEntry(entry: TemporaryBasal): Long {
changes.add(entry)
return dao.updateExistingEntry(entry)
}
}

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.database.daos.delegated
import info.nightscout.androidaps.database.daos.TemporaryTargetDao
import info.nightscout.androidaps.database.entities.TemporaryTarget
import info.nightscout.androidaps.database.interfaces.DBEntry
internal class DelegatedTemporaryTargetDao(changes: MutableList<DBEntry>, private val dao: TemporaryTargetDao) : DelegatedDao(changes), TemporaryTargetDao by dao {
override fun insertNewEntry(entry: TemporaryTarget): Long {
changes.add(entry)
return dao.insertNewEntry(entry)
}
override fun updateExistingEntry(entry: TemporaryTarget): Long {
changes.add(entry)
return dao.updateExistingEntry(entry)
}
}

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.database.daos.delegated
import info.nightscout.androidaps.database.daos.TherapyEventDao
import info.nightscout.androidaps.database.entities.TherapyEvent
import info.nightscout.androidaps.database.interfaces.DBEntry
internal class DelegatedTherapyEventDao(changes: MutableList<DBEntry>, private val dao: TherapyEventDao) : DelegatedDao(changes), TherapyEventDao by dao {
override fun insertNewEntry(entry: TherapyEvent): Long {
changes.add(entry)
return dao.insertNewEntry(entry)
}
override fun updateExistingEntry(entry: TherapyEvent): Long {
changes.add(entry)
return dao.updateExistingEntry(entry)
}
}

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.database.daos.delegated
import info.nightscout.androidaps.database.daos.TotalDailyDoseDao
import info.nightscout.androidaps.database.entities.TotalDailyDose
import info.nightscout.androidaps.database.interfaces.DBEntry
internal class DelegatedTotalDailyDoseDao(changes: MutableList<DBEntry>, private val dao: TotalDailyDoseDao) : DelegatedDao(changes), TotalDailyDoseDao by dao {
override fun insertNewEntry(entry: TotalDailyDose): Long {
changes.add(entry)
return dao.insertNewEntry(entry)
}
override fun updateExistingEntry(entry: TotalDailyDose): Long {
changes.add(entry)
return dao.updateExistingEntry(entry)
}
}

View file

@ -0,0 +1,14 @@
package info.nightscout.androidaps.database.daos.delegated
import info.nightscout.androidaps.database.daos.VersionChangeDao
import info.nightscout.androidaps.database.entities.VersionChange
import info.nightscout.androidaps.database.interfaces.DBEntry
internal class DelegatedVersionChangeDao(changes: MutableList<DBEntry>, private val dao: VersionChangeDao) : DelegatedDao(changes), VersionChangeDao by dao {
override fun insert(versionChange: VersionChange) {
changes.add(versionChange)
return dao.insert(versionChange)
}
}

View file

@ -0,0 +1,23 @@
package info.nightscout.androidaps.database.daos.workaround;
import androidx.room.Transaction;
import info.nightscout.androidaps.database.daos.ProfileSwitchDao;
import info.nightscout.androidaps.database.daos.ProfileSwitchDaoKt;
import info.nightscout.androidaps.database.daos.TraceableDao;
import info.nightscout.androidaps.database.entities.ProfileSwitch;
public interface ProfileSwitchDaoWorkaround extends TraceableDao<ProfileSwitch> {
@Override
@Transaction
default long insertNewEntry(ProfileSwitch entry) {
return ProfileSwitchDaoKt.insertNewEntryImpl((ProfileSwitchDao) this, entry);
}
@Override
@Transaction
default long updateExistingEntry(ProfileSwitch entry) {
return ProfileSwitchDaoKt.updateExistingEntryImpl((ProfileSwitchDao) this, entry);
}
}

View file

@ -0,0 +1,31 @@
package info.nightscout.androidaps.database.daos.workaround;
import androidx.room.Transaction;
import info.nightscout.androidaps.database.daos.TraceableDao;
import info.nightscout.androidaps.database.daos.TraceableDaoKt;
import info.nightscout.androidaps.database.interfaces.TraceableDBEntry;
public interface TraceableDaoWorkaround<T extends TraceableDBEntry> {
/**
* Inserts a new entry
*
* @return The ID of the newly generated entry
*/
@Transaction
default long insertNewEntry(T entry) {
return TraceableDaoKt.insertNewEntryImpl((TraceableDao<T>) this, entry);
}
/**
* Updates an existing entry
*
* @return The ID of the newly generated HISTORIC entry
*/
@Transaction
default long updateExistingEntry(T entry) {
return TraceableDaoKt.updateExistingEntryImpl((TraceableDao<T>) this, entry);
}
}

View file

@ -0,0 +1,5 @@
/**
* Workarounds until Kotlin is able to properly translate interface default methods while annotation processing.
* See https://youtrack.jetbrains.com/issue/KT-25960
*/
package info.nightscout.androidaps.database.daos.workaround;

View file

@ -0,0 +1,11 @@
package info.nightscout.androidaps.database.data
import java.util.concurrent.TimeUnit
data class Block(var duration: Long, var amount: Double)
fun List<Block>.checkSanity(): Boolean {
var sum = 0L
forEach { sum += it.duration }
return sum == TimeUnit.DAYS.toMillis(1)
}

View file

@ -0,0 +1,11 @@
package info.nightscout.androidaps.database.data
import java.util.concurrent.TimeUnit
data class TargetBlock(var duration: Long, var lowTarget: Double, var highTarget: Double)
fun List<TargetBlock>.checkSanity(): Boolean {
var sum = 0L
forEach { sum += it.duration }
return sum == TimeUnit.DAYS.toMillis(1)
}

View file

@ -0,0 +1,7 @@
package info.nightscout.androidaps.database.embedments
data class InsulinConfiguration(
var insulinLabel: String,
var insulinEndTime: Long,
var peak: Long
)

View file

@ -0,0 +1,20 @@
package info.nightscout.androidaps.database.embedments
data class InterfaceIDs(
var nightscoutSystemId: String? = null,
var nightscoutId: String? = null,
var pumpType: PumpType? = null,
var pumpSerial: String? = null,
var pumpId: Long? = null,
var startId: Long? = null,
var endId: Long? = null
) {
enum class PumpType {
ACCU_CHEK_INSIGHT,
ACCU_CHEK_COMBO,
DANA_R,
DANA_RS,
MEDTRONIC,
OMNIPOD
}
}

View file

@ -0,0 +1,42 @@
package info.nightscout.androidaps.database.entities
import androidx.room.*
import info.nightscout.androidaps.database.TABLE_APS_RESULTS
import info.nightscout.androidaps.database.embedments.InterfaceIDs
import info.nightscout.androidaps.database.interfaces.DBEntryWithTime
import info.nightscout.androidaps.database.interfaces.TraceableDBEntry
import java.util.TimeZone
@Entity(tableName = TABLE_APS_RESULTS,
foreignKeys = [ForeignKey(
entity = APSResult::class,
parentColumns = ["id"],
childColumns = ["referenceId"])],
indices = [Index("referenceId"), Index("timestamp")])
data class APSResult(
@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,
override var timestamp: Long,
override var utcOffset: Long = TimeZone.getDefault().getOffset(timestamp).toLong(),
var algorithm: Algorithm,
var glucoseStatusJson: String,
var currentTempJson: String,
var iobDataJson: String,
var profileJson: String,
var autosensDataJson: String?,
var mealDataJson: String,
var isMicroBolusAllowed: Boolean?,
var resultJson: String
) : TraceableDBEntry, DBEntryWithTime {
enum class Algorithm {
MA,
AMA,
SMB
}
}

View file

@ -0,0 +1,42 @@
package info.nightscout.androidaps.database.entities
import androidx.room.*
import info.nightscout.androidaps.database.TABLE_APS_RESULT_LINKS
import info.nightscout.androidaps.database.embedments.InterfaceIDs
import info.nightscout.androidaps.database.interfaces.TraceableDBEntry
@Entity(tableName = TABLE_APS_RESULT_LINKS,
foreignKeys = [ForeignKey(
entity = APSResult::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("apsResultId")), ForeignKey(
entity = Bolus::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("smbId")), ForeignKey(
entity = TemporaryBasal::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("tbrId")), ForeignKey(
entity = APSResultLink::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("referenceId"))],
indices = [Index("referenceId"), Index("apsResultId"),
Index("smbId"), Index("tbrId")])
data class APSResultLink(
@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 apsResultId: Long,
var smbId: Long? = null,
var tbrId: Long? = null
) : TraceableDBEntry {
override val foreignKeysValid: Boolean
get() = super.foreignKeysValid && apsResultId != 0L && smbId != 0L && tbrId != 0L
}

View file

@ -0,0 +1,39 @@
package info.nightscout.androidaps.database.entities
import androidx.room.*
import info.nightscout.androidaps.database.TABLE_BOLUSES
import info.nightscout.androidaps.database.embedments.InsulinConfiguration
import info.nightscout.androidaps.database.embedments.InterfaceIDs
import info.nightscout.androidaps.database.interfaces.DBEntryWithTime
import info.nightscout.androidaps.database.interfaces.TraceableDBEntry
import java.util.TimeZone
@Entity(tableName = TABLE_BOLUSES,
foreignKeys = [ForeignKey(
entity = Bolus::class,
parentColumns = ["id"],
childColumns = ["referenceId"])],
indices = [Index("referenceId"), Index("timestamp")])
data class Bolus(
@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,
override var timestamp: Long,
override var utcOffset: Long = TimeZone.getDefault().getOffset(timestamp).toLong(),
var amount: Double,
var type: Type,
var isBasalInsulin: Boolean,
@Embedded
var insulinConfiguration: InsulinConfiguration? = null
) : TraceableDBEntry, DBEntryWithTime {
enum class Type {
NORMAL,
SMB,
PRIMING
}
}

View file

@ -0,0 +1,53 @@
package info.nightscout.androidaps.database.entities
import androidx.room.*
import info.nightscout.androidaps.database.TABLE_BOLUS_CALCULATOR_RESULTS
import info.nightscout.androidaps.database.embedments.InterfaceIDs
import info.nightscout.androidaps.database.interfaces.DBEntryWithTime
import info.nightscout.androidaps.database.interfaces.TraceableDBEntry
import java.util.TimeZone
@Entity(tableName = TABLE_BOLUS_CALCULATOR_RESULTS,
foreignKeys = [ForeignKey(
entity = BolusCalculatorResult::class,
parentColumns = ["id"],
childColumns = ["referenceId"])],
indices = [Index("referenceId"), Index("timestamp")])
data class BolusCalculatorResult(
@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,
override var timestamp: Long,
override var utcOffset: Long = TimeZone.getDefault().getOffset(timestamp).toLong(),
var targetBGLow: Double,
var targetBGHigh: Double,
var isf: Double,
var ic: Double,
var bolusIOB: Double,
var wasBolusIOBUsed: Boolean,
var basalIOB: Double,
var wasBasalIOBUsed: Boolean,
var glucoseValue: Double,
var wasGlucoseUsed: Boolean,
var glucoseDifference: Double,
var glucoseInsulin: Double,
var glucoseTrend: Double,
var wasTrendUsed: Boolean,
var trendInsulin: Double,
var cob: Double,
var wasCOBUsed: Boolean,
var cobInsulin: Double,
var carbs: Double,
var wereCarbsUsed: Boolean,
var carbsInsulin: Double,
var otherCorrection: Double,
var wasSuperbolusUsed: Boolean,
var superbolusInsulin: Double,
var wasTempTargetUsed: Boolean,
var totalInsulin: Double
) : TraceableDBEntry, DBEntryWithTime

View file

@ -0,0 +1,29 @@
package info.nightscout.androidaps.database.entities
import androidx.room.*
import info.nightscout.androidaps.database.TABLE_CARBS
import info.nightscout.androidaps.database.embedments.InterfaceIDs
import info.nightscout.androidaps.database.interfaces.DBEntryWithTimeAndDuration
import info.nightscout.androidaps.database.interfaces.TraceableDBEntry
import java.util.TimeZone
@Entity(tableName = TABLE_CARBS,
foreignKeys = [ForeignKey(
entity = Carbs::class,
parentColumns = ["id"],
childColumns = ["referenceId"])],
indices = [Index("referenceId"), Index("timestamp")])
data class Carbs(
@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,
override var timestamp: Long,
override var utcOffset: Long = TimeZone.getDefault().getOffset(timestamp).toLong(),
override var duration: Long,
var amount: Double
) : TraceableDBEntry, DBEntryWithTimeAndDuration

View file

@ -0,0 +1,30 @@
package info.nightscout.androidaps.database.entities
import androidx.room.*
import info.nightscout.androidaps.database.data.Block
import info.nightscout.androidaps.database.TABLE_EFFECTIVE_PROFILE_SWITCHES
import info.nightscout.androidaps.database.embedments.InterfaceIDs
import info.nightscout.androidaps.database.interfaces.DBEntryWithTimeAndDuration
import info.nightscout.androidaps.database.interfaces.TraceableDBEntry
import java.util.TimeZone
@Entity(tableName = TABLE_EFFECTIVE_PROFILE_SWITCHES,
foreignKeys = [ForeignKey(
entity = EffectiveProfileSwitch::class,
parentColumns = ["id"],
childColumns = ["referenceId"])],
indices = [Index("referenceId"), Index("timestamp")])
data class EffectiveProfileSwitch(
@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,
override var timestamp: Long,
override var utcOffset: Long = TimeZone.getDefault().getOffset(timestamp).toLong(),
override var duration: Long,
var basalBlocks: List<Block>
) : TraceableDBEntry, DBEntryWithTimeAndDuration

View file

@ -0,0 +1,30 @@
package info.nightscout.androidaps.database.entities
import androidx.room.*
import info.nightscout.androidaps.database.TABLE_EXTENDED_BOLUSES
import info.nightscout.androidaps.database.embedments.InterfaceIDs
import info.nightscout.androidaps.database.interfaces.DBEntryWithTimeAndDuration
import info.nightscout.androidaps.database.interfaces.TraceableDBEntry
import java.util.TimeZone
@Entity(tableName = TABLE_EXTENDED_BOLUSES,
foreignKeys = [ForeignKey(
entity = ExtendedBolus::class,
parentColumns = ["id"],
childColumns = ["referenceId"])],
indices = [Index("referenceId"), Index("timestamp")])
data class ExtendedBolus(
@PrimaryKey(autoGenerate = true)
override var id: Long = 0,
override var version: Int = 0,
override var dateCreated: Long = -1,
override var isValid: Boolean = true,
override var referenceId: Long? = null,
@Embedded
override var interfaceIDs_backing: InterfaceIDs? = InterfaceIDs(),
override var timestamp: Long,
override var utcOffset: Long = TimeZone.getDefault().getOffset(timestamp).toLong(),
override var duration: Long,
var amount: Double,
var isEmulatingTempBasal: Boolean
) : TraceableDBEntry, DBEntryWithTimeAndDuration

View file

@ -0,0 +1,100 @@
package info.nightscout.androidaps.database.entities
import com.google.gson.annotations.SerializedName
import androidx.room.*
import info.nightscout.androidaps.database.TABLE_GLUCOSE_VALUES
import info.nightscout.androidaps.database.embedments.InterfaceIDs
import info.nightscout.androidaps.database.interfaces.DBEntryWithTime
import info.nightscout.androidaps.database.interfaces.TraceableDBEntry
import java.util.TimeZone
@Entity(tableName = TABLE_GLUCOSE_VALUES,
foreignKeys = [ForeignKey(
entity = GlucoseValue::class,
parentColumns = ["id"],
childColumns = ["referenceId"])],
indices = [Index("referenceId"), Index("timestamp")])
data class GlucoseValue(
@PrimaryKey(autoGenerate = true)
override var id: Long = 0,
override var version: Int = 0,
override var dateCreated: Long = -1,
override var isValid: Boolean = true,
override var referenceId: Long? = null,
@Embedded
override var interfaceIDs_backing: InterfaceIDs? = InterfaceIDs(),
override var timestamp: Long,
override var utcOffset: Long = TimeZone.getDefault().getOffset(timestamp).toLong(),
var raw: Double?,
var value: Double,
var trendArrow: TrendArrow,
var noise: Double?,
var sourceSensor: SourceSensor
) : TraceableDBEntry, DBEntryWithTime {
fun contentEqualsTo(other: GlucoseValue): Boolean =
timestamp == other.timestamp &&
utcOffset == other.utcOffset &&
raw == other.raw &&
value == other.value &&
trendArrow == other.trendArrow &&
noise == other.noise &&
sourceSensor == other.sourceSensor &&
isValid == other.isValid
fun isRecordDeleted(other: GlucoseValue): Boolean =
isValid && !other.isValid
enum class TrendArrow (val text:String, val symbol:String){
@SerializedName("NONE") NONE("NONE", "??"),
@SerializedName("TripleUp")TRIPLE_UP("TripleUp", "X"),
@SerializedName("DoubleUp")DOUBLE_UP("DoubleUp", "\u21c8"),
@SerializedName("SingleUp")SINGLE_UP("SingleUp", "\u2191"),
@SerializedName("FortyFiveUp")FORTY_FIVE_UP("FortyFiveUp", "\u2197"),
@SerializedName("Flat")FLAT("Flat", "\u2192"),
@SerializedName("FortyFiveDown")FORTY_FIVE_DOWN("FortyFiveDown", "\u2198"),
@SerializedName("SingleDown")SINGLE_DOWN("SingleDown", "\u2193"),
@SerializedName("DoubleDown")DOUBLE_DOWN("DoubleDown", "\u21ca"),
@SerializedName("TripleDown")TRIPLE_DOWN("TripleDown", "X")
;
companion object {
fun fromString(direction : String?) = values().firstOrNull {it.text == direction} ?: NONE
}
}
enum class SourceSensor(val text : String) {
@SerializedName("AndroidAPS-Dexcom") DEXCOM_NATIVE_UNKNOWN("AndroidAPS-Dexcom"),
@SerializedName("AndroidAPS-DexcomG6") DEXCOM_G6_NATIVE("AndroidAPS-DexcomG6"),
@SerializedName("AndroidAPS-DexcomG5") DEXCOM_G5_NATIVE("AndroidAPS-DexcomG5"),
@SerializedName("Bluetooth Wixel") DEXCOM_G4_WIXEL("Bluetooth Wixel"),
@SerializedName("xBridge Wixel") DEXCOM_G4_XBRIDGE("xBridge Wixel"),
@SerializedName("G4 Share Receiver") DEXCOM_G4_NATIVE("G4 Share Receiver"),
@SerializedName("Medtrum A6") MEDTRUM_A6("Medtrum A6"),
@SerializedName("Network G4") DEXCOM_G4_NET("Network G4"),
@SerializedName("Network G4 and xBridge") DEXCOM_G4_NET_XBRIDGE("Network G4 and xBridge"),
@SerializedName("Network G4 and Classic xDrip") DEXCOM_G4_NET_CLASSIC("Network G4 and Classic xDrip"),
@SerializedName("DexcomG5") DEXCOM_G5_XDRIP("DexcomG5"),
@SerializedName("G6 Native") DEXCOM_G6_NATIVE_XDRIP("G6 Native"),
@SerializedName("G5 Native") DEXCOM_G5_NATIVE_XDRIP("G5 Native"),
@SerializedName("Network libre") LIBRE_1_NET("Network libre"),
@SerializedName("BlueReader") LIBRE_1_BLUE("BlueReader"),
@SerializedName("Transmiter PL") LIBRE_1_PL("Transmiter PL"),
@SerializedName("Blucon") LIBRE_1_BLUCON("Blucon"),
@SerializedName("Tomato") LIBRE_1_TOMATO("Tomato"),
@SerializedName("Rfduino") LIBRE_1_RF("Rfduino"),
@SerializedName("LimiTTer") LIBRE_1_LIMITTER("LimiTTer"),
@SerializedName("Glimp") GLIMP("Glimp"),
@SerializedName("Libre2") LIBRE_2_NATIVE("Libre2"),
@SerializedName("Poctech") POCTECH_NATIVE("Poctech"),
@SerializedName("MM600Series") MM_600_SERIES("MM600Series"),
@SerializedName("Eversense") EVERSENSE("Eversense"),
@SerializedName("Random") RANDOM("Random"),
@SerializedName("Unknown") UNKNOWN("Unknown")
;
companion object {
fun fromString(source : String?) = values().firstOrNull {it.text == source} ?: UNKNOWN
}
}
}

View file

@ -0,0 +1,54 @@
package info.nightscout.androidaps.database.entities
import androidx.room.*
import info.nightscout.androidaps.database.TABLE_MEAL_LINKS
import info.nightscout.androidaps.database.embedments.InterfaceIDs
import info.nightscout.androidaps.database.interfaces.TraceableDBEntry
@Entity(tableName = TABLE_MEAL_LINKS,
foreignKeys = [ForeignKey(
entity = Bolus::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("bolusId")), ForeignKey(
entity = Carbs::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("carbsId")), ForeignKey(
entity = BolusCalculatorResult::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("bolusCalcResultId")), ForeignKey(
entity = TemporaryBasal::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("superbolusTempBasalId")), ForeignKey(
entity = TherapyEvent::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("noteId")), ForeignKey(
entity = MealLink::class,
parentColumns = ["id"],
childColumns = ["referenceId"])],
indices = [Index("referenceId"), Index("bolusId"),
Index("carbsId"), Index("bolusCalcResultId"),
Index("superbolusTempBasalId"), Index("noteId")])
data class MealLink(
@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 bolusId: Long? = null,
var carbsId: Long? = null,
var bolusCalcResultId: Long? = null,
var superbolusTempBasalId: Long? = null,
var noteId: Long? = null
) : TraceableDBEntry {
override val foreignKeysValid: Boolean
get() = super.foreignKeysValid && bolusId != 0L && carbsId != 0L &&
bolusCalcResultId != 0L && superbolusTempBasalId != 0L && noteId != 0L
}

View file

@ -0,0 +1,37 @@
package info.nightscout.androidaps.database.entities
import androidx.room.*
import info.nightscout.androidaps.database.TABLE_MULTIWAVE_BOLUS_LINKS
import info.nightscout.androidaps.database.embedments.InterfaceIDs
import info.nightscout.androidaps.database.interfaces.TraceableDBEntry
@Entity(tableName = TABLE_MULTIWAVE_BOLUS_LINKS,
foreignKeys = [ForeignKey(
entity = Bolus::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("bolusId")), ForeignKey(
entity = ExtendedBolus::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("extendedBolusId")), ForeignKey(
entity = MultiwaveBolusLink::class,
parentColumns = ["id"],
childColumns = ["referenceId"])],
indices = [Index("referenceId"), Index("bolusId"),
Index("extendedBolusId")])
data class MultiwaveBolusLink(
@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 bolusId: Long,
var extendedBolusId: Long
) : TraceableDBEntry {
override val foreignKeysValid: Boolean
get() = super.foreignKeysValid && bolusId != 0L && bolusId != 0L && extendedBolusId != 0L
}

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.database.entities
import androidx.room.Entity
import androidx.room.PrimaryKey
import info.nightscout.androidaps.database.TABLE_PREFERENCE_CHANGES
import info.nightscout.androidaps.database.interfaces.DBEntry
import info.nightscout.androidaps.database.interfaces.DBEntryWithTime
import java.util.TimeZone
@Entity(tableName = TABLE_PREFERENCE_CHANGES)
data class PreferenceChange(
@PrimaryKey(autoGenerate = true)
override var id: Long = 0L,
override var timestamp: Long,
override var utcOffset: Long = TimeZone.getDefault().getOffset(timestamp).toLong(),
var key: String,
var value: Any?
) : DBEntry, DBEntryWithTime

View file

@ -0,0 +1,46 @@
package info.nightscout.androidaps.database.entities
import androidx.room.*
import info.nightscout.androidaps.database.data.Block
import info.nightscout.androidaps.database.TABLE_PROFILE_SWITCHES
import info.nightscout.androidaps.database.data.TargetBlock
import info.nightscout.androidaps.database.embedments.InsulinConfiguration
import info.nightscout.androidaps.database.embedments.InterfaceIDs
import info.nightscout.androidaps.database.interfaces.DBEntryWithTimeAndDuration
import info.nightscout.androidaps.database.interfaces.TraceableDBEntry
import java.util.TimeZone
@Entity(tableName = TABLE_PROFILE_SWITCHES,
foreignKeys = [ForeignKey(
entity = ProfileSwitch::class,
parentColumns = ["id"],
childColumns = ["referenceId"])],
indices = [Index("referenceId"), Index("timestamp")])
data class ProfileSwitch(
@PrimaryKey(autoGenerate = true)
override var id: Long = 0,
override var version: Int = 0,
override var dateCreated: Long = -1,
override var isValid: Boolean = true,
override var referenceId: Long? = null,
@Embedded
override var interfaceIDs_backing: InterfaceIDs? = InterfaceIDs(),
override var timestamp: Long,
override var utcOffset: Long = TimeZone.getDefault().getOffset(timestamp).toLong(),
var profileName: String,
var glucoseUnit: GlucoseUnit,
var basalBlocks: List<Block>,
var isfBlocks: List<Block>,
var icBlocks: List<Block>,
var targetBlocks: List<TargetBlock>,
@Embedded
var insulinConfiguration: InsulinConfiguration,
var timeshift: Int,
var percentage: Int,
override var duration: Long
) : TraceableDBEntry, DBEntryWithTimeAndDuration {
enum class GlucoseUnit {
MGDL,
MMOL
}
}

View file

@ -0,0 +1,38 @@
package info.nightscout.androidaps.database.entities
import androidx.room.*
import info.nightscout.androidaps.database.TABLE_TEMPORARY_BASALS
import info.nightscout.androidaps.database.embedments.InterfaceIDs
import info.nightscout.androidaps.database.interfaces.DBEntryWithTimeAndDuration
import info.nightscout.androidaps.database.interfaces.TraceableDBEntry
import java.util.TimeZone
@Entity(tableName = TABLE_TEMPORARY_BASALS,
foreignKeys = [ForeignKey(
entity = TemporaryBasal::class,
parentColumns = ["id"],
childColumns = ["referenceId"])],
indices = [Index("referenceId"), Index("timestamp")])
data class TemporaryBasal(
@PrimaryKey(autoGenerate = true)
override var id: Long = 0,
override var version: Int = 0,
override var dateCreated: Long = -1,
override var isValid: Boolean = true,
override var referenceId: Long? = null,
@Embedded
override var interfaceIDs_backing: InterfaceIDs? = InterfaceIDs(),
override var timestamp: Long,
override var utcOffset: Long = TimeZone.getDefault().getOffset(timestamp).toLong(),
var type: Type,
var isAbsolute: Boolean,
var rate: Double,
override var duration: Long
) : TraceableDBEntry, DBEntryWithTimeAndDuration {
enum class Type {
NORMAL,
EMULATED_PUMP_SUSPEND,
PUMP_SUSPEND,
SUPERBOLUS
}
}

View file

@ -0,0 +1,68 @@
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 com.google.gson.annotations.SerializedName
import info.nightscout.androidaps.database.TABLE_TEMPORARY_TARGETS
import info.nightscout.androidaps.database.embedments.InterfaceIDs
import info.nightscout.androidaps.database.interfaces.DBEntryWithTimeAndDuration
import info.nightscout.androidaps.database.interfaces.TraceableDBEntry
import java.util.*
@Entity(tableName = TABLE_TEMPORARY_TARGETS,
foreignKeys = [ForeignKey(
entity = TemporaryTarget::class,
parentColumns = ["id"],
childColumns = ["referenceId"])],
indices = [Index("referenceId"), Index("timestamp")])
data class TemporaryTarget(
@PrimaryKey(autoGenerate = true)
override var id: Long = 0,
override var version: Int = 0,
override var dateCreated: Long = -1,
override var isValid: Boolean = true,
override var referenceId: Long? = null,
@Embedded
override var interfaceIDs_backing: InterfaceIDs? = InterfaceIDs(),
override var timestamp: Long,
override var utcOffset: Long = TimeZone.getDefault().getOffset(timestamp).toLong(),
var reason: Reason,
var highTarget: Double, // in mgdl
var lowTarget: Double, // in mgdl
override var duration: Long // in millis
) : TraceableDBEntry, DBEntryWithTimeAndDuration {
fun contentEqualsTo(other: TemporaryTarget): Boolean =
timestamp == other.timestamp &&
utcOffset == other.utcOffset &&
reason == other.reason &&
highTarget == other.highTarget &&
lowTarget == other.lowTarget &&
duration == other.duration &&
isValid == other.isValid
fun isRecordDeleted(other: TemporaryTarget): Boolean =
isValid && !other.isValid
enum class Reason(val text: String) {
@SerializedName("Custom")
CUSTOM("Custom"),
@SerializedName("Hypo")
HYPOGLYCEMIA("Hypo"),
@SerializedName("Activity")
ACTIVITY("Activity"),
@SerializedName("Eating Soon")
EATING_SOON("Eating Soon"),
@SerializedName("Automation")
AUTOMATION("Automation")
;
companion object {
fun fromString(direction: String?) = values().firstOrNull { it.text == direction }
?: CUSTOM
}
}
}

View file

@ -0,0 +1,63 @@
package info.nightscout.androidaps.database.entities
import androidx.room.*
import info.nightscout.androidaps.database.TABLE_THERAPY_EVENTS
import info.nightscout.androidaps.database.embedments.InterfaceIDs
import info.nightscout.androidaps.database.interfaces.DBEntryWithTimeAndDuration
import info.nightscout.androidaps.database.interfaces.TraceableDBEntry
import java.util.TimeZone
@Entity(tableName = TABLE_THERAPY_EVENTS,
foreignKeys = [ForeignKey(
entity = TherapyEvent::class,
parentColumns = ["id"],
childColumns = ["referenceId"])],
indices = [Index("referenceId"), Index("timestamp")])
data class TherapyEvent(
@PrimaryKey(autoGenerate = true)
override var id: Long = 0,
override var version: Int = 0,
override var dateCreated: Long = -1,
override var isValid: Boolean = true,
override var referenceId: Long? = null,
@Embedded
override var interfaceIDs_backing: InterfaceIDs? = InterfaceIDs(),
override var timestamp: Long,
override var utcOffset: Long = TimeZone.getDefault().getOffset(timestamp).toLong(),
override var duration: Long = 0,
var type: Type,
var note: String? = null,
var amount: Double? = null
) : TraceableDBEntry, DBEntryWithTimeAndDuration {
enum class Type {
CANNULA_CHANGED,
TUBE_CHANGED,
RESERVOIR_CHANGED,
BATTERY_CHANGED,
LEAKING_INFUSION_SET,
SENSOR_INSERTED,
SENSOR_STARTED,
SENSOR_STOPPED,
FINGER_STICK_BG_VALUE,
ACTIVITY,
FALLING_ASLEEP,
WAKING_UP,
SICKNESS,
STRESS,
PRE_PERIOD,
ALCOHOL,
CORTISON,
FEELING_LOW,
FEELING_HIGH,
ANNOUNCEMENT,
QUESTION,
NOTE,
APS_OFFLINE,
BATTERY_EMPTY,
RESERVOIR_EMPTY,
OCCLUSION,
PUMP_STOPPED,
PUMP_STARTED,
PUMP_PAUSED
}
}

View file

@ -0,0 +1,30 @@
package info.nightscout.androidaps.database.entities
import androidx.room.*
import info.nightscout.androidaps.database.TABLE_TOTAL_DAILY_DOSES
import info.nightscout.androidaps.database.embedments.InterfaceIDs
import info.nightscout.androidaps.database.interfaces.DBEntryWithTime
import info.nightscout.androidaps.database.interfaces.TraceableDBEntry
import java.util.TimeZone
@Entity(tableName = TABLE_TOTAL_DAILY_DOSES,
foreignKeys = [ForeignKey(
entity = TotalDailyDose::class,
parentColumns = ["id"],
childColumns = ["referenceId"])],
indices = [Index("referenceId"), Index("timestamp")])
data class TotalDailyDose(
@PrimaryKey(autoGenerate = true)
override var id: Long = 0,
override var version: Int = 0,
override var dateCreated: Long = -1,
override var isValid: Boolean = true,
override var referenceId: Long? = null,
@Embedded
override var interfaceIDs_backing: InterfaceIDs? = InterfaceIDs(),
override var timestamp: Long,
override var utcOffset: Long = TimeZone.getDefault().getOffset(timestamp).toLong(),
var basalAmount: Double?,
var bolusAmount: Double?,
var totalAmount: Double?
) : TraceableDBEntry, DBEntryWithTime

View file

@ -0,0 +1,20 @@
package info.nightscout.androidaps.database.entities
import androidx.room.Entity
import androidx.room.PrimaryKey
import info.nightscout.androidaps.database.TABLE_VERSION_CHANGES
import info.nightscout.androidaps.database.interfaces.DBEntry
import info.nightscout.androidaps.database.interfaces.DBEntryWithTime
import java.util.TimeZone
@Entity(tableName = TABLE_VERSION_CHANGES)
data class VersionChange(
@PrimaryKey(autoGenerate = true)
override var id: Long = 0L,
override var timestamp: Long,
override var utcOffset: Long = TimeZone.getDefault().getOffset(timestamp).toLong(),
var versionCode: Int,
var versionName: String,
var gitRemote: String?,
var commitHash: String?
) : DBEntry, DBEntryWithTime

View file

@ -0,0 +1,5 @@
package info.nightscout.androidaps.database.interfaces
interface DBEntry {
var id: Long
}

View file

@ -0,0 +1,7 @@
package info.nightscout.androidaps.database.interfaces
interface DBEntryWithDuration {
var duration: Long
val durationUnknown get() = duration == Long.MAX_VALUE
}

View file

@ -0,0 +1,6 @@
package info.nightscout.androidaps.database.interfaces
interface DBEntryWithTime {
var timestamp: Long
var utcOffset: Long
}

View file

@ -0,0 +1,14 @@
package info.nightscout.androidaps.database.interfaces
import kotlin.math.min
interface DBEntryWithTimeAndDuration : DBEntryWithTime, DBEntryWithDuration
var DBEntryWithTimeAndDuration.end
get() = timestamp + duration
set(value) {
duration = value - timestamp
}
@JvmOverloads
fun DBEntryWithTimeAndDuration.getRemainingDuration(current: Long = System.currentTimeMillis()) = min(0L, end - current)

View file

@ -0,0 +1,28 @@
package info.nightscout.androidaps.database.interfaces
import info.nightscout.androidaps.database.embedments.InterfaceIDs
interface TraceableDBEntry: DBEntry {
var version: Int
var dateCreated: Long
var isValid: Boolean
var referenceId: Long?
var interfaceIDs_backing: InterfaceIDs?
val historic: Boolean get() = referenceId != null
val foreignKeysValid: Boolean get() = referenceId != 0L
var interfaceIDs: InterfaceIDs
get() {
var value = this.interfaceIDs_backing
if (value == null) {
value = InterfaceIDs()
interfaceIDs_backing = value
}
return value
}
set(value) {
interfaceIDs_backing = value
}
}

View file

@ -0,0 +1,16 @@
package info.nightscout.androidaps.database.transactions
import info.nightscout.androidaps.database.entities.TemporaryTarget
import info.nightscout.androidaps.database.interfaces.end
class CancelCurrentTemporaryTargetIfAnyTransaction(
val timestamp: Long
) : Transaction<Unit>() {
override fun run() {
val current = database.temporaryTargetDao.getTemporaryTargetActiveAt(timestamp)
if (current != null) {
current.end = timestamp
database.temporaryTargetDao.updateExistingEntry(current)
}
}
}

View file

@ -0,0 +1,74 @@
package info.nightscout.androidaps.database.transactions
import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.database.entities.TherapyEvent
/**
* Inserts data from a CGM source into the database
*/
class CgmSourceTransaction(
private val glucoseValues: List<TransactionGlucoseValue>,
private val calibrations: List<Calibration>,
private val sensorInsertionTime: Long?
) : Transaction<List<GlucoseValue>>() {
override fun run(): List<GlucoseValue> {
val insertedGlucoseValues = mutableListOf<GlucoseValue>()
glucoseValues.forEach {
val current = database.glucoseValueDao.findByTimestampAndSensor(it.timestamp, it.sourceSensor)
val glucoseValue = GlucoseValue(
timestamp = it.timestamp,
raw = it.raw,
value = it.value,
noise = it.noise,
trendArrow = it.trendArrow,
sourceSensor = it.sourceSensor
)
glucoseValue.interfaceIDs.nightscoutId = it.nightscoutId
when {
current == null -> {
database.glucoseValueDao.insertNewEntry(glucoseValue)
insertedGlucoseValues.add(glucoseValue)
}
!current.contentEqualsTo(glucoseValue) -> {
glucoseValue.id = current.id
database.glucoseValueDao.updateExistingEntry(glucoseValue)
}
}
}
calibrations.forEach {
if (database.therapyEventDao.findByTimestamp(TherapyEvent.Type.FINGER_STICK_BG_VALUE, it.timestamp) == null) {
database.therapyEventDao.insertNewEntry(TherapyEvent(
timestamp = it.timestamp,
type = TherapyEvent.Type.FINGER_STICK_BG_VALUE,
amount = it.value
))
}
}
sensorInsertionTime?.let {
if (database.therapyEventDao.findByTimestamp(TherapyEvent.Type.SENSOR_INSERTED, it) == null) {
database.therapyEventDao.insertNewEntry(TherapyEvent(
timestamp = it,
type = TherapyEvent.Type.SENSOR_INSERTED
))
}
}
return insertedGlucoseValues
}
data class TransactionGlucoseValue(
val timestamp: Long,
val value: Double,
val raw: Double?,
val noise: Double?,
val trendArrow: GlucoseValue.TrendArrow,
val nightscoutId: String? = null,
val sourceSensor: GlucoseValue.SourceSensor
)
data class Calibration(
val timestamp: Long,
val value: Double
)
}

View file

@ -0,0 +1,13 @@
package info.nightscout.androidaps.database.transactions
import info.nightscout.androidaps.database.entities.GlucoseValue
/**
* Creates the GlucoseValue
*/
class InsertGlucoseValueTransaction(val glucoseValue: GlucoseValue) : Transaction<Unit>() {
override fun run() {
database.glucoseValueDao.insert(glucoseValue)
}
}

View file

@ -0,0 +1,21 @@
package info.nightscout.androidaps.database.transactions
import info.nightscout.androidaps.database.entities.TemporaryTarget
import info.nightscout.androidaps.database.interfaces.end
class InsertTemporaryTargetAndCancelCurrentTransaction(
private val temporaryTarget: TemporaryTarget
) : Transaction<Unit>() {
constructor(timestamp: Long, duration: Long, reason: TemporaryTarget.Reason, lowTarget: Double, highTarget: Double) :
this(TemporaryTarget(timestamp = timestamp, reason = reason, lowTarget = lowTarget, highTarget = highTarget, duration = duration))
override fun run() {
val current = database.temporaryTargetDao.getTemporaryTargetActiveAt(temporaryTarget.timestamp)
if (current != null) {
current.end = temporaryTarget.timestamp
database.temporaryTargetDao.updateExistingEntry(current)
}
database.temporaryTargetDao.insertNewEntry(temporaryTarget)
}
}

View file

@ -0,0 +1,15 @@
package info.nightscout.androidaps.database.transactions
import info.nightscout.androidaps.database.transactions.Transaction
/**
* Invalidates the GlucoseValue with the specified id
*/
class InvalidateGlucoseValueTransaction(val id: Long) : Transaction<Unit>() {
override fun run() {
val glucoseValue = database.glucoseValueDao.findById(id)
?: throw IllegalArgumentException("There is no such GlucoseValue with the specified ID.")
glucoseValue.isValid = false
database.glucoseValueDao.updateExistingEntry(glucoseValue)
}
}

View file

@ -0,0 +1,10 @@
package info.nightscout.androidaps.database.transactions
class InvalidateTemporaryTargetTransaction(val id: Long) : Transaction<Unit>() {
override fun run() {
val temporaryTarget = database.temporaryTargetDao.findById(id)
?: throw IllegalArgumentException("There is no such TemporaryTarget with the specified ID.")
temporaryTarget.isValid = false
database.temporaryTargetDao.updateExistingEntry(temporaryTarget)
}
}

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.database.transactions
import info.nightscout.androidaps.database.DelegatedAppDatabase
/**
* Base class for database transactions
* @param T The return type of the Transaction
*/
abstract class Transaction<T> {
/**
* Executes the Transaction
*/
internal abstract fun run(): T
internal lateinit var database: DelegatedAppDatabase
}

View file

@ -0,0 +1,13 @@
package info.nightscout.androidaps.database.transactions
import info.nightscout.androidaps.database.entities.GlucoseValue
/**
* Updates the GlucoseValue
*/
class UpdateGlucoseValueTransaction(val glucoseValue: GlucoseValue) : Transaction<Unit>() {
override fun run() {
database.glucoseValueDao.updateExistingEntry(glucoseValue)
}
}

View file

@ -0,0 +1,13 @@
package info.nightscout.androidaps.database.transactions
import info.nightscout.androidaps.database.entities.TemporaryTarget
/**
* Updates the TemporaryTarget
*/
class UpdateTemporaryTargetTransaction(private val temporaryTarget: TemporaryTarget) : Transaction<Unit>() {
override fun run() {
database.temporaryTargetDao.updateExistingEntry(temporaryTarget)
}
}

View file

@ -0,0 +1,30 @@
package info.nightscout.androidaps.database.transactions
import info.nightscout.androidaps.database.entities.VersionChange
import java.util.*
class VersionChangeTransaction(
private val versionName: String,
private val versionCode: Int,
private val gitRemote: String?,
private val commitHash: String?) : Transaction<Unit>() {
override fun run() {
val current = database.versionChangeDao.getMostRecentVersionChange()
if (current == null
|| current.versionName != versionName
|| current.versionCode != versionCode
|| current.gitRemote != gitRemote
|| current.commitHash != commitHash) {
val currentTime = System.currentTimeMillis()
database.versionChangeDao.insert(VersionChange(
timestamp = System.currentTimeMillis(),
versionCode = versionCode,
versionName = versionName,
gitRemote = gitRemote,
commitHash = commitHash
))
}
}
}

View file

@ -0,0 +1,18 @@
package info.nightscout.database
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}

View file

@ -1,2 +1,3 @@
include ':database'
include ':app', ':wear', ':core', ':dana', ':danar', ':danars', ':rileylink', ':medtronic', ':omnipod'