Merge remote-tracking branch 'Nightscout/dev' into Autotune/TuneWeekDaysClean

This commit is contained in:
Philoul 2022-12-14 08:48:36 +01:00
commit 8ba4aa3642
768 changed files with 8498 additions and 6742 deletions

View file

@ -111,7 +111,7 @@ android {
defaultConfig {
multiDexEnabled true
versionCode 1500
version "3.1.0.3-dev-d"
version "3.1.0.3-dev-e"
buildConfigField "String", "VERSION", '"' + version + '"'
buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"'
buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"'
@ -198,10 +198,12 @@ dependencies {
implementation project(':plugins:aps')
implementation project(':plugins:automation')
implementation project(':plugins:configuration')
implementation project(':plugins:constraints')
implementation project(':plugins:insulin')
implementation project(':plugins:main')
implementation project(':plugins:openhumans')
implementation project(':plugins:sensitivity')
implementation project(':plugins:support')
implementation project(':plugins:source')
implementation project(':plugins:sync')
implementation project(':implementation')
implementation project(':database:entities')
@ -217,10 +219,11 @@ dependencies {
implementation project(':pump:medtronic')
implementation project(':pump:pump-common')
implementation project(':pump:pump-core')
implementation project(':pump:rileylink')
implementation project(':pump:omnipod-common')
implementation project(':pump:omnipod-eros')
implementation project(':pump:omnipod-dash')
implementation project(':pump:rileylink')
implementation project(':pump:virtual')
implementation project(':workflow')
implementation fileTree(include: ['*.jar'], dir: 'libs')

View file

@ -132,7 +132,7 @@
</provider>
<service
android:name="info.nightscout.core.services.AlarmSoundService"
android:name="info.nightscout.ui.services.AlarmSoundService"
android:enabled="true"
android:exported="true" />
<uses-library

View file

@ -151,10 +151,6 @@ class MainActivity : DaggerAppCompatActivityWithResult() {
.toObservable(EventPreferenceChange::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ processPreferenceChange(it) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventInitializationChanged::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ passwordResetCheck(this) }, fabricPrivacy::logException)
if (startWizard() && !isRunningRealPumpTest()) {
protectionCheck.queryProtection(this, ProtectionCheck.Protection.PREFERENCES, {
startActivity(Intent(this, SetupWizardActivity::class.java))
@ -168,6 +164,7 @@ class MainActivity : DaggerAppCompatActivityWithResult() {
androidPermission.notifyForSystemWindowPermissions(this)
androidPermission.notifyForBtConnectPermission(this)
}
passwordResetCheck(this)
}
private fun checkPluginPreferences(viewPager: ViewPager2) {
@ -352,6 +349,7 @@ class MainActivity : DaggerAppCompatActivityWithResult() {
message += "Flavor: ${BuildConfig.FLAVOR}${BuildConfig.BUILD_TYPE}\n"
message += "${rh.gs(info.nightscout.configuration.R.string.configbuilder_nightscoutversion_label)} ${nsSettingsStatus.getVersion()}"
if (config.isEngineeringMode()) message += "\n${rh.gs(info.nightscout.configuration.R.string.engineering_mode_enabled)}"
if (config.isUnfinishedMode()) message += "\nUnfinished mode enabled"
if (!fabricPrivacy.fabricEnabled()) message += "\n${rh.gs(R.string.fabric_upload_disabled)}"
message += rh.gs(info.nightscout.pump.combo.R.string.about_link_urls)
val messageSpanned = SpannableString(message)

View file

@ -28,6 +28,7 @@ import info.nightscout.automation.AutomationPlugin
import info.nightscout.configuration.maintenance.MaintenancePlugin
import info.nightscout.core.ui.dialogs.OKDialog
import info.nightscout.implementation.plugin.PluginStore
import info.nightscout.insulin.InsulinOrefFreePeakPlugin
import info.nightscout.interfaces.Config
import info.nightscout.interfaces.nsclient.NSSettingsStatus
import info.nightscout.interfaces.plugin.PluginBase
@ -47,22 +48,13 @@ import info.nightscout.plugins.general.autotune.AutotunePlugin
import info.nightscout.plugins.general.smsCommunicator.SmsCommunicatorPlugin
import info.nightscout.plugins.general.wear.WearPlugin
import info.nightscout.plugins.general.xdripStatusline.StatusLinePlugin
import info.nightscout.plugins.insulin.InsulinOrefFreePeakPlugin
import info.nightscout.plugins.pump.virtual.VirtualPumpPlugin
import info.nightscout.plugins.source.AidexPlugin
import info.nightscout.plugins.source.DexcomPlugin
import info.nightscout.plugins.source.EversensePlugin
import info.nightscout.plugins.source.GlimpPlugin
import info.nightscout.plugins.source.GlunovoPlugin
import info.nightscout.plugins.source.IntelligoPlugin
import info.nightscout.plugins.source.PoctechPlugin
import info.nightscout.plugins.source.TomatoPlugin
import info.nightscout.plugins.sync.nsclient.NSClientPlugin
import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin
import info.nightscout.plugins.sync.tidepool.TidepoolPlugin
import info.nightscout.pump.combo.ComboPlugin
import info.nightscout.pump.combov2.ComboV2Plugin
import info.nightscout.pump.diaconn.DiaconnG8Plugin
import info.nightscout.pump.virtual.VirtualPumpPlugin
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventPreferenceChange
import info.nightscout.rx.events.EventRebuildTabs
@ -72,6 +64,14 @@ import info.nightscout.sensitivity.SensitivityWeightedAveragePlugin
import info.nightscout.shared.SafeParse
import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.source.AidexPlugin
import info.nightscout.source.DexcomPlugin
import info.nightscout.source.EversensePlugin
import info.nightscout.source.GlimpPlugin
import info.nightscout.source.GlunovoPlugin
import info.nightscout.source.IntelligoPlugin
import info.nightscout.source.PoctechPlugin
import info.nightscout.source.TomatoPlugin
import javax.inject.Inject
class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeListener {

View file

@ -20,9 +20,10 @@ import info.nightscout.core.di.CoreModule
import info.nightscout.core.validators.di.ValidatorsModule
import info.nightscout.database.impl.DatabaseModule
import info.nightscout.implementation.di.ImplementationModule
import info.nightscout.insulin.di.InsulinModule
import info.nightscout.plugins.aps.di.ApsModule
import info.nightscout.plugins.constraints.di.PluginsConstraintsModule
import info.nightscout.plugins.di.PluginsModule
import info.nightscout.plugins.support.di.PluginsSupportModule
import info.nightscout.plugins.sync.di.SyncModule
import info.nightscout.pump.combo.di.ComboModule
import info.nightscout.pump.combov2.di.ComboV2Module
@ -31,9 +32,11 @@ import info.nightscout.pump.dana.di.DanaHistoryModule
import info.nightscout.pump.dana.di.DanaModule
import info.nightscout.pump.danars.di.DanaRSModule
import info.nightscout.pump.diaconn.di.DiaconnG8Module
import info.nightscout.pump.virtual.di.VirtualPumpModule
import info.nightscout.rx.di.RxModule
import info.nightscout.shared.di.SharedModule
import info.nightscout.shared.impl.di.SharedImplModule
import info.nightscout.source.di.SourceModule
import info.nightscout.ui.di.UiModule
import info.nightscout.workflow.di.WorkflowModule
import javax.inject.Singleton
@ -55,6 +58,7 @@ import javax.inject.Singleton
CoreModule::class,
DatabaseModule::class,
ImplementationModule::class,
InsulinModule::class,
OpenHumansModule::class,
PluginsModule::class,
RxModule::class,
@ -62,7 +66,8 @@ import javax.inject.Singleton
SharedImplModule::class,
UiModule::class,
ValidatorsModule::class,
PluginsSupportModule::class,
PluginsConstraintsModule::class,
SourceModule::class,
SyncModule::class,
WorkflowModule::class,
@ -81,7 +86,8 @@ import javax.inject.Singleton
OmnipodDashModule::class,
OmnipodErosModule::class,
PumpCommonModule::class,
RileyLinkModule::class
RileyLinkModule::class,
VirtualPumpModule::class
]
)
interface AppComponent : AndroidInjector<MainApp> {

View file

@ -7,13 +7,15 @@ import dagger.Module
import dagger.Provides
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.implementations.UiInteractionImpl
import info.nightscout.androidaps.implementations.ConfigImpl
import info.nightscout.androidaps.implementations.InstantiatorImpl
import info.nightscout.androidaps.implementations.UiInteractionImpl
import info.nightscout.androidaps.workflow.CalculationWorkflowImpl
import info.nightscout.androidaps.workflow.WorkerClassesImpl
import info.nightscout.core.workflow.CalculationWorkflow
import info.nightscout.interfaces.Config
import info.nightscout.interfaces.plugin.PluginBase
import info.nightscout.interfaces.profile.Instantiator
import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.interfaces.workflow.WorkerClasses
@ -53,6 +55,8 @@ open class AppModule {
@Binds fun bindActivityNames(activityNames: UiInteractionImpl): UiInteraction
@Binds fun bindWorkerClasses(workerClassesImpl: WorkerClassesImpl): WorkerClasses
@Binds fun bindCalculationWorkflow(calculationWorkflow: CalculationWorkflowImpl): CalculationWorkflow
@Binds fun bindInstantiator(instantiatorImpl: InstantiatorImpl): Instantiator
}
}

View file

@ -8,7 +8,6 @@ import info.nightscout.androidaps.danaRKorean.DanaRKoreanPlugin
import info.nightscout.androidaps.danaRv2.DanaRv2Plugin
import info.nightscout.androidaps.danar.DanaRPlugin
import info.nightscout.androidaps.plugin.general.openhumans.OpenHumansUploaderPlugin
import info.nightscout.plugins.general.persistentNotification.PersistentNotificationPlugin
import info.nightscout.androidaps.plugins.pump.eopatch.EopatchPumpPlugin
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin
@ -17,6 +16,10 @@ import info.nightscout.androidaps.plugins.pump.omnipod.eros.OmnipodErosPumpPlugi
import info.nightscout.automation.AutomationPlugin
import info.nightscout.configuration.configBuilder.ConfigBuilderPlugin
import info.nightscout.configuration.maintenance.MaintenancePlugin
import info.nightscout.insulin.InsulinLyumjevPlugin
import info.nightscout.insulin.InsulinOrefFreePeakPlugin
import info.nightscout.insulin.InsulinOrefRapidActingPlugin
import info.nightscout.insulin.InsulinOrefUltraRapidActingPlugin
import info.nightscout.interfaces.plugin.PluginBase
import info.nightscout.plugins.aps.loop.LoopPlugin
import info.nightscout.plugins.aps.openAPSAMA.OpenAPSAMAPlugin
@ -31,37 +34,34 @@ import info.nightscout.plugins.general.autotune.AutotunePlugin
import info.nightscout.plugins.general.dataBroadcaster.DataBroadcastPlugin
import info.nightscout.plugins.general.food.FoodPlugin
import info.nightscout.plugins.general.overview.OverviewPlugin
import info.nightscout.plugins.general.persistentNotification.PersistentNotificationPlugin
import info.nightscout.plugins.general.smsCommunicator.SmsCommunicatorPlugin
import info.nightscout.plugins.general.themes.ThemeSwitcherPlugin
import info.nightscout.plugins.general.wear.WearPlugin
import info.nightscout.plugins.general.xdripStatusline.StatusLinePlugin
import info.nightscout.plugins.insulin.InsulinLyumjevPlugin
import info.nightscout.plugins.insulin.InsulinOrefFreePeakPlugin
import info.nightscout.plugins.insulin.InsulinOrefRapidActingPlugin
import info.nightscout.plugins.insulin.InsulinOrefUltraRapidActingPlugin
import info.nightscout.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.plugins.profile.ProfilePlugin
import info.nightscout.plugins.pump.virtual.VirtualPumpPlugin
import info.nightscout.plugins.source.AidexPlugin
import info.nightscout.plugins.source.DexcomPlugin
import info.nightscout.plugins.source.GlimpPlugin
import info.nightscout.plugins.source.GlunovoPlugin
import info.nightscout.plugins.source.IntelligoPlugin
import info.nightscout.plugins.source.MM640gPlugin
import info.nightscout.plugins.source.NSClientSourcePlugin
import info.nightscout.plugins.source.PoctechPlugin
import info.nightscout.plugins.source.RandomBgPlugin
import info.nightscout.plugins.source.TomatoPlugin
import info.nightscout.plugins.source.XdripPlugin
import info.nightscout.plugins.sync.nsclient.NSClientPlugin
import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin
import info.nightscout.plugins.sync.tidepool.TidepoolPlugin
import info.nightscout.pump.combo.ComboPlugin
import info.nightscout.pump.combov2.ComboV2Plugin
import info.nightscout.pump.diaconn.DiaconnG8Plugin
import info.nightscout.pump.virtual.VirtualPumpPlugin
import info.nightscout.sensitivity.SensitivityAAPSPlugin
import info.nightscout.sensitivity.SensitivityOref1Plugin
import info.nightscout.sensitivity.SensitivityWeightedAveragePlugin
import info.nightscout.source.AidexPlugin
import info.nightscout.source.DexcomPlugin
import info.nightscout.source.GlimpPlugin
import info.nightscout.source.GlunovoPlugin
import info.nightscout.source.IntelligoPlugin
import info.nightscout.source.MM640gPlugin
import info.nightscout.source.NSClientSourcePlugin
import info.nightscout.source.PoctechPlugin
import info.nightscout.source.RandomBgPlugin
import info.nightscout.source.TomatoPlugin
import info.nightscout.source.XdripPlugin
import javax.inject.Qualifier
@Suppress("unused")

View file

@ -0,0 +1,28 @@
package info.nightscout.androidaps.implementations
import dagger.Reusable
import dagger.android.HasAndroidInjector
import info.nightscout.implementation.profile.ProfileStoreObject
import info.nightscout.interfaces.aps.APSResult
import info.nightscout.interfaces.aps.AutosensData
import info.nightscout.interfaces.profile.Instantiator
import info.nightscout.interfaces.profile.ProfileStore
import info.nightscout.plugins.aps.APSResultObject
import info.nightscout.plugins.iob.iobCobCalculator.data.AutosensDataObject
import info.nightscout.shared.utils.DateUtil
import org.json.JSONObject
import javax.inject.Inject
@Reusable
class InstantiatorImpl @Inject constructor(
private val injector: HasAndroidInjector,
private val dateUtil: DateUtil
) : Instantiator {
override fun provideProfileStore(jsonObject: JSONObject): ProfileStore =
ProfileStoreObject(injector, jsonObject, dateUtil)
override fun provideAPSResultObject(): APSResult = APSResultObject(injector)
override fun provideAutosensDataObject(): AutosensData = AutosensDataObject(injector)
}

View file

@ -14,8 +14,8 @@ import info.nightscout.androidaps.activities.MyPreferenceFragment
import info.nightscout.androidaps.activities.PreferencesActivity
import info.nightscout.configuration.activities.SingleFragmentActivity
import info.nightscout.core.events.EventNewNotification
import info.nightscout.core.services.AlarmSoundService
import info.nightscout.core.services.AlarmSoundServiceHelper
import info.nightscout.ui.services.AlarmSoundService
import info.nightscout.ui.services.AlarmSoundServiceHelper
import info.nightscout.core.ui.toast.ToastUtils
import info.nightscout.interfaces.notifications.Notification
import info.nightscout.interfaces.nsclient.NSAlarm
@ -39,6 +39,7 @@ import info.nightscout.ui.dialogs.TempBasalDialog
import info.nightscout.ui.dialogs.TempTargetDialog
import info.nightscout.ui.dialogs.TreatmentDialog
import info.nightscout.ui.dialogs.WizardDialog
import info.nightscout.ui.widget.Widget
import javax.inject.Inject
class UiInteractionImpl @Inject constructor(
@ -67,6 +68,10 @@ class UiInteractionImpl @Inject constructor(
context.startActivity(i)
}
override fun updateWidget(context: Context) {
Widget.updateWidget(context)
}
override fun runWizardDialog(fragmentManager: FragmentManager, carbs: Int?, name: String?) {
WizardDialog().also { dialog ->
dialog.arguments = Bundle().also { bundle ->

View file

@ -12,17 +12,17 @@ import info.nightscout.core.utils.extensions.copyString
import info.nightscout.core.utils.receivers.DataWorkerStorage
import info.nightscout.interfaces.receivers.Intents
import info.nightscout.plugins.general.smsCommunicator.SmsCommunicatorPlugin
import info.nightscout.plugins.source.AidexPlugin
import info.nightscout.plugins.source.DexcomPlugin
import info.nightscout.plugins.source.EversensePlugin
import info.nightscout.plugins.source.GlimpPlugin
import info.nightscout.plugins.source.MM640gPlugin
import info.nightscout.plugins.source.PoctechPlugin
import info.nightscout.plugins.source.TomatoPlugin
import info.nightscout.plugins.source.XdripPlugin
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.BundleLogger
import info.nightscout.rx.logging.LTag
import info.nightscout.source.AidexPlugin
import info.nightscout.source.DexcomPlugin
import info.nightscout.source.EversensePlugin
import info.nightscout.source.GlimpPlugin
import info.nightscout.source.MM640gPlugin
import info.nightscout.source.PoctechPlugin
import info.nightscout.source.TomatoPlugin
import info.nightscout.source.XdripPlugin
import javax.inject.Inject
open class DataReceiver : DaggerBroadcastReceiver() {

View file

@ -3,7 +3,7 @@ package info.nightscout.androidaps.workflow
import info.nightscout.interfaces.workflow.WorkerClasses
import info.nightscout.plugins.general.food.FoodPlugin
import info.nightscout.plugins.profile.ProfilePlugin
import info.nightscout.plugins.source.NSClientSourcePlugin
import info.nightscout.source.NSClientSourcePlugin
import javax.inject.Inject
class WorkerClassesImpl @Inject constructor(): WorkerClasses{

View file

@ -46,7 +46,6 @@
<string name="authorizationfailed">Удостоверяването неуспешно</string>
<string name="copytolocalprofile_invalid">Създаването на профила невъзможно. Профилът е невалиден.</string>
<string name="cta_dont_kill_my_app_info">Не убивай приложението?</string>
<string name="time_to_eat">Време за ядене!\nИзпълнете болус съветника и направете изчисления отново.</string>
<string name="fabric_upload_disabled">Качването на данни за проблеми е забранено!(Fabric)</string>
<string name="clear_filter">Премахни филтъра</string>
<string name="cannula">Канюла</string>

View file

@ -44,7 +44,6 @@
<string name="authorizationfailed">L\'autorització ha fallat</string>
<string name="copytolocalprofile_invalid">No s\'ha pogut crear el perfil local. Perfil no vàlid.</string>
<string name="cta_dont_kill_my_app_info">No matar la meva app?</string>
<string name="time_to_eat">Hora de menjar!\nExecuteu l\'assistent de bolus i torneu a fer els càlculs.</string>
<string name="fabric_upload_disabled">Enviament de logs d\'error desactivat!</string>
<string name="clear_filter">Netejar filtres</string>
<string name="cannula">Cànula</string>

View file

@ -46,7 +46,6 @@
<string name="authorizationfailed">Autorizace selhala</string>
<string name="copytolocalprofile_invalid">Nelze vytvořit profil. Profil je neplatný.</string>
<string name="cta_dont_kill_my_app_info">Nezabíjet mou aplikaci?</string>
<string name="time_to_eat">Čas k jídlu!\nSpusťte Bolusovou kalkulačku a proveďte výpočet znovu.</string>
<string name="fabric_upload_disabled">Nahrávání protokolů o pádech zakázáno!</string>
<string name="clear_filter">Vymazat filtr</string>
<string name="cannula">Kanyla</string>

View file

@ -46,7 +46,6 @@
<string name="authorizationfailed">Godkendelse mislykkedes</string>
<string name="copytolocalprofile_invalid">Kunne ikke oprette profil. Profilen er ugyldig.</string>
<string name="cta_dont_kill_my_app_info">Luk ikke min app?</string>
<string name="time_to_eat">Tid til at spise!\nKør Bolus guiden og lav beregning igen.</string>
<string name="fabric_upload_disabled">Upload af Crash logs deaktiveret!</string>
<string name="clear_filter">Nulstil filter</string>
<string name="cannula">Kanyle</string>

View file

@ -46,7 +46,6 @@
<string name="authorizationfailed">Autorisierung fehlgeschlagen</string>
<string name="copytolocalprofile_invalid">Profil kann nicht erstellt werden. Profil ist ungültig.</string>
<string name="cta_dont_kill_my_app_info">Don\'t kill my app?</string>
<string name="time_to_eat">Zeit zum Essen!\nStarte den Bolus-Rechner und gib die KH ein. </string>
<string name="fabric_upload_disabled">Hochladen von Crash-Protokollen deaktiviert!</string>
<string name="clear_filter">Filter löschen</string>
<string name="cannula">Kanüle</string>

View file

@ -46,7 +46,6 @@
<string name="authorizationfailed">Ha fallado la autorización</string>
<string name="copytolocalprofile_invalid">No se puede crear el perfil. El perfil es inválido.</string>
<string name="cta_dont_kill_my_app_info">¿No matar mi aplicación?</string>
<string name="time_to_eat">¡Hora de comer!\nEjecutar el asistente de bolo y calcular de nuevo.</string>
<string name="fabric_upload_disabled">¡Carga de registros de errores desactivada!</string>
<string name="clear_filter">Borrar filtro</string>
<string name="cannula">Cánula</string>

View file

@ -46,7 +46,6 @@
<string name="authorizationfailed">Echec de l\'authentification</string>
<string name="copytolocalprofile_invalid">Impossible de créer le profil. Le profil est invalide.</string>
<string name="cta_dont_kill_my_app_info">Garder l\'appli en arrière plan ?</string>
<string name="time_to_eat">Il est temps de manger !\nExécutez l\'assistant Bolus et refaites le calcul.</string>
<string name="fabric_upload_disabled">Téléchargement logs crashs désactivé!</string>
<string name="clear_filter">Effacer le filtre</string>
<string name="cannula">Canule</string>

View file

@ -46,7 +46,6 @@
<string name="authorizationfailed">Autorizzazione fallita</string>
<string name="copytolocalprofile_invalid">Impossibile creare il profilo. Il profilo non è valido.</string>
<string name="cta_dont_kill_my_app_info">Non terminare l\'app?</string>
<string name="time_to_eat">Tempo di mangiare!\nEsegui il calcolatore e fai di nuovi i calcoli.</string>
<string name="fabric_upload_disabled">Caricamento log dei crash disabilitato!</string>
<string name="clear_filter">Cancella filtro</string>
<string name="cannula">Cannula</string>

View file

@ -46,7 +46,6 @@
<string name="authorizationfailed">ההרשאה נכשלה</string>
<string name="copytolocalprofile_invalid">לא ניתן ליצור פרופיל מקומי. הפרופיל אינו חוקי.</string>
<string name="cta_dont_kill_my_app_info">איך לא להשבית את האפליקציה שלי?</string>
<string name="time_to_eat">זמן לאכול!\nהפעילו את אשף הבולוסים וחשבו בולוס חדש.</string>
<string name="fabric_upload_disabled">העלאת רשומות קריסה מושבתת!</string>
<string name="clear_filter">נקה סינון</string>
<string name="cannula">צינורית</string>

View file

@ -43,7 +43,6 @@
<string name="chartmenu">차트 메뉴</string>
<string name="authorizationfailed">인증 실패</string>
<string name="cta_dont_kill_my_app_info">앱이 종료되지 않도록 합니다?</string>
<string name="time_to_eat">식사할 시간입니다! \nBolus wizard를 켜고 다시 계산하십시오.</string>
<string name="fabric_upload_disabled">충돌 로그 업로드가 작동하지 않습니다.</string>
<string name="clear_filter">필터 지우기</string>
<string name="cannula">캐뉼라</string>

View file

@ -46,7 +46,6 @@
<string name="authorizationfailed">Autorizacija nepavyko</string>
<string name="copytolocalprofile_invalid">Nepavyksta sukurti profilio. Profilis neteisingas.</string>
<string name="cta_dont_kill_my_app_info">Don\'t kill my app?</string>
<string name="time_to_eat">Laikas valgyti!\nĮjunkite Boluso patarėją ir atlikite skaičiavimą dar kartą.</string>
<string name="fabric_upload_disabled">Sutrikimų žurnalo įrašų įkėlimas išjungtas!</string>
<string name="clear_filter">Valyti filtrą</string>
<string name="cannula">Kaniulė</string>

View file

@ -46,7 +46,6 @@
<string name="authorizationfailed">Autorisatie mislukt</string>
<string name="copytolocalprofile_invalid">Kan profiel niet aanmaken. Profiel is ongeldig.</string>
<string name="cta_dont_kill_my_app_info">Don\'t kill my app?</string>
<string name="time_to_eat">Tijd om te eten!\nVoer de boluswizard opnieuw uit.</string>
<string name="fabric_upload_disabled">Upload van crashrapporten is uitgeschakeld!</string>
<string name="clear_filter">Verwijder filter</string>
<string name="cannula">Canule</string>

View file

@ -46,7 +46,6 @@
<string name="authorizationfailed">Autentisering feilet</string>
<string name="copytolocalprofile_invalid">Klarte ikke å opprette profil. Profilen er ikke gyldig.</string>
<string name="cta_dont_kill_my_app_info">Avslutte app?</string>
<string name="time_to_eat">Nå må du spise!\Bruk bolus veiviseren og beregn på nytt.</string>
<string name="fabric_upload_disabled">Opplast av krasj logger er deaktivert!</string>
<string name="clear_filter">Nullstill filtre</string>
<string name="cannula">Kanyle</string>

View file

@ -44,7 +44,6 @@
<string name="authorizationfailed">Autoryzacja nie powiodła się</string>
<string name="copytolocalprofile_invalid">Nie można utworzyć profilu. Profil jest nieprawidłowy.</string>
<string name="cta_dont_kill_my_app_info">Nie zabij mojej aplikacji?</string>
<string name="time_to_eat">Czas jeść!\nUruchom kreatora bolusa i zrób obliczenia ponownie.</string>
<string name="fabric_upload_disabled">Przesyłanie dzienników awarii jest wyłączone!</string>
<string name="clear_filter">Wyczyść filtr</string>
<string name="cannula">Kaniula</string>

View file

@ -46,7 +46,6 @@
<string name="authorizationfailed">Falha na autorização</string>
<string name="copytolocalprofile_invalid">Não foi possível criar o perfil. Perfil inválido.</string>
<string name="cta_dont_kill_my_app_info">Não encerre meu aplicativo?</string>
<string name="time_to_eat">Hora de comer!\nAbra o assistente de bolus e faça o cálculo novamente.</string>
<string name="fabric_upload_disabled">Envio de logs de erro desativado!</string>
<string name="clear_filter">Limpar filtro</string>
<string name="cannula">Cânula</string>

View file

@ -44,7 +44,6 @@
<string name="authorizationfailed">Falha na autorização</string>
<string name="copytolocalprofile_invalid">Não é possível criar o perfil. O perfil é inválido.</string>
<string name="cta_dont_kill_my_app_info">Não encerre minha app?</string>
<string name="time_to_eat">Hora de comer!\nExecutar assistente de Bólus e fazer cálculo novamente.</string>
<string name="fabric_upload_disabled">Envio de registos de erro desativado!</string>
<string name="clear_filter">Limpar filtros</string>
<string name="cannula">Cânula</string>

View file

@ -44,7 +44,6 @@
<string name="authorizationfailed">Autorizarea a eșuat</string>
<string name="copytolocalprofile_invalid">Nu se poate crea profilul. Profilul este invalid.</string>
<string name="cta_dont_kill_my_app_info">Nu-mi opri aplicația?</string>
<string name="time_to_eat">Timpul sa mănânci!\nRuleaza Calculatorul de Bolus pentru a face calculele din nou.</string>
<string name="fabric_upload_disabled">Încărcarea jurnalelor de erori este dezactivata!</string>
<string name="clear_filter">Șterge filtru</string>
<string name="cannula">Canula</string>

View file

@ -46,7 +46,6 @@
<string name="authorizationfailed">Ошибка авторизации</string>
<string name="copytolocalprofile_invalid">Не удается создать локальный профиль. Настройки профиля неправильны.</string>
<string name="cta_dont_kill_my_app_info">Не закрывать приложение?</string>
<string name="time_to_eat">Пора есть!\nЗапустите помощник болюса снова для подсчета.</string>
<string name="fabric_upload_disabled">Загрузка журналов сбоя на сервер отключена!</string>
<string name="clear_filter">Очистить фильтр</string>
<string name="cannula">Катетер помпы</string>

View file

@ -46,7 +46,6 @@
<string name="authorizationfailed">Autorizácia zlyhala</string>
<string name="copytolocalprofile_invalid">Nie je možné vytvoriť lokálny profil. Profil je neplatný.</string>
<string name="cta_dont_kill_my_app_info">Nepotláčať moju aplikáciu?</string>
<string name="time_to_eat">Čas na jedlo!\nSpustite Bolusovú kalkulačku a urobte výpočet znova.</string>
<string name="fabric_upload_disabled">Odosielanie protokolov o zlyhaní je zakázané!</string>
<string name="clear_filter">Vyčistiť filter</string>
<string name="cannula">Kanyla</string>

View file

@ -44,7 +44,6 @@
<string name="authorizationfailed">Behörighetskontroll misslyckades</string>
<string name="copytolocalprofile_invalid">Kan inte att skapa profilen. Profilen är felaktig.</string>
<string name="cta_dont_kill_my_app_info">Döda inte min app?</string>
<string name="time_to_eat">Dags att äta!\nKör bolusguiden igen för ny beräkning.</string>
<string name="fabric_upload_disabled">Uppladdning av kraschloggar inaktiverad!</string>
<string name="clear_filter">Rensa filter</string>
<string name="cannula">Kanyl</string>

View file

@ -46,7 +46,6 @@
<string name="authorizationfailed">Yetkilendirme başarısız oldu</string>
<string name="copytolocalprofile_invalid">Profil oluşturulamıyor. Profil geçersiz.</string>
<string name="cta_dont_kill_my_app_info">Uygulamamı devre dışı bırakma?</string>
<string name="time_to_eat">Yemek zamanı!\nBolus sihirbazını çalıştırın ve yeniden hesaplama yapın.</string>
<string name="fabric_upload_disabled">Çökme günlükleri yükleme devre dışı bırakıldı!</string>
<string name="clear_filter">Filtreyi temizle</string>
<string name="cannula">Kanül</string>

View file

@ -46,7 +46,6 @@
<string name="authorizationfailed">授权失败</string>
<string name="copytolocalprofile_invalid">无法创建配置文件。配置文件无效。</string>
<string name="cta_dont_kill_my_app_info">不要杀死我的应用程序?</string>
<string name="time_to_eat">吃饭时间到了!\n请运行大剂量向导然后进行计算。</string>
<string name="fabric_upload_disabled">已禁用崩溃日志上传!</string>
<string name="clear_filter">清除筛选</string>
<string name="cannula">输注导管</string>

View file

@ -79,7 +79,6 @@
<string name="authorizationfailed">Authorization failed</string>
<string name="copytolocalprofile_invalid">Unable to create profile. Profile is invalid.</string>
<string name="cta_dont_kill_my_app_info">Don\'t kill my app?</string>
<string name="time_to_eat">Time to eat!\nRun Bolus wizard and do calculation again.</string>
<string name="fabric_upload_disabled">Crash logs upload disabled!</string>
<string name="clear_filter">Clear filter</string>
<string name="cannula">Cannula</string>

View file

@ -10,9 +10,8 @@ import info.nightscout.androidaps.insight.database.InsightDatabase
import info.nightscout.androidaps.insight.database.InsightDatabaseDao
import info.nightscout.androidaps.insight.database.InsightDbHelper
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
import info.nightscout.core.iob.iobCobCalculator.GlucoseStatusProvider
import info.nightscout.database.impl.AppRepository
import info.nightscout.implementation.constraints.ConstraintsImpl
import info.nightscout.implementation.iob.GlucoseStatusProviderImpl
import info.nightscout.interfaces.bgQualityCheck.BgQualityCheck
import info.nightscout.interfaces.constraints.Constraint
import info.nightscout.interfaces.constraints.Constraints
@ -21,7 +20,7 @@ import info.nightscout.interfaces.maintenance.PrefFileListProvider
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.plugin.PluginBase
import info.nightscout.interfaces.plugin.PluginType
import info.nightscout.interfaces.profile.ProfileInstantiator
import info.nightscout.interfaces.profile.Instantiator
import info.nightscout.interfaces.profiling.Profiler
import info.nightscout.interfaces.pump.DetailedBolusInfoStorage
import info.nightscout.interfaces.pump.PumpEnactResult
@ -34,17 +33,19 @@ import info.nightscout.interfaces.utils.HardLimits
import info.nightscout.plugins.aps.openAPSAMA.OpenAPSAMAPlugin
import info.nightscout.plugins.aps.openAPSSMB.OpenAPSSMBPlugin
import info.nightscout.plugins.aps.openAPSSMBDynamicISF.OpenAPSSMBDynamicISFPlugin
import info.nightscout.plugins.constraints.ConstraintsImpl
import info.nightscout.plugins.constraints.objectives.ObjectivesPlugin
import info.nightscout.plugins.constraints.objectives.objectives.Objective
import info.nightscout.plugins.constraints.safety.SafetyPlugin
import info.nightscout.plugins.pump.virtual.VirtualPumpPlugin
import info.nightscout.plugins.source.GlimpPlugin
import info.nightscout.pump.combo.ComboPlugin
import info.nightscout.pump.combo.ruffyscripter.RuffyScripter
import info.nightscout.pump.dana.DanaPump
import info.nightscout.pump.dana.R
import info.nightscout.pump.dana.database.DanaHistoryDatabase
import info.nightscout.pump.virtual.VirtualPumpPlugin
import info.nightscout.shared.sharedPreferences.SP
import org.junit.Assert
import info.nightscout.source.GlimpPlugin
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.mockito.Mock
@ -69,7 +70,7 @@ class ConstraintsCheckerTest : TestBaseWithProfile() {
@Mock lateinit var insightDatabaseDao: InsightDatabaseDao
@Mock lateinit var ruffyScripter: RuffyScripter
@Mock lateinit var uiInteraction: UiInteraction
@Mock lateinit var profileInstantiator: ProfileInstantiator
@Mock lateinit var instantiator: Instantiator
@Mock lateinit var danaHistoryDatabase: DanaHistoryDatabase
@Mock lateinit var insightDatabase: InsightDatabase
@Mock lateinit var bgQualityCheck: BgQualityCheck
@ -102,46 +103,49 @@ class ConstraintsCheckerTest : TestBaseWithProfile() {
@BeforeEach
fun prepare() {
`when`(rh.gs(info.nightscout.plugins.R.string.closed_loop_disabled_on_dev_branch)).thenReturn("Running dev version. Closed loop is disabled.")
`when`(rh.gs(info.nightscout.plugins.R.string.closedmodedisabledinpreferences)).thenReturn("Closed loop mode disabled in preferences")
`when`(rh.gs(info.nightscout.plugins.constraints.R.string.closed_loop_disabled_on_dev_branch)).thenReturn("Running dev version. Closed loop is disabled.")
`when`(rh.gs(info.nightscout.plugins.constraints.R.string.closedmodedisabledinpreferences)).thenReturn("Closed loop mode disabled in preferences")
`when`(rh.gs(info.nightscout.core.ui.R.string.no_valid_basal_rate)).thenReturn("No valid basal rate read from pump")
`when`(rh.gs(info.nightscout.plugins.aps.R.string.autosens_disabled_in_preferences)).thenReturn("Autosens disabled in preferences")
`when`(rh.gs(info.nightscout.plugins.aps.R.string.smb_disabled_in_preferences)).thenReturn("SMB disabled in preferences")
`when`(rh.gs(info.nightscout.core.ui.R.string.pumplimit)).thenReturn("pump limit")
`when`(rh.gs(info.nightscout.core.ui.R.string.itmustbepositivevalue)).thenReturn("it must be positive value")
`when`(rh.gs(info.nightscout.plugins.R.string.maxvalueinpreferences)).thenReturn("max value in preferences")
`when`(rh.gs(info.nightscout.plugins.constraints.R.string.maxvalueinpreferences)).thenReturn("max value in preferences")
`when`(rh.gs(info.nightscout.plugins.aps.R.string.max_basal_multiplier)).thenReturn("max basal multiplier")
`when`(rh.gs(info.nightscout.plugins.aps.R.string.max_daily_basal_multiplier)).thenReturn("max daily basal multiplier")
`when`(rh.gs(info.nightscout.core.ui.R.string.pumplimit)).thenReturn("pump limit")
`when`(rh.gs(info.nightscout.core.ui.R.string.limitingbolus)).thenReturn("Limiting bolus to %.1f U because of %s")
`when`(rh.gs(info.nightscout.plugins.R.string.hardlimit)).thenReturn("hard limit")
`when`(rh.gs(info.nightscout.plugins.constraints.R.string.hardlimit)).thenReturn("hard limit")
`when`(rh.gs(info.nightscout.core.utils.R.string.key_child)).thenReturn("child")
`when`(rh.gs(info.nightscout.plugins.R.string.limitingcarbs)).thenReturn("Limiting carbs to %d g because of %s")
`when`(rh.gs(info.nightscout.plugins.constraints.R.string.limitingcarbs)).thenReturn("Limiting carbs to %d g because of %s")
`when`(rh.gs(info.nightscout.plugins.aps.R.string.limiting_iob)).thenReturn("Limiting IOB to %.1f U because of %s")
`when`(rh.gs(info.nightscout.core.ui.R.string.limitingbasalratio)).thenReturn("Limiting max basal rate to %1\$.2f U/h because of %2\$s")
`when`(rh.gs(info.nightscout.core.ui.R.string.limitingpercentrate)).thenReturn("Limiting max percent rate to %1\$d%% because of %2\$s")
`when`(rh.gs(info.nightscout.core.ui.R.string.itmustbepositivevalue)).thenReturn("it must be positive value")
`when`(rh.gs(info.nightscout.plugins.R.string.smbnotallowedinopenloopmode)).thenReturn("SMB not allowed in open loop mode")
`when`(rh.gs(info.nightscout.plugins.constraints.R.string.smbnotallowedinopenloopmode)).thenReturn("SMB not allowed in open loop mode")
`when`(rh.gs(info.nightscout.core.ui.R.string.pumplimit)).thenReturn("pump limit")
`when`(rh.gs(info.nightscout.plugins.R.string.smbalwaysdisabled)).thenReturn("SMB always and after carbs disabled because active BG source doesn\\'t support advanced filtering")
`when`(rh.gs(info.nightscout.plugins.constraints.R.string.smbalwaysdisabled)).thenReturn("SMB always and after carbs disabled because active BG source doesn\\'t support advanced filtering")
`when`(rh.gs(info.nightscout.core.ui.R.string.limitingpercentrate)).thenReturn("Limiting max percent rate to %1\$d%% because of %2\$s")
`when`(rh.gs(info.nightscout.core.ui.R.string.limitingbolus)).thenReturn("Limiting bolus to %1\$.1f U because of %2\$s")
`when`(rh.gs(info.nightscout.core.ui.R.string.limitingbasalratio)).thenReturn("Limiting max basal rate to %1\$.2f U/h because of %2\$s")
`when`(context.getString(info.nightscout.pump.combo.R.string.combo_pump_unsupported_operation)).thenReturn("Requested operation not supported by pump")
`when`(rh.gs(info.nightscout.plugins.R.string.objectivenotstarted)).thenReturn("Objective %1\$d not started")
`when`(rh.gs(info.nightscout.plugins.constraints.R.string.objectivenotstarted)).thenReturn("Objective %1\$d not started")
// RS constructor
`when`(sp.getString(info.nightscout.pump.dana.R.string.key_danars_address, "")).thenReturn("")
`when`(sp.getString(R.string.key_danars_name, "")).thenReturn("")
`when`(sp.getString(R.string.key_danars_address, "")).thenReturn("")
// R
`when`(sp.getString(R.string.key_danar_bt_name, "")).thenReturn("")
//SafetyPlugin
`when`(activePlugin.activePump).thenReturn(virtualPumpPlugin)
constraintChecker = ConstraintsImpl(activePlugin)
val glucoseStatusProvider = GlucoseStatusProvider(aapsLogger = aapsLogger, iobCobCalculator = iobCobCalculator, dateUtil = dateUtil)
val glucoseStatusProvider = GlucoseStatusProviderImpl(aapsLogger = aapsLogger, iobCobCalculator = iobCobCalculator, dateUtil = dateUtil)
hardLimits = HardLimitsMock(sp, rh)
insightDbHelper = InsightDbHelper(insightDatabaseDao)
danaPump = DanaPump(aapsLogger, sp, dateUtil, profileInstantiator)
danaPump = DanaPump(aapsLogger, sp, dateUtil, instantiator)
objectivesPlugin = ObjectivesPlugin(injector, aapsLogger, rh, activePlugin, sp, config)
comboPlugin = ComboPlugin(injector, aapsLogger, rxBus, rh, profileFunction, sp, commandQueue, pumpSync, dateUtil, ruffyScripter, uiInteraction)
danaRPlugin = DanaRPlugin(injector, aapsLogger, aapsSchedulers, rxBus, context, rh, constraintChecker, activePlugin, sp, commandQueue, danaPump, dateUtil, fabricPrivacy, pumpSync,
@ -238,7 +242,8 @@ class ConstraintsCheckerTest : TestBaseWithProfile() {
hardLimits,
ConfigImpl(fileListProvider),
iobCobCalculator,
dateUtil
dateUtil,
uiInteraction
)
val constraintsPluginsList = ArrayList<PluginBase>()
constraintsPluginsList.add(safetyPlugin)
@ -260,9 +265,9 @@ class ConstraintsCheckerTest : TestBaseWithProfile() {
comboPlugin.setPluginEnabled(PluginType.PUMP, true)
comboPlugin.setValidBasalRateProfileSelectedOnPump(false)
val c = constraintChecker.isLoopInvocationAllowed()
Assert.assertEquals(true, c.reasonList.size == 2) // Combo & Objectives
Assert.assertEquals(true, c.mostLimitedReasonList.size == 2) // Combo & Objectives
Assert.assertEquals(java.lang.Boolean.FALSE, c.value())
Assertions.assertEquals(true, c.reasonList.size == 2) // Combo & Objectives
Assertions.assertEquals(true, c.mostLimitedReasonList.size == 2) // Combo & Objectives
Assertions.assertEquals(java.lang.Boolean.FALSE, c.value())
}
// Safety & Objectives
@ -273,13 +278,13 @@ class ConstraintsCheckerTest : TestBaseWithProfile() {
objectivesPlugin.objectives[Objectives.MAXIOB_ZERO_CL_OBJECTIVE].startedOn = 0
var c: Constraint<Boolean> = constraintChecker.isClosedLoopAllowed()
aapsLogger.debug("Reason list: " + c.reasonList.toString())
// Assert.assertTrue(c.reasonList[0].toString().contains("Closed loop is disabled")) // Safety & Objectives
Assert.assertEquals(false, c.value())
// Assertions.assertTrue(c.reasonList[0].toString().contains("Closed loop is disabled")) // Safety & Objectives
Assertions.assertEquals(false, c.value())
`when`(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, "open")).thenReturn("open")
c = constraintChecker.isClosedLoopAllowed()
Assert.assertTrue(c.reasonList[0].contains("Closed loop mode disabled in preferences")) // Safety & Objectives
// Assert.assertEquals(3, c.reasonList.size) // 2x Safety & Objectives
Assert.assertEquals(false, c.value())
Assertions.assertTrue(c.reasonList[0].contains("Closed loop mode disabled in preferences")) // Safety & Objectives
// Assertions.assertEquals(3, c.reasonList.size) // 2x Safety & Objectives
Assertions.assertEquals(false, c.value())
}
// Safety & Objectives
@ -289,9 +294,9 @@ class ConstraintsCheckerTest : TestBaseWithProfile() {
objectivesPlugin.objectives[Objectives.AUTOSENS_OBJECTIVE].startedOn = 0
`when`(sp.getBoolean(info.nightscout.plugins.aps.R.string.key_openapsama_use_autosens, false)).thenReturn(false)
val c = constraintChecker.isAutosensModeEnabled()
Assert.assertEquals(true, c.reasonList.size == 2) // Safety & Objectives
Assert.assertEquals(true, c.mostLimitedReasonList.size == 2) // Safety & Objectives
Assert.assertEquals(java.lang.Boolean.FALSE, c.value())
Assertions.assertEquals(true, c.reasonList.size == 2) // Safety & Objectives
Assertions.assertEquals(true, c.mostLimitedReasonList.size == 2) // Safety & Objectives
Assertions.assertEquals(java.lang.Boolean.FALSE, c.value())
}
// Safety
@ -299,9 +304,9 @@ class ConstraintsCheckerTest : TestBaseWithProfile() {
fun isAdvancedFilteringEnabledTest() {
`when`(activePlugin.activeBgSource).thenReturn(glimpPlugin)
val c = constraintChecker.isAdvancedFilteringEnabled()
Assert.assertEquals(true, c.reasonList.size == 1) // Safety
Assert.assertEquals(true, c.mostLimitedReasonList.size == 1) // Safety
Assert.assertEquals(false, c.value())
Assertions.assertEquals(true, c.reasonList.size == 1) // Safety
Assertions.assertEquals(true, c.mostLimitedReasonList.size == 1) // Safety
Assertions.assertEquals(false, c.value())
}
// SMB should limit
@ -309,7 +314,7 @@ class ConstraintsCheckerTest : TestBaseWithProfile() {
fun isSuperBolusEnabledTest() {
openAPSSMBPlugin.setPluginEnabled(PluginType.APS, true)
val c = constraintChecker.isSuperBolusEnabled()
Assert.assertEquals(java.lang.Boolean.FALSE, c.value()) // SMB should limit
Assertions.assertEquals(java.lang.Boolean.FALSE, c.value()) // SMB should limit
}
// Safety & Objectives
@ -321,9 +326,9 @@ class ConstraintsCheckerTest : TestBaseWithProfile() {
`when`(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, "open")).thenReturn("open")
// `when`(constraintChecker.isClosedLoopAllowed()).thenReturn(Constraint(true))
val c = constraintChecker.isSMBModeEnabled()
Assert.assertEquals(true, c.reasonList.size == 3) // 2x Safety & Objectives
Assert.assertEquals(true, c.mostLimitedReasonList.size == 3) // 2x Safety & Objectives
Assert.assertEquals(false, c.value())
Assertions.assertEquals(true, c.reasonList.size == 3) // 2x Safety & Objectives
Assertions.assertEquals(true, c.mostLimitedReasonList.size == 3) // 2x Safety & Objectives
Assertions.assertEquals(false, c.value())
}
// applyBasalConstraints tests
@ -349,9 +354,9 @@ class ConstraintsCheckerTest : TestBaseWithProfile() {
// Apply all limits
val d = constraintChecker.getMaxBasalAllowed(validProfile)
Assert.assertEquals(0.8, d.value(), 0.01)
Assert.assertEquals(3, d.reasonList.size)
Assert.assertEquals("DanaR: Limiting max basal rate to 0.80 U/h because of pump limit", d.getMostLimitedReasons(aapsLogger))
Assertions.assertEquals(0.8, d.value(), 0.01)
Assertions.assertEquals(3, d.reasonList.size)
Assertions.assertEquals("DanaR: Limiting max basal rate to 0.80 U/h because of pump limit", d.getMostLimitedReasons(aapsLogger))
}
@Test
@ -376,9 +381,9 @@ class ConstraintsCheckerTest : TestBaseWithProfile() {
// Apply all limits
val i = constraintChecker.getMaxBasalPercentAllowed(validProfile)
Assert.assertEquals(200, i.value())
Assert.assertEquals(6, i.reasonList.size)
Assert.assertEquals("Safety: Limiting max percent rate to 200% because of pump limit", i.getMostLimitedReasons(aapsLogger))
Assertions.assertEquals(200, i.value())
Assertions.assertEquals(6, i.reasonList.size)
Assertions.assertEquals("Safety: Limiting max percent rate to 200% because of pump limit", i.getMostLimitedReasons(aapsLogger))
}
// applyBolusConstraints tests
@ -403,9 +408,9 @@ class ConstraintsCheckerTest : TestBaseWithProfile() {
// Apply all limits
val d = constraintChecker.getMaxBolusAllowed()
Assert.assertEquals(3.0, d.value(), 0.01)
Assert.assertEquals(4, d.reasonList.size) // 2x Safety & RS & R
Assert.assertEquals("Safety: Limiting bolus to 3.0 U because of max value in preferences", d.getMostLimitedReasons(aapsLogger))
Assertions.assertEquals(3.0, d.value(), 0.01)
Assertions.assertEquals(4, d.reasonList.size) // 2x Safety & RS & R
Assertions.assertEquals("Safety: Limiting bolus to 3.0 U because of max value in preferences", d.getMostLimitedReasons(aapsLogger))
}
// applyCarbsConstraints tests
@ -416,9 +421,9 @@ class ConstraintsCheckerTest : TestBaseWithProfile() {
// Apply all limits
val i = constraintChecker.getMaxCarbsAllowed()
Assert.assertEquals(48, i.value())
Assert.assertEquals(true, i.reasonList.size == 1)
Assert.assertEquals("Safety: Limiting carbs to 48 g because of max value in preferences", i.getMostLimitedReasons(aapsLogger))
Assertions.assertEquals(48, i.value())
Assertions.assertEquals(true, i.reasonList.size == 1)
Assertions.assertEquals("Safety: Limiting carbs to 48 g because of max value in preferences", i.getMostLimitedReasons(aapsLogger))
}
// applyMaxIOBConstraints tests
@ -433,9 +438,9 @@ class ConstraintsCheckerTest : TestBaseWithProfile() {
// Apply all limits
val d = constraintChecker.getMaxIOBAllowed()
Assert.assertEquals(1.5, d.value(), 0.01)
Assert.assertEquals(d.reasonList.toString(), 2, d.reasonList.size)
Assert.assertEquals("OpenAPSAMA: Limiting IOB to 1.5 U because of max value in preferences", d.getMostLimitedReasons(aapsLogger))
Assertions.assertEquals(1.5, d.value(), 0.01)
Assertions.assertEquals(2, d.reasonList.size)
Assertions.assertEquals("OpenAPSAMA: Limiting IOB to 1.5 U because of max value in preferences", d.getMostLimitedReasons(aapsLogger))
}
@Test
@ -449,8 +454,8 @@ class ConstraintsCheckerTest : TestBaseWithProfile() {
// Apply all limits
val d = constraintChecker.getMaxIOBAllowed()
Assert.assertEquals(3.0, d.value(), 0.01)
Assert.assertEquals(d.reasonList.toString(), 2, d.reasonList.size)
Assert.assertEquals("OpenAPSSMB: Limiting IOB to 3.0 U because of max value in preferences", d.getMostLimitedReasons(aapsLogger))
Assertions.assertEquals(3.0, d.value(), 0.01)
Assertions.assertEquals(2, d.reasonList.size)
Assertions.assertEquals("OpenAPSSMB: Limiting IOB to 3.0 U because of max value in preferences", d.getMostLimitedReasons(aapsLogger))
}
}

View file

@ -21,7 +21,7 @@ import info.nightscout.interfaces.receivers.ReceiverStatusStore
import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.plugins.aps.loop.LoopFragment
import info.nightscout.plugins.aps.loop.LoopPlugin
import info.nightscout.plugins.pump.virtual.VirtualPumpPlugin
import info.nightscout.pump.virtual.VirtualPumpPlugin
import info.nightscout.rx.bus.RxBus
import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.sharedPreferences.SP

View file

@ -4,25 +4,25 @@ import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.HardLimitsMock
import info.nightscout.androidaps.TestBaseWithProfile
import info.nightscout.core.iob.iobCobCalculator.GlucoseStatusProvider
import info.nightscout.database.impl.AppRepository
import info.nightscout.interfaces.Constants
import info.nightscout.interfaces.bgQualityCheck.BgQualityCheck
import info.nightscout.interfaces.constraints.Constraint
import info.nightscout.interfaces.constraints.Constraints
import info.nightscout.interfaces.iob.GlucoseStatusProvider
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.plugin.PluginType
import info.nightscout.interfaces.profiling.Profiler
import info.nightscout.interfaces.pump.defs.PumpDescription
import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.interfaces.utils.HardLimits
import info.nightscout.plugins.R
import info.nightscout.plugins.aps.openAPSAMA.OpenAPSAMAPlugin
import info.nightscout.plugins.aps.openAPSSMB.OpenAPSSMBPlugin
import info.nightscout.plugins.constraints.safety.SafetyPlugin
import info.nightscout.plugins.pump.virtual.VirtualPumpPlugin
import info.nightscout.plugins.source.GlimpPlugin
import info.nightscout.pump.virtual.VirtualPumpPlugin
import info.nightscout.shared.sharedPreferences.SP
import org.junit.Assert
import info.nightscout.source.GlimpPlugin
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.mockito.Mock
@ -39,6 +39,7 @@ class SafetyPluginTest : TestBaseWithProfile() {
@Mock lateinit var repository: AppRepository
@Mock lateinit var glucoseStatusProvider: GlucoseStatusProvider
@Mock lateinit var bgQualityCheck: BgQualityCheck
@Mock lateinit var uiInteraction: UiInteraction
private lateinit var hardLimits: HardLimits
private lateinit var safetyPlugin: SafetyPlugin
@ -50,24 +51,24 @@ class SafetyPluginTest : TestBaseWithProfile() {
@BeforeEach
fun prepare() {
`when`(rh.gs(R.string.hardlimit)).thenReturn("hard limit")
`when`(rh.gs(info.nightscout.plugins.constraints.R.string.hardlimit)).thenReturn("hard limit")
`when`(rh.gs(info.nightscout.core.ui.R.string.itmustbepositivevalue)).thenReturn("it must be positive value")
`when`(rh.gs(info.nightscout.core.ui.R.string.pumplimit)).thenReturn("pump limit")
`when`(rh.gs(R.string.maxvalueinpreferences)).thenReturn("max value in preferences")
`when`(rh.gs(info.nightscout.plugins.constraints.R.string.maxvalueinpreferences)).thenReturn("max value in preferences")
`when`(rh.gs(info.nightscout.plugins.aps.R.string.max_daily_basal_multiplier)).thenReturn("max daily basal multiplier")
`when`(rh.gs(info.nightscout.plugins.aps.R.string.max_basal_multiplier)).thenReturn("max basal multiplier")
`when`(rh.gs(info.nightscout.core.ui.R.string.limitingbolus)).thenReturn("Limiting bolus to %1\$.1f U because of %2\$s")
`when`(rh.gs(info.nightscout.core.ui.R.string.limitingbasalratio)).thenReturn("Limiting max basal rate to %1\$.2f U/h because of %2\$s")
`when`(rh.gs(info.nightscout.core.ui.R.string.limiting_iob)).thenReturn("Limiting IOB to %1\$.1f U because of %2\$s")
`when`(rh.gs(R.string.limitingcarbs)).thenReturn("Limiting carbs to %1\$d g because of %2\$s")
`when`(rh.gs(info.nightscout.plugins.constraints.R.string.limitingcarbs)).thenReturn("Limiting carbs to %1\$d g because of %2\$s")
`when`(rh.gs(info.nightscout.core.ui.R.string.limitingpercentrate)).thenReturn("Limiting max percent rate to %1\$d%% because of %2\$s")
`when`(rh.gs(R.string.pumpisnottempbasalcapable)).thenReturn("Pump is not temp basal capable")
`when`(rh.gs(info.nightscout.plugins.constraints.R.string.pumpisnottempbasalcapable)).thenReturn("Pump is not temp basal capable")
`when`(rh.gs(info.nightscout.plugins.aps.R.string.increasing_max_basal)).thenReturn("Increasing max basal value because setting is lower than your max basal in profile")
`when`(rh.gs(info.nightscout.plugins.aps.R.string.smb_disabled_in_preferences)).thenReturn("SMB disabled in preferences")
`when`(rh.gs(R.string.closedmodedisabledinpreferences)).thenReturn("Closed loop mode disabled in preferences")
`when`(rh.gs(R.string.closed_loop_disabled_on_dev_branch)).thenReturn("Running dev version. Closed loop is disabled.")
`when`(rh.gs(R.string.smbalwaysdisabled)).thenReturn("SMB always and after carbs disabled because active BG source doesn\\'t support advanced filtering")
`when`(rh.gs(R.string.smbnotallowedinopenloopmode)).thenReturn("SMB not allowed in open loop mode")
`when`(rh.gs(info.nightscout.plugins.constraints.R.string.closedmodedisabledinpreferences)).thenReturn("Closed loop mode disabled in preferences")
`when`(rh.gs(info.nightscout.plugins.constraints.R.string.closed_loop_disabled_on_dev_branch)).thenReturn("Running dev version. Closed loop is disabled.")
`when`(rh.gs(info.nightscout.plugins.constraints.R.string.smbalwaysdisabled)).thenReturn("SMB always and after carbs disabled because active BG source doesn\\'t support advanced filtering")
`when`(rh.gs(info.nightscout.plugins.constraints.R.string.smbnotallowedinopenloopmode)).thenReturn("SMB not allowed in open loop mode")
`when`(rh.gs(info.nightscout.core.utils.R.string.key_child)).thenReturn("child")
`when`(rh.gs(info.nightscout.core.ui.R.string.lowglucosesuspend)).thenReturn("Low Glucose Suspend")
@ -75,7 +76,7 @@ class SafetyPluginTest : TestBaseWithProfile() {
`when`(virtualPumpPlugin.pumpDescription).thenReturn(pumpDescription)
`when`(config.APS).thenReturn(true)
hardLimits = HardLimitsMock(sp, rh)
safetyPlugin = SafetyPlugin(injector, aapsLogger, rh, sp, rxBus, constraintChecker, activePlugin, hardLimits, config, iobCobCalculator, dateUtil)
safetyPlugin = SafetyPlugin(injector, aapsLogger, rh, sp, rxBus, constraintChecker, activePlugin, hardLimits, config, iobCobCalculator, dateUtil, uiInteraction)
openAPSAMAPlugin = OpenAPSAMAPlugin(
injector, aapsLogger, rxBus, constraintChecker, rh, profileFunction, context, activePlugin, iobCobCalculator, hardLimits, profiler, fabricPrivacy,
dateUtil, repository, glucoseStatusProvider, sp
@ -91,8 +92,8 @@ class SafetyPluginTest : TestBaseWithProfile() {
pumpDescription.isTempBasalCapable = false
var c = Constraint(true)
c = safetyPlugin.isLoopInvocationAllowed(c)
Assert.assertEquals("Safety: Pump is not temp basal capable", c.getReasons(aapsLogger))
Assert.assertEquals(false, c.value())
Assertions.assertEquals("Safety: Pump is not temp basal capable", c.getReasons(aapsLogger))
Assertions.assertEquals(false, c.value())
}
@Test
@ -101,8 +102,8 @@ class SafetyPluginTest : TestBaseWithProfile() {
`when`(config.isEngineeringModeOrRelease()).thenReturn(false)
var c = Constraint(true)
c = safetyPlugin.isClosedLoopAllowed(c)
Assert.assertTrue(c.getReasons(aapsLogger).contains("Running dev version. Closed loop is disabled."))
Assert.assertEquals(false, c.value())
Assertions.assertTrue(c.getReasons(aapsLogger).contains("Running dev version. Closed loop is disabled."))
Assertions.assertEquals(false, c.value())
}
@Test
@ -110,8 +111,8 @@ class SafetyPluginTest : TestBaseWithProfile() {
`when`(sp.getString(info.nightscout.core.utils.R.string.key_aps_mode, "open")).thenReturn("open")
var c = Constraint(true)
c = safetyPlugin.isClosedLoopAllowed(c)
Assert.assertTrue(c.getReasons(aapsLogger).contains("Closed loop mode disabled in preferences"))
Assert.assertEquals(false, c.value())
Assertions.assertTrue(c.getReasons(aapsLogger).contains("Closed loop mode disabled in preferences"))
Assertions.assertEquals(false, c.value())
}
@Test
@ -120,8 +121,8 @@ class SafetyPluginTest : TestBaseWithProfile() {
`when`(constraintChecker.isClosedLoopAllowed(anyObject())).thenReturn(Constraint(true))
var c = Constraint(true)
c = openAPSSMBPlugin.isSMBModeEnabled(c)
Assert.assertTrue(c.getReasons(aapsLogger).contains("SMB disabled in preferences"))
Assert.assertEquals(false, c.value())
Assertions.assertTrue(c.getReasons(aapsLogger).contains("SMB disabled in preferences"))
Assertions.assertEquals(false, c.value())
}
@Test
@ -130,8 +131,8 @@ class SafetyPluginTest : TestBaseWithProfile() {
`when`(constraintChecker.isClosedLoopAllowed(anyObject())).thenReturn(Constraint(false))
var c = Constraint(true)
c = safetyPlugin.isSMBModeEnabled(c)
Assert.assertTrue(c.getReasons(aapsLogger).contains("SMB not allowed in open loop mode"))
Assert.assertEquals(false, c.value())
Assertions.assertTrue(c.getReasons(aapsLogger).contains("SMB not allowed in open loop mode"))
Assertions.assertEquals(false, c.value())
}
@Test
@ -139,8 +140,8 @@ class SafetyPluginTest : TestBaseWithProfile() {
`when`(activePlugin.activeBgSource).thenReturn(glimpPlugin)
var c = Constraint(true)
c = safetyPlugin.isAdvancedFilteringEnabled(c)
Assert.assertEquals("Safety: SMB always and after carbs disabled because active BG source doesn\\'t support advanced filtering", c.getReasons(aapsLogger))
Assert.assertEquals(false, c.value())
Assertions.assertEquals("Safety: SMB always and after carbs disabled because active BG source doesn\\'t support advanced filtering", c.getReasons(aapsLogger))
Assertions.assertEquals(false, c.value())
}
@Test
@ -151,13 +152,13 @@ class SafetyPluginTest : TestBaseWithProfile() {
`when`(sp.getString(info.nightscout.core.utils.R.string.key_age, "")).thenReturn("child")
val c = Constraint(Constants.REALLYHIGHBASALRATE)
safetyPlugin.applyBasalConstraints(c, validProfile)
Assert.assertEquals(2.0, c.value(), 0.01)
Assert.assertEquals(
Assertions.assertEquals(2.0, c.value(), 0.01)
Assertions.assertEquals(
"""
Safety: Limiting max basal rate to 2.00 U/h because of hard limit
""".trimIndent(), c.getReasons(aapsLogger)
)
Assert.assertEquals("Safety: Limiting max basal rate to 2.00 U/h because of hard limit", c.getMostLimitedReasons(aapsLogger))
Assertions.assertEquals("Safety: Limiting max basal rate to 2.00 U/h because of hard limit", c.getMostLimitedReasons(aapsLogger))
}
@Test
@ -165,8 +166,8 @@ class SafetyPluginTest : TestBaseWithProfile() {
`when`(sp.getString(info.nightscout.core.utils.R.string.key_age, "")).thenReturn("child")
val d = Constraint(-0.5)
safetyPlugin.applyBasalConstraints(d, validProfile)
Assert.assertEquals(0.0, d.value(), 0.01)
Assert.assertEquals("Safety: Limiting max basal rate to 0.00 U/h because of it must be positive value", d.getReasons(aapsLogger))
Assertions.assertEquals(0.0, d.value(), 0.01)
Assertions.assertEquals("Safety: Limiting max basal rate to 0.00 U/h because of it must be positive value", d.getReasons(aapsLogger))
}
@Test
@ -178,8 +179,8 @@ class SafetyPluginTest : TestBaseWithProfile() {
`when`(sp.getString(info.nightscout.core.utils.R.string.key_age, "")).thenReturn("child")
val i = Constraint(Constants.REALLYHIGHPERCENTBASALRATE)
safetyPlugin.applyBasalPercentConstraints(i, validProfile)
Assert.assertEquals(200, i.value())
Assert.assertEquals(
Assertions.assertEquals(200, i.value())
Assertions.assertEquals(
"""
Safety: Percent rate 1111111% recalculated to 11111.11 U/h with current basal 1.00 U/h
Safety: Limiting max basal rate to 2.00 U/h because of hard limit
@ -187,7 +188,7 @@ Safety: Limiting max percent rate to 200% because of pump limit
Safety: Limiting max basal rate to 500.00 U/h because of pump limit
""".trimIndent(), i.getReasons(aapsLogger)
)
Assert.assertEquals("Safety: Limiting max percent rate to 200% because of pump limit", i.getMostLimitedReasons(aapsLogger))
Assertions.assertEquals("Safety: Limiting max percent rate to 200% because of pump limit", i.getMostLimitedReasons(aapsLogger))
}
@Test
@ -200,15 +201,15 @@ Safety: Limiting max basal rate to 500.00 U/h because of pump limit
openAPSSMBPlugin.setPluginEnabled(PluginType.APS, true)
val i = Constraint(Constants.REALLYHIGHBASALRATE)
openAPSSMBPlugin.applyBasalConstraints(i, validProfile)
Assert.assertEquals(1.0, i.value(), 0.01)
Assert.assertEquals(
Assertions.assertEquals(1.0, i.value(), 0.01)
Assertions.assertEquals(
"""
OpenAPSSMB: Limiting max basal rate to 1.00 U/h because of max value in preferences
OpenAPSSMB: Limiting max basal rate to 4.00 U/h because of max basal multiplier
OpenAPSSMB: Limiting max basal rate to 3.00 U/h because of max daily basal multiplier
""".trimIndent(), i.getReasons(aapsLogger)
)
Assert.assertEquals("OpenAPSSMB: Limiting max basal rate to 1.00 U/h because of max value in preferences", i.getMostLimitedReasons(aapsLogger))
Assertions.assertEquals("OpenAPSSMB: Limiting max basal rate to 1.00 U/h because of max value in preferences", i.getMostLimitedReasons(aapsLogger))
}
@Test
@ -216,15 +217,15 @@ Safety: Limiting max basal rate to 500.00 U/h because of pump limit
`when`(sp.getString(info.nightscout.core.utils.R.string.key_age, "")).thenReturn("child")
val i = Constraint(-22)
safetyPlugin.applyBasalPercentConstraints(i, validProfile)
Assert.assertEquals(0, i.value())
Assert.assertEquals(
Assertions.assertEquals(0, i.value())
Assertions.assertEquals(
"""
Safety: Percent rate -22% recalculated to -0.22 U/h with current basal 1.00 U/h
Safety: Limiting max basal rate to 0.00 U/h because of it must be positive value
Safety: Limiting max percent rate to 0% because of pump limit
""".trimIndent(), i.getReasons(aapsLogger)
)
Assert.assertEquals("Safety: Limiting max percent rate to 0% because of pump limit", i.getMostLimitedReasons(aapsLogger))
Assertions.assertEquals("Safety: Limiting max percent rate to 0% because of pump limit", i.getMostLimitedReasons(aapsLogger))
}
@Test
@ -233,14 +234,14 @@ Safety: Limiting max basal rate to 500.00 U/h because of pump limit
`when`(sp.getString(info.nightscout.core.utils.R.string.key_age, "")).thenReturn("child")
var d = Constraint(Constants.REALLYHIGHBOLUS)
d = safetyPlugin.applyBolusConstraints(d)
Assert.assertEquals(3.0, d.value(), 0.01)
Assert.assertEquals(
Assertions.assertEquals(3.0, d.value(), 0.01)
Assertions.assertEquals(
"""
Safety: Limiting bolus to 3.0 U because of max value in preferences
Safety: Limiting bolus to 5.0 U because of hard limit
""".trimIndent(), d.getReasons(aapsLogger)
)
Assert.assertEquals("Safety: Limiting bolus to 3.0 U because of max value in preferences", d.getMostLimitedReasons(aapsLogger))
Assertions.assertEquals("Safety: Limiting bolus to 3.0 U because of max value in preferences", d.getMostLimitedReasons(aapsLogger))
}
@Test
@ -249,9 +250,9 @@ Safety: Limiting max basal rate to 500.00 U/h because of pump limit
`when`(sp.getString(info.nightscout.core.utils.R.string.key_age, "")).thenReturn("child")
var d = Constraint(-22.0)
d = safetyPlugin.applyBolusConstraints(d)
Assert.assertEquals(0.0, d.value(), 0.01)
Assert.assertEquals("Safety: Limiting bolus to 0.0 U because of it must be positive value", d.getReasons(aapsLogger))
Assert.assertEquals("Safety: Limiting bolus to 0.0 U because of it must be positive value", d.getMostLimitedReasons(aapsLogger))
Assertions.assertEquals(0.0, d.value(), 0.01)
Assertions.assertEquals("Safety: Limiting bolus to 0.0 U because of it must be positive value", d.getReasons(aapsLogger))
Assertions.assertEquals("Safety: Limiting bolus to 0.0 U because of it must be positive value", d.getMostLimitedReasons(aapsLogger))
}
@Test
@ -262,13 +263,13 @@ Safety: Limiting max basal rate to 500.00 U/h because of pump limit
// Negative carbs not allowed
var i = Constraint(-22)
safetyPlugin.applyCarbsConstraints(i)
Assert.assertEquals(0, i.value())
Assert.assertEquals("Safety: Limiting carbs to 0 g because of it must be positive value", i.getReasons(aapsLogger))
Assertions.assertEquals(0, i.value())
Assertions.assertEquals("Safety: Limiting carbs to 0 g because of it must be positive value", i.getReasons(aapsLogger))
// Apply all limits
i = safetyPlugin.applyCarbsConstraints(Constraint(Constants.REALLYHIGHCARBS))
Assert.assertEquals(48, i.value())
Assert.assertEquals("Safety: Limiting carbs to 48 g because of max value in preferences", i.getReasons(aapsLogger))
Assertions.assertEquals(48, i.value())
Assertions.assertEquals("Safety: Limiting carbs to 48 g because of max value in preferences", i.getReasons(aapsLogger))
}
@Test
@ -285,22 +286,22 @@ Safety: Limiting max basal rate to 500.00 U/h because of pump limit
// Apply all limits
var d = Constraint(Constants.REALLYHIGHIOB)
d = safetyPlugin.applyMaxIOBConstraints(d)
Assert.assertEquals(HardLimits.MAX_IOB_LGS, d.value(), 0.01)
Assert.assertEquals("Safety: Limiting IOB to 0.0 U because of Low Glucose Suspend", d.getReasons(aapsLogger))
Assert.assertEquals("Safety: Limiting IOB to 0.0 U because of Low Glucose Suspend", d.getMostLimitedReasons(aapsLogger))
Assertions.assertEquals(HardLimits.MAX_IOB_LGS, d.value(), 0.01)
Assertions.assertEquals("Safety: Limiting IOB to 0.0 U because of Low Glucose Suspend", d.getReasons(aapsLogger))
Assertions.assertEquals("Safety: Limiting IOB to 0.0 U because of Low Glucose Suspend", d.getMostLimitedReasons(aapsLogger))
// Apply all limits
d = Constraint(Constants.REALLYHIGHIOB)
val a = openAPSAMAPlugin.applyMaxIOBConstraints(d)
Assert.assertEquals(1.5, a.value(), 0.01)
Assert.assertEquals("OpenAPSAMA: Limiting IOB to 1.5 U because of max value in preferences\nOpenAPSAMA: Limiting IOB to 7.0 U because of hard limit", d.getReasons(aapsLogger))
Assert.assertEquals("OpenAPSAMA: Limiting IOB to 1.5 U because of max value in preferences", d.getMostLimitedReasons(aapsLogger))
Assertions.assertEquals(1.5, a.value(), 0.01)
Assertions.assertEquals("OpenAPSAMA: Limiting IOB to 1.5 U because of max value in preferences\nOpenAPSAMA: Limiting IOB to 7.0 U because of hard limit", d.getReasons(aapsLogger))
Assertions.assertEquals("OpenAPSAMA: Limiting IOB to 1.5 U because of max value in preferences", d.getMostLimitedReasons(aapsLogger))
// Apply all limits
d = Constraint(Constants.REALLYHIGHIOB)
val s = openAPSSMBPlugin.applyMaxIOBConstraints(d)
Assert.assertEquals(3.0, s.value(), 0.01)
Assert.assertEquals("OpenAPSSMB: Limiting IOB to 3.0 U because of max value in preferences\nOpenAPSSMB: Limiting IOB to 22.0 U because of hard limit", d.getReasons(aapsLogger))
Assert.assertEquals("OpenAPSSMB: Limiting IOB to 3.0 U because of max value in preferences", d.getMostLimitedReasons(aapsLogger))
Assertions.assertEquals(3.0, s.value(), 0.01)
Assertions.assertEquals("OpenAPSSMB: Limiting IOB to 3.0 U because of max value in preferences\nOpenAPSSMB: Limiting IOB to 22.0 U because of hard limit", d.getReasons(aapsLogger))
Assertions.assertEquals("OpenAPSSMB: Limiting IOB to 3.0 U because of max value in preferences", d.getMostLimitedReasons(aapsLogger))
}
}

View file

@ -6,7 +6,7 @@ import info.nightscout.androidaps.TestBaseWithProfile
import info.nightscout.core.pump.toHtml
import info.nightscout.interfaces.pump.PumpEnactResult
import info.nightscout.plugins.aps.loop.extensions.json
import info.nightscout.plugins.extensions.toText
import info.nightscout.pump.virtual.extensions.toText
import info.nightscout.plugins.sync.nsShared.extensions.log
import org.json.JSONObject
import org.junit.jupiter.api.Assertions

View file

@ -12,7 +12,7 @@ buildscript {
dagger_version = '2.44.2'
coroutines_version = '1.6.4'
activity_version = '1.6.1'
fragmentktx_version = '1.5.4'
fragmentktx_version = '1.5.5'
ormLite_version = '4.46'
gson_version = '2.10'
nav_version = '2.5.3'
@ -32,7 +32,7 @@ buildscript {
swipe_version = '1.1.0'
junit_version = '4.13.2'
junit_jupiter_version = '5.7.0'
junit_jupiter_version = '5.9.1'
mockito_version = '4.6.1'
dexmaker_version = '1.2'
retrofit2_version = '2.9.0'

View file

@ -1,4 +1,4 @@
package info.nightscout.plugins.insulin
package info.nightscout.core.graph
import android.content.Context
import android.graphics.Color

View file

@ -1,14 +0,0 @@
package info.nightscout.interfaces
interface BolusTimer {
/**
* Create new Automation event to alarm when is time to bolus
*/
fun scheduleAutomationEventBolusReminder()
/**
* Remove Automation event
*/
fun removeAutomationEventBolusReminder()
}

View file

@ -1,21 +0,0 @@
package info.nightscout.interfaces
interface CarbTimer {
/**
* Generate reminder via [info.nightscout.androidaps.utils.TimerUtil]
*
* @param seconds seconds to the future
*/
fun scheduleTimeToEatReminder(seconds: Int)
/**
* Create new Automation event to alarm when is time to eat
*/
fun scheduleAutomationEventEatReminder()
/**
* Remove Automation event
*/
fun removeAutomationEventEatReminder()
}

View file

@ -0,0 +1,3 @@
package info.nightscout.interfaces.actions
interface Actions

View file

@ -8,6 +8,7 @@ import info.nightscout.interfaces.iob.IobTotal
import org.json.JSONObject
interface APSResult {
var date: Long
var json: JSONObject?
var reason: String
var rate: Double

View file

@ -18,8 +18,13 @@ interface AutosensData {
var pastSensitivity: String
var deviation: Double
var validDeviation: Boolean
var activeCarbsList: MutableList<CarbsInPast>
var absorbed: Double
var carbsFromBolus: Double
var cob: Double
var bgi: Double
var delta: Double
var avgDelta: Double
var slopeFromMaxDeviation: Double
var slopeFromMinDeviation: Double
var usedMinCarbsImpact: Double
@ -37,4 +42,6 @@ interface AutosensData {
var autosensResult: AutosensResult
fun cloneCarbsList(): MutableList<CarbsInPast>
fun deductAbsorbedCarbs()
fun removeOldCarbs(toTime: Long, isAAPSOrWeighted: Boolean)
}

View file

@ -1,6 +1,36 @@
package info.nightscout.interfaces.automation
interface Automation {
fun userEvents(): List<AutomationEvent>
fun processEvent(someEvent: AutomationEvent)
/**
* Generate reminder via [info.nightscout.interfaces.utils.TimerUtil]
*
*/
fun scheduleAutomationEventBolusReminder()
/**
* Remove scheduled reminder from automations
*
*/
fun removeAutomationEventBolusReminder()
/**
* Generate reminder via [info.nightscout.interfaces.utils.TimerUtil]
*
* @param seconds seconds to the future
*/
fun scheduleTimeToEatReminder(seconds: Int)
/**
* Remove Automation event
*/
fun removeAutomationEventEatReminder()
/**
* Create new Automation event to alarm when is time to eat
*/
fun scheduleAutomationEventEatReminder()
}

View file

@ -5,6 +5,7 @@ import info.nightscout.database.ValueWrapper
import info.nightscout.database.entities.Bolus
import info.nightscout.database.entities.BolusCalculatorResult
import info.nightscout.database.entities.Carbs
import info.nightscout.database.entities.EffectiveProfileSwitch
import info.nightscout.database.entities.TemporaryTarget
import info.nightscout.database.entities.UserEntry
import info.nightscout.interfaces.queue.Callback
@ -20,4 +21,5 @@ interface PersistenceLayer {
fun getTemporaryTargetActiveAt(timestamp: Long): Single<ValueWrapper<TemporaryTarget>>
fun getUserEntryFilteredDataFromTime(timestamp: Long): Single<List<UserEntry>>
fun getEffectiveProfileSwitchActiveAt(timestamp: Long): Single<ValueWrapper<EffectiveProfileSwitch>>
}

View file

@ -0,0 +1,6 @@
package info.nightscout.interfaces.iob
interface GlucoseStatusProvider {
val glucoseStatusData: GlucoseStatus?
fun getGlucoseStatusData(allowOldData: Boolean = false): GlucoseStatus?
}

View file

@ -1,7 +1,48 @@
package info.nightscout.interfaces.nsclient
import info.nightscout.database.entities.Bolus
import info.nightscout.database.entities.BolusCalculatorResult
import info.nightscout.database.entities.Carbs
import info.nightscout.database.entities.DeviceStatus
import info.nightscout.database.entities.EffectiveProfileSwitch
import info.nightscout.database.entities.ExtendedBolus
import info.nightscout.database.entities.Food
import info.nightscout.database.entities.GlucoseValue
import info.nightscout.database.entities.OfflineEvent
import info.nightscout.database.entities.ProfileSwitch
import info.nightscout.database.entities.TemporaryBasal
import info.nightscout.database.entities.TemporaryTarget
import info.nightscout.database.entities.TherapyEvent
import info.nightscout.database.transactions.TransactionGlucoseValue
interface StoreDataForDb {
val glucoseValues: MutableList<TransactionGlucoseValue>
val boluses: MutableList<Bolus>
val carbs: MutableList<Carbs>
val temporaryTargets: MutableList<TemporaryTarget>
val effectiveProfileSwitches: MutableList<EffectiveProfileSwitch>
val bolusCalculatorResults: MutableList<BolusCalculatorResult>
val therapyEvents: MutableList<TherapyEvent>
val extendedBoluses: MutableList<ExtendedBolus>
val temporaryBasals: MutableList<TemporaryBasal>
val profileSwitches: MutableList<ProfileSwitch>
val offlineEvents: MutableList<OfflineEvent>
val nsIdGlucoseValues: MutableList<GlucoseValue>
val nsIdBoluses: MutableList<Bolus>
val nsIdCarbs: MutableList<Carbs>
val nsIdFoods: MutableList<Food>
val nsIdTemporaryTargets: MutableList<TemporaryTarget>
val nsIdEffectiveProfileSwitches: MutableList<EffectiveProfileSwitch>
val nsIdBolusCalculatorResults: MutableList<BolusCalculatorResult>
val nsIdTherapyEvents: MutableList<TherapyEvent>
val nsIdExtendedBoluses: MutableList<ExtendedBolus>
val nsIdTemporaryBasals: MutableList<TemporaryBasal>
val nsIdProfileSwitches: MutableList<ProfileSwitch>
val nsIdOfflineEvents: MutableList<OfflineEvent>
val nsIdDeviceStatuses: MutableList<DeviceStatus>
fun storeTreatmentsToDb()
fun storeGlucoseValuesToDb()
fun scheduleNsIdUpdate()
}

View file

@ -0,0 +1,12 @@
package info.nightscout.interfaces.profile
import info.nightscout.interfaces.aps.APSResult
import info.nightscout.interfaces.aps.AutosensData
import org.json.JSONObject
interface Instantiator {
fun provideProfileStore(jsonObject: JSONObject): ProfileStore
fun provideAPSResultObject(): APSResult
fun provideAutosensDataObject(): AutosensData
}

View file

@ -1,7 +0,0 @@
package info.nightscout.interfaces.profile
import org.json.JSONObject
interface ProfileInstantiator {
fun storeInstance(jsonObject: JSONObject): ProfileStore
}

View file

@ -1,3 +1,8 @@
package info.nightscout.interfaces.source
interface DexcomBoyda
interface DexcomBoyda {
fun isEnabled(): Boolean
fun requestPermissionIfNeeded()
fun findDexcomPackageName(): String?
}

View file

@ -0,0 +1,5 @@
package info.nightscout.interfaces.source
interface XDrip {
fun isEnabled(): Boolean
}

View file

@ -17,19 +17,24 @@ import org.json.JSONObject
interface DataSyncSelector {
data class PairTemporaryTarget(val value: TemporaryTarget, val updateRecordId: Long)
data class PairGlucoseValue(val value: GlucoseValue, val updateRecordId: Long)
data class PairTherapyEvent(val value: TherapyEvent, val updateRecordId: Long)
data class PairFood(val value: Food, val updateRecordId: Long)
data class PairBolus(val value: Bolus, val updateRecordId: Long)
data class PairCarbs(val value: Carbs, val updateRecordId: Long)
data class PairBolusCalculatorResult(val value: BolusCalculatorResult, val updateRecordId: Long)
data class PairTemporaryBasal(val value: TemporaryBasal, val updateRecordId: Long)
data class PairExtendedBolus(val value: ExtendedBolus, val updateRecordId: Long)
data class PairProfileSwitch(val value: ProfileSwitch, val updateRecordId: Long)
data class PairEffectiveProfileSwitch(val value: EffectiveProfileSwitch, val updateRecordId: Long)
data class PairOfflineEvent(val value: OfflineEvent, val updateRecordId: Long)
data class PairProfileStore(val value: JSONObject, val timestampSync: Long)
interface DataPair {
val value: Any
val id: Long
}
data class PairTemporaryTarget(override val value: TemporaryTarget, override val id: Long): DataPair
data class PairGlucoseValue(override val value: GlucoseValue, override val id: Long): DataPair
data class PairTherapyEvent(override val value: TherapyEvent, override val id: Long): DataPair
data class PairFood(override val value: Food, override val id: Long): DataPair
data class PairBolus(override val value: Bolus, override val id: Long): DataPair
data class PairCarbs(override val value: Carbs, override val id: Long): DataPair
data class PairBolusCalculatorResult(override val value: BolusCalculatorResult, override val id: Long): DataPair
data class PairTemporaryBasal(override val value: TemporaryBasal, override val id: Long): DataPair
data class PairExtendedBolus(override val value: ExtendedBolus, override val id: Long): DataPair
data class PairProfileSwitch(override val value: ProfileSwitch, override val id: Long): DataPair
data class PairEffectiveProfileSwitch(override val value: EffectiveProfileSwitch, override val id: Long): DataPair
data class PairOfflineEvent(override val value: OfflineEvent, override val id: Long): DataPair
data class PairProfileStore(override val value: JSONObject, override val id: Long): DataPair
data class PairDeviceStatus(override val value: DeviceStatus, override val id: Long): DataPair
fun queueSize(): Long
@ -38,81 +43,42 @@ interface DataSyncSelector {
fun resetToNextFullSync()
fun confirmLastBolusIdIfGreater(lastSynced: Long)
fun changedBoluses(): List<Bolus>
// Until NS v3
fun processChangedBolusesCompat()
fun confirmLastCarbsIdIfGreater(lastSynced: Long)
fun changedCarbs(): List<Carbs>
// Until NS v3
fun processChangedCarbsCompat()
fun confirmLastBolusCalculatorResultsIdIfGreater(lastSynced: Long)
fun changedBolusCalculatorResults(): List<BolusCalculatorResult>
// Until NS v3
fun processChangedBolusCalculatorResultsCompat()
fun confirmLastTempTargetsIdIfGreater(lastSynced: Long)
fun changedTempTargets(): List<TemporaryTarget>
// Until NS v3
fun processChangedTempTargetsCompat()
fun confirmLastGlucoseValueIdIfGreater(lastSynced: Long)
fun changedGlucoseValues(): List<GlucoseValue>
// Until NS v3
fun processChangedGlucoseValuesCompat()
fun confirmLastTherapyEventIdIfGreater(lastSynced: Long)
fun changedTherapyEvents(): List<TherapyEvent>
// Until NS v3
fun processChangedTherapyEventsCompat()
fun confirmLastFoodIdIfGreater(lastSynced: Long)
fun changedFoods(): List<Food>
// Until NS v3
fun processChangedFoodsCompat()
fun confirmLastDeviceStatusIdIfGreater(lastSynced: Long)
fun changedDeviceStatuses(): List<DeviceStatus>
// Until NS v3
fun processChangedDeviceStatusesCompat()
fun confirmLastTemporaryBasalIdIfGreater(lastSynced: Long)
fun changedTemporaryBasals(): List<TemporaryBasal>
// Until NS v3
fun processChangedTemporaryBasalsCompat()
fun confirmLastExtendedBolusIdIfGreater(lastSynced: Long)
fun changedExtendedBoluses(): List<ExtendedBolus>
// Until NS v3
fun processChangedExtendedBolusesCompat()
fun confirmLastProfileSwitchIdIfGreater(lastSynced: Long)
fun changedProfileSwitch(): List<ProfileSwitch>
// Until NS v3
fun processChangedProfileSwitchesCompat()
fun confirmLastEffectiveProfileSwitchIdIfGreater(lastSynced: Long)
fun changedEffectiveProfileSwitch(): List<EffectiveProfileSwitch>
// Until NS v3
fun processChangedEffectiveProfileSwitchesCompat()
fun confirmLastOfflineEventIdIfGreater(lastSynced: Long)
fun changedOfflineEvents(): List<OfflineEvent>
// Until NS v3
fun processChangedOfflineEventsCompat()
fun confirmLastProfileStore(lastSynced: Long)

View file

@ -2,7 +2,6 @@ package info.nightscout.interfaces.sync
import android.text.Spanned
import info.nightscout.interfaces.nsclient.NSAlarm
import org.json.JSONObject
interface NsClient : Sync {
enum class Version {
@ -11,7 +10,6 @@ interface NsClient : Sync {
val version: Version
val address: String
val nsClientService: NSClientService?
fun pause(newState: Boolean)
fun resend(reason: String)
@ -24,9 +22,6 @@ interface NsClient : Sync {
fun resetToFullSync()
interface NSClientService {
fun dbAdd(collection: String, data: JSONObject, originalObject: Any, progress: String)
fun dbUpdate(collection: String, _id: String?, data: JSONObject?, originalObject: Any, progress: String)
}
fun dbAdd(collection: String, dataPair: DataSyncSelector.DataPair, progress: String)
fun dbUpdate(collection: String, dataPair: DataSyncSelector.DataPair, progress: String)
}

View file

@ -31,6 +31,9 @@ interface UiInteraction {
* @param soundId sound resource. if == 0 alarm is not started
*/
fun runAlarm(status: String, title: String, @RawRes soundId: Int = 0)
fun updateWidget(context: Context)
fun runWizardDialog(fragmentManager: FragmentManager, carbs: Int? = null, name: String? = null)
fun runLoopDialog(fragmentManager: FragmentManager, showOkCancel: Int)
fun runProfileSwitchDialog(fragmentManager: FragmentManager, profileName: String? = null)

View file

@ -2,8 +2,6 @@ package info.nightscout.core.di
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.core.aps.APSResultObject
import info.nightscout.core.iob.iobCobCalculator.data.AutosensDataObject
import info.nightscout.core.wizard.BolusWizard
import info.nightscout.core.wizard.QuickWizardEntry
import info.nightscout.interfaces.pump.PumpEnactResult
@ -13,8 +11,6 @@ import info.nightscout.interfaces.pump.PumpEnactResult
abstract class CoreDataClassesModule {
@ContributesAndroidInjector abstract fun pumpEnactResultInjector(): PumpEnactResult
@ContributesAndroidInjector abstract fun apsResultInjector(): APSResultObject
@ContributesAndroidInjector abstract fun autosensDataInjector(): AutosensDataObject
@ContributesAndroidInjector abstract fun bolusWizardInjector(): BolusWizard
@ContributesAndroidInjector abstract fun quickWizardEntryInjector(): QuickWizardEntry
}

View file

@ -5,14 +5,11 @@ import android.os.Build
import android.telephony.SmsManager
import dagger.Module
import dagger.Provides
import dagger.android.ContributesAndroidInjector
import info.nightscout.core.services.AlarmSoundService
@Module(
includes = [
CoreDataClassesModule::class,
PreferencesModule::class,
ServicesModule::class
PreferencesModule::class
]
)
open class CoreModule {

View file

@ -1,12 +0,0 @@
package info.nightscout.core.di
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.core.services.AlarmSoundService
@Module
@Suppress("unused")
abstract class ServicesModule {
@ContributesAndroidInjector abstract fun contributesAlarmSoundService(): AlarmSoundService
}

View file

@ -1,2 +0,0 @@
package info.nightscout.core.profile

View file

@ -6,7 +6,6 @@ import com.google.common.base.Joiner
import dagger.android.HasAndroidInjector
import info.nightscout.core.extensions.highValueToUnitsToString
import info.nightscout.core.extensions.lowValueToUnitsToString
import info.nightscout.core.iob.iobCobCalculator.GlucoseStatusProvider
import info.nightscout.core.iob.round
import info.nightscout.core.ui.dialogs.OKDialog
import info.nightscout.core.utils.extensions.formatColor
@ -16,14 +15,14 @@ import info.nightscout.database.entities.TemporaryTarget
import info.nightscout.database.entities.UserEntry.Action
import info.nightscout.database.entities.UserEntry.Sources
import info.nightscout.database.entities.ValueWithUnit
import info.nightscout.interfaces.BolusTimer
import info.nightscout.interfaces.CarbTimer
import info.nightscout.interfaces.Config
import info.nightscout.interfaces.aps.Loop
import info.nightscout.interfaces.automation.Automation
import info.nightscout.interfaces.constraints.Constraint
import info.nightscout.interfaces.constraints.Constraints
import info.nightscout.interfaces.db.PersistenceLayer
import info.nightscout.interfaces.iob.GlucoseStatus
import info.nightscout.interfaces.iob.GlucoseStatusProvider
import info.nightscout.interfaces.iob.IobCobCalculator
import info.nightscout.interfaces.logging.UserEntryLogger
import info.nightscout.interfaces.plugin.ActivePlugin
@ -69,8 +68,7 @@ class BolusWizard @Inject constructor(
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var config: Config
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var carbTimer: CarbTimer
@Inject lateinit var bolusTimer: BolusTimer
@Inject lateinit var automation: Automation
@Inject lateinit var glucoseStatusProvider: GlucoseStatusProvider
@Inject lateinit var uiInteraction: UiInteraction
@Inject lateinit var persistenceLayer: PersistenceLayer
@ -361,9 +359,9 @@ class BolusWizard @Inject constructor(
}
accepted = true
if (calculatedTotalInsulin > 0.0)
bolusTimer.removeAutomationEventBolusReminder()
automation.removeAutomationEventBolusReminder()
if (carbs > 0.0)
carbTimer.removeAutomationEventEatReminder()
automation.removeAutomationEventEatReminder()
if (sp.getBoolean(info.nightscout.core.ui.R.string.key_usebolusadvisor, false) && Profile.toMgdl(bg, profile.units) > 180 && carbs > 0 && carbTime >= 0)
OKDialog.showYesNoCancel(ctx, rh.gs(info.nightscout.core.ui.R.string.bolus_advisor), rh.gs(info.nightscout.core.ui.R.string.bolus_advisor_message),
{ bolusAdvisorProcessing(ctx) },
@ -402,7 +400,7 @@ class BolusWizard @Inject constructor(
if (!result.success) {
uiInteraction.runAlarm(result.comment, rh.gs(info.nightscout.core.ui.R.string.treatmentdeliveryerror), info.nightscout.core.ui.R.raw.boluserror)
} else
carbTimer.scheduleAutomationEventEatReminder()
automation.scheduleAutomationEventEatReminder()
}
})
}
@ -494,7 +492,7 @@ class BolusWizard @Inject constructor(
bolusCalculatorResult?.let { persistenceLayer.insertOrUpdate(it) }
}
if (useAlarm && carbs > 0 && carbTime > 0) {
carbTimer.scheduleTimeToEatReminder(T.mins(carbTime.toLong()).secs().toInt())
automation.scheduleTimeToEatReminder(T.mins(carbTime.toLong()).secs().toInt())
}
}
})

View file

@ -3,13 +3,13 @@ package info.nightscout.core.wizard
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.core.extensions.valueToUnits
import info.nightscout.core.iob.iobCobCalculator.GlucoseStatusProvider
import info.nightscout.core.iob.round
import info.nightscout.core.utils.MidnightUtils
import info.nightscout.database.ValueWrapper
import info.nightscout.database.entities.GlucoseValue
import info.nightscout.interfaces.aps.Loop
import info.nightscout.interfaces.db.PersistenceLayer
import info.nightscout.interfaces.iob.GlucoseStatusProvider
import info.nightscout.interfaces.iob.IobCobCalculator
import info.nightscout.interfaces.plugin.PluginBase
import info.nightscout.interfaces.profile.Profile

View file

@ -1,107 +0,0 @@
package info.nightscout.androidaps
import androidx.collection.ArrayMap
import dagger.android.HasAndroidInjector
import info.nightscout.core.extensions.pureProfileFromJson
import info.nightscout.core.profile.ProfileSealed
import info.nightscout.interfaces.Config
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.profile.ProfileStore
import info.nightscout.interfaces.profile.PureProfile
import info.nightscout.interfaces.utils.HardLimits
import info.nightscout.interfaces.utils.JsonHelper
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.utils.DateUtil
import org.json.JSONException
import org.json.JSONObject
import javax.inject.Inject
class ProfileStoreObject(val injector: HasAndroidInjector, override val data: JSONObject, val dateUtil: DateUtil) : ProfileStore {
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var config: Config
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var rxBus: RxBus
@Inject lateinit var hardLimits: HardLimits
init {
injector.androidInjector().inject(this)
}
private val cachedObjects = ArrayMap<String, PureProfile>()
private fun storeUnits(): String? = JsonHelper.safeGetStringAllowNull(data, "units", null)
private fun getStore(): JSONObject? {
try {
if (data.has("store")) return data.getJSONObject("store")
} catch (e: JSONException) {
aapsLogger.error("Unhandled exception", e)
}
return null
}
override fun getStartDate(): Long {
val iso = JsonHelper.safeGetString(data, "startDate") ?: return 0
return try {
dateUtil.fromISODateString(iso)
} catch (e: Exception) {
0
}
}
override fun getDefaultProfile(): PureProfile? = getDefaultProfileName()?.let { getSpecificProfile(it) }
override fun getDefaultProfileJson(): JSONObject? = getDefaultProfileName()?.let { getSpecificProfileJson(it) }
override fun getDefaultProfileName(): String? {
val defaultProfileName = data.optString("defaultProfile")
return if (defaultProfileName.isNotEmpty()) getStore()?.has(defaultProfileName)?.let { defaultProfileName } else null
}
override fun getProfileList(): ArrayList<CharSequence> {
val ret = ArrayList<CharSequence>()
getStore()?.keys()?.let { keys ->
while (keys.hasNext()) {
val profileName = keys.next() as String
ret.add(profileName)
}
}
return ret
}
@Synchronized
override fun getSpecificProfile(profileName: String): PureProfile? {
var profile: PureProfile? = null
val units = JsonHelper.safeGetStringAllowNull(data, "units", storeUnits())
getStore()?.let { store ->
if (store.has(profileName)) {
profile = cachedObjects[profileName]
if (profile == null) {
JsonHelper.safeGetJSONObject(store, profileName, null)?.let { profileObject ->
profile = pureProfileFromJson(profileObject, dateUtil, units)
profile?.let { cachedObjects[profileName] = profile }
}
}
}
}
return profile
}
private fun getSpecificProfileJson(profileName: String): JSONObject? {
getStore()?.let { store ->
if (store.has(profileName))
return JsonHelper.safeGetJSONObject(store, profileName, null)
}
return null
}
override val allProfilesValid: Boolean
get() = getProfileList()
.asSequence()
.map { profileName -> getSpecificProfile(profileName.toString()) }
.map { pureProfile -> pureProfile?.let { ProfileSealed.Pure(pureProfile).isValid("allProfilesValid", activePlugin.activePump, config, rh, rxBus, hardLimits, false) } }
.all { it?.isValid == true }
}

View file

@ -10,7 +10,6 @@ import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.profile.DefaultValueHelper
import info.nightscout.interfaces.profile.Profile
import info.nightscout.interfaces.profile.ProfileFunction
import info.nightscout.interfaces.profile.ProfileStore
import info.nightscout.interfaces.utils.HardLimits
import info.nightscout.rx.bus.RxBus
import info.nightscout.shared.interfaces.ResourceHelper
@ -38,18 +37,7 @@ open class TestBaseWithProfile : TestBase() {
val rxBus = RxBus(aapsSchedulers, aapsLogger)
val profileInjector = HasAndroidInjector {
AndroidInjector {
if (it is ProfileStoreObject) {
it.aapsLogger = aapsLogger
it.activePlugin = activePluginProvider
it.config = config
it.rh = rh
it.rxBus = rxBus
it.hardLimits = hardLimits
}
}
}
val profileInjector = HasAndroidInjector { AndroidInjector { } }
private lateinit var invalidProfileJSON: String
private lateinit var validProfileJSON: String
@ -70,32 +58,4 @@ open class TestBaseWithProfile : TestBase() {
`when`(activePluginProvider.activePump).thenReturn(testPumpPlugin)
hardLimits = HardLimitsMock(sp, rh)
}
fun getValidProfileStore(): ProfileStore {
val json = JSONObject()
val store = JSONObject()
store.put(TESTPROFILENAME, JSONObject(validProfileJSON))
json.put("defaultProfile", TESTPROFILENAME)
json.put("store", store)
return ProfileStoreObject(profileInjector, json, dateUtil)
}
fun getInvalidProfileStore1(): ProfileStore {
val json = JSONObject()
val store = JSONObject()
store.put(TESTPROFILENAME, JSONObject(invalidProfileJSON))
json.put("defaultProfile", TESTPROFILENAME)
json.put("store", store)
return ProfileStoreObject(profileInjector, json, dateUtil)
}
fun getInvalidProfileStore2(): ProfileStore {
val json = JSONObject()
val store = JSONObject()
store.put(TESTPROFILENAME, JSONObject(validProfileJSON))
store.put("invalid", JSONObject(invalidProfileJSON))
json.put("defaultProfile", TESTPROFILENAME + "invalid")
json.put("store", store)
return ProfileStoreObject(profileInjector, json, dateUtil)
}
}

View file

@ -1,316 +0,0 @@
package info.nightscout.androidaps.plugins.iob.iobCobCalculator
import android.content.Context
import info.nightscout.androidaps.TestBase
import info.nightscout.core.iob.iobCobCalculator.AutosensDataStoreObject
import info.nightscout.database.entities.GlucoseValue
import info.nightscout.shared.utils.DateUtil
import info.nightscout.shared.utils.T
import org.junit.Assert
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.mockito.Mock
class AutosensDataStoreTest : TestBase() {
@Mock lateinit var context: Context
lateinit var dateUtil: DateUtil
private val autosensDataStore = AutosensDataStoreObject()
@BeforeEach
fun mock() {
dateUtil = DateUtil(context)
}
@Test
fun isAbout5minDataTest() {
val bgReadingList: MutableList<GlucoseValue> = ArrayList()
// Super data should not be touched
bgReadingList.clear()
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(20).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(15).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(10).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(5).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
autosensDataStore.bgReadings = bgReadingList
Assert.assertEquals(true, autosensDataStore.isAbout5minData(aapsLogger))
// too much shifted data should return false
bgReadingList.clear()
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(20).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(15).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(9).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(5).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
autosensDataStore.bgReadings = bgReadingList
Assert.assertEquals(false, autosensDataStore.isAbout5minData(aapsLogger))
// too much shifted and missing data should return false
bgReadingList.clear()
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(20).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(9).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(5).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
autosensDataStore.bgReadings = bgReadingList
Assert.assertEquals(false, autosensDataStore.isAbout5minData(aapsLogger))
// too much shifted and missing data should return false
bgReadingList.clear()
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(83).plus(T.secs(40)).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(78).plus(T.secs(40)).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(73).plus(T.secs(40)).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(68).plus(T.secs(40)).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(63).plus(T.secs(40)).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(58).plus(T.secs(40)).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(53).plus(T.secs(40)).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(48).plus(T.secs(40)).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(43).plus(T.secs(40)).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(38).plus(T.secs(40)).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(33).plus(T.secs(1)).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(28).plus(T.secs(0)).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(23).plus(T.secs(0)).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(16).plus(T.secs(36)).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
autosensDataStore.bgReadings = bgReadingList
Assert.assertEquals(false, autosensDataStore.isAbout5minData(aapsLogger))
// slightly shifted data should return true
bgReadingList.clear()
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(20).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(15).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(10).plus(T.secs(10)).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(5).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
autosensDataStore.bgReadings = bgReadingList
Assert.assertEquals(true, autosensDataStore.isAbout5minData(aapsLogger))
// slightly shifted and missing data should return true
bgReadingList.clear()
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(20).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(10).plus(T.secs(10)).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(5).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
autosensDataStore.bgReadings = bgReadingList
Assert.assertEquals(true, autosensDataStore.isAbout5minData(aapsLogger))
}
@Test
fun createBucketedData5minTest1() {
val bgReadingList: MutableList<GlucoseValue> = ArrayList()
// Super data should not be touched
bgReadingList.clear()
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(20).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(15).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(10).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(5).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
autosensDataStore.bgReadings = bgReadingList
Assert.assertEquals(true, autosensDataStore.isAbout5minData(aapsLogger))
autosensDataStore.createBucketedData(aapsLogger, dateUtil)
Assert.assertEquals(bgReadingList[0].timestamp, autosensDataStore.bucketedData!![0].timestamp)
Assert.assertEquals(bgReadingList[3].timestamp, autosensDataStore.bucketedData!![3].timestamp)
Assert.assertEquals(bgReadingList.size.toLong(), autosensDataStore.bucketedData!!.size.toLong())
// Missing value should be replaced
bgReadingList.clear()
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(20).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(10).plus(T.secs(10)).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(5).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
autosensDataStore.bgReadings = bgReadingList
Assert.assertEquals(true, autosensDataStore.isAbout5minData(aapsLogger))
autosensDataStore.createBucketedData(aapsLogger, dateUtil)
Assert.assertEquals(bgReadingList[0].timestamp, autosensDataStore.bucketedData!![0].timestamp)
Assert.assertEquals(bgReadingList[2].timestamp, autosensDataStore.bucketedData!![3].timestamp)
Assert.assertEquals(bgReadingList.size + 1.toLong(), autosensDataStore.bucketedData!!.size.toLong())
// drift should be cleared
bgReadingList.clear()
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(20).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(15).msecs() + T.secs(10).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(10).msecs() + T.secs(10).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(5).msecs() + T.secs(10).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(0).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
autosensDataStore.bgReadings = bgReadingList
Assert.assertEquals(true, autosensDataStore.isAbout5minData(aapsLogger))
autosensDataStore.createBucketedData(aapsLogger, dateUtil)
Assert.assertEquals(T.mins(20).msecs(), autosensDataStore.bucketedData!![0].timestamp)
Assert.assertEquals(T.mins(15).msecs(), autosensDataStore.bucketedData!![1].timestamp)
Assert.assertEquals(T.mins(10).msecs(), autosensDataStore.bucketedData!![2].timestamp)
Assert.assertEquals(T.mins(5).msecs(), autosensDataStore.bucketedData!![3].timestamp)
Assert.assertEquals(bgReadingList.size.toLong(), autosensDataStore.bucketedData!!.size.toLong())
// bucketed data should return null if not enough bg data
bgReadingList.clear()
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(30).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(5).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
autosensDataStore.bgReadings = bgReadingList
Assert.assertEquals(true, autosensDataStore.isAbout5minData(aapsLogger))
autosensDataStore.createBucketedData(aapsLogger, dateUtil)
Assert.assertEquals(null, autosensDataStore.bucketedData)
// data should be reconstructed
bgReadingList.clear()
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(50).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 90.0, timestamp = T.mins(45).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 40.0, timestamp = T.mins(20).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
autosensDataStore.bgReadings = bgReadingList
Assert.assertEquals(true, autosensDataStore.isAbout5minData(aapsLogger))
autosensDataStore.createBucketedData(aapsLogger, dateUtil)
Assert.assertEquals(T.mins(50).msecs(), autosensDataStore.bucketedData!![0].timestamp)
Assert.assertEquals(T.mins(20).msecs(), autosensDataStore.bucketedData!![6].timestamp)
Assert.assertEquals(7, autosensDataStore.bucketedData!!.size.toLong())
Assert.assertEquals(100.0, autosensDataStore.bucketedData!![0].value, 1.0)
Assert.assertEquals(90.0, autosensDataStore.bucketedData!![1].value, 1.0)
Assert.assertEquals(50.0, autosensDataStore.bucketedData!![5].value, 1.0)
Assert.assertEquals(40.0, autosensDataStore.bucketedData!![6].value, 1.0)
// non 5min data should be reconstructed
bgReadingList.clear()
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(50).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 96.0, timestamp = T.mins(48).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 40.0, timestamp = T.mins(20).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
autosensDataStore.bgReadings = bgReadingList
Assert.assertEquals(false, autosensDataStore.isAbout5minData(aapsLogger))
autosensDataStore.createBucketedData(aapsLogger, dateUtil)
Assert.assertEquals(T.mins(50).msecs(), autosensDataStore.bucketedData!![0].timestamp)
Assert.assertEquals(T.mins(20).msecs(), autosensDataStore.bucketedData!![6].timestamp)
Assert.assertEquals(7, autosensDataStore.bucketedData!!.size.toLong())
Assert.assertEquals(100.0, autosensDataStore.bucketedData!![0].value, 1.0)
Assert.assertEquals(90.0, autosensDataStore.bucketedData!![1].value, 1.0)
Assert.assertEquals(50.0, autosensDataStore.bucketedData!![5].value, 1.0)
Assert.assertEquals(40.0, autosensDataStore.bucketedData!![6].value, 1.0)
}
@Test
fun createBucketedData5minTest2() {
val bgReadingList: MutableList<GlucoseValue> = ArrayList()
//bucketed data should be null if no bg data available
autosensDataStore.bgReadings = ArrayList()
autosensDataStore.createBucketedData(aapsLogger, dateUtil)
Assert.assertEquals(null, autosensDataStore.bucketedData)
// real data gap test
bgReadingList.clear()
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T13:34:55Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T13:14:55Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T13:09:55Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T13:04:55Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T12:59:55Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T12:54:55Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T12:49:55Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T12:44:55Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T12:39:55Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T12:34:55Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T12:29:56Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T12:24:55Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T12:19:56Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T12:14:56Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T12:09:56Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T12:04:56Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T11:59:55Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T04:29:57Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T04:24:56Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T04:19:57Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T04:14:57Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T04:10:03Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T04:04:56Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T03:59:56Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T03:54:56Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T03:50:03Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-09-05T03:44:57Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
autosensDataStore.bgReadings = bgReadingList
autosensDataStore.referenceTime = -1
Assert.assertEquals(true, autosensDataStore.isAbout5minData(aapsLogger))
autosensDataStore.createBucketedData(aapsLogger, dateUtil)
Assert.assertEquals(dateUtil.fromISODateString("2018-09-05T13:34:57Z"), autosensDataStore.bucketedData!![0].timestamp)
Assert.assertEquals(dateUtil.fromISODateString("2018-09-05T03:44:57Z"), autosensDataStore.bucketedData!![autosensDataStore.bucketedData!!.size - 1].timestamp)
// 5min 4sec data
bgReadingList.clear()
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-10-05T06:33:40Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-10-05T06:28:36Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-10-05T06:23:32Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-10-05T06:18:28Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-10-05T06:13:24Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-10-05T06:08:19Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-10-05T06:03:16Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-10-05T05:58:11Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-10-05T05:53:07Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-10-05T05:48:03Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-10-05T05:42:58Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-10-05T05:37:54Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-10-05T05:32:51Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-10-05T05:27:46Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-10-05T05:22:42Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-10-05T05:17:38Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-10-05T05:12:33Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-10-05T05:07:29Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-10-05T05:02:26Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-10-05T04:57:21Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = dateUtil.fromISODateString("2018-10-05T04:52:17Z"), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
autosensDataStore.bgReadings = bgReadingList
Assert.assertEquals(false, autosensDataStore.isAbout5minData(aapsLogger))
}
@Test
fun bgReadingsTest() {
val bgReadingList: List<GlucoseValue> = ArrayList()
autosensDataStore.bgReadings = bgReadingList
Assert.assertEquals(bgReadingList, autosensDataStore.bgReadings)
}
@Test
fun roundUpTimeTest() {
Assert.assertEquals(T.mins(3).msecs(), autosensDataStore.roundUpTime(T.secs(155).msecs()))
}
@Test
fun findNewerTest() {
val bgReadingList: MutableList<GlucoseValue> = ArrayList()
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(20).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(15).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(10).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(5).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
autosensDataStore.bgReadings = bgReadingList
Assert.assertEquals(T.mins(10).msecs(), autosensDataStore.findNewer(T.mins(8).msecs())!!.timestamp)
Assert.assertEquals(T.mins(5).msecs(), autosensDataStore.findNewer(T.mins(5).msecs())!!.timestamp)
Assert.assertEquals(T.mins(10).msecs(), autosensDataStore.findNewer(T.mins(10).msecs())!!.timestamp)
Assert.assertEquals(T.mins(20).msecs(), autosensDataStore.findNewer(T.mins(20).msecs())!!.timestamp)
Assert.assertEquals(null, autosensDataStore.findNewer(T.mins(22).msecs()))
}
@Test
fun findOlderTest() {
val bgReadingList: MutableList<GlucoseValue> = ArrayList()
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(20).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(15).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(10).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(5).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
autosensDataStore.bgReadings = bgReadingList
Assert.assertEquals(T.mins(5).msecs(), autosensDataStore.findOlder(T.mins(8).msecs())!!.timestamp)
Assert.assertEquals(T.mins(5).msecs(), autosensDataStore.findOlder(T.mins(5).msecs())!!.timestamp)
Assert.assertEquals(T.mins(10).msecs(), autosensDataStore.findOlder(T.mins(10).msecs())!!.timestamp)
Assert.assertEquals(T.mins(20).msecs(), autosensDataStore.findOlder(T.mins(20).msecs())!!.timestamp)
Assert.assertEquals(null, autosensDataStore.findOlder(T.mins(4).msecs()))
}
@Test
fun findPreviousTimeFromBucketedDataTest() {
val bgReadingList: MutableList<GlucoseValue> = ArrayList()
autosensDataStore.bgReadings = bgReadingList
autosensDataStore.createBucketedData(aapsLogger, dateUtil)
Assert.assertEquals(null, autosensDataStore.findPreviousTimeFromBucketedData(1000))
// Super data should not be touched
bgReadingList.clear()
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(20).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(15).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(10).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
bgReadingList.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(5).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT))
autosensDataStore.bgReadings = bgReadingList
autosensDataStore.createBucketedData(aapsLogger, dateUtil)
Assert.assertEquals(null, autosensDataStore.findPreviousTimeFromBucketedData(T.mins(4).msecs()))
Assert.assertEquals(T.mins(5).msecs(), autosensDataStore.findPreviousTimeFromBucketedData(T.mins(6).msecs()))
Assert.assertEquals(T.mins(20).msecs(), autosensDataStore.findPreviousTimeFromBucketedData(T.mins(20).msecs()))
Assert.assertEquals(T.mins(20).msecs(), autosensDataStore.findPreviousTimeFromBucketedData(T.mins(25).msecs()))
}
}

View file

@ -3,12 +3,16 @@ package info.nightscout.sdk
import android.content.Context
import info.nightscout.sdk.exceptions.DateHeaderOutOfToleranceException
import info.nightscout.sdk.exceptions.InvalidAccessTokenException
import info.nightscout.sdk.exceptions.InvalidFormatNightscoutException
import info.nightscout.sdk.exceptions.TodoNightscoutException
import info.nightscout.sdk.exceptions.UnknownResponseNightscoutException
import info.nightscout.sdk.interfaces.NSAndroidClient
import info.nightscout.sdk.localmodel.Status
import info.nightscout.sdk.localmodel.entry.NSSgvV3
import info.nightscout.sdk.localmodel.treatment.CreateUpdateResponse
import info.nightscout.sdk.localmodel.treatment.NSTreatment
import info.nightscout.sdk.mapper.toLocal
import info.nightscout.sdk.mapper.toRemoteTreatment
import info.nightscout.sdk.mapper.toSgv
import info.nightscout.sdk.mapper.toTreatment
import info.nightscout.sdk.networking.NetworkStackBuilder
@ -58,6 +62,8 @@ class NSAndroidClientImpl(
accessToken = accessToken,
logging = logging
)
override var lastStatus: Status? = null
private set
/*
* TODO: how should our result look like?
@ -81,7 +87,7 @@ class NSAndroidClientImpl(
}
override suspend fun getStatus(): Status = callWrapper(dispatcher) {
api.statusSimple().result!!.toLocal()
api.statusSimple().result!!.toLocal().also { lastStatus = it }
}
// TODO: return something better than a String
@ -132,11 +138,13 @@ class NSAndroidClientImpl(
}
}
override suspend fun getTreatmentsModifiedSince(from: Long, limit: Long): List<NSTreatment> = callWrapper(dispatcher) {
override suspend fun getTreatmentsModifiedSince(from: Long, limit: Long): NSAndroidClient.ReadResponse<List<NSTreatment>> = callWrapper(dispatcher) {
val response = api.getTreatmentsModifiedSince(from, limit)
val eTagString = response.headers()["ETag"]
val eTag = eTagString?.substring(3, eTagString.length - 1)?.toLong() ?: throw TodoNightscoutException()
if (response.isSuccessful) {
return@callWrapper response.body()?.result?.map(RemoteTreatment::toTreatment).toNotNull()
return@callWrapper NSAndroidClient.ReadResponse(eTag, response.body()?.result?.map(RemoteTreatment::toTreatment).toNotNull())
} else {
throw TodoNightscoutException() // TODO: react to response errors (offline, ...)
}
@ -152,6 +160,41 @@ class NSAndroidClientImpl(
}
}
override suspend fun createTreatment(nsTreatment: NSTreatment): CreateUpdateResponse = callWrapper(dispatcher) {
val remoteTreatment = nsTreatment.toRemoteTreatment() ?: throw InvalidFormatNightscoutException()
remoteTreatment.app = "AAPS"
val response = api.createTreatment(remoteTreatment)
if (response.isSuccessful) {
return@callWrapper CreateUpdateResponse(
response = response.code(),
identifier = response.body()?.result?.identifier ?: throw UnknownResponseNightscoutException(),
isDeduplication = response.body()?.result?.isDeduplication ?: false,
deduplicatedIdentifier = response.body()?.result?.deduplicatedIdentifier,
lastModified = response.body()?.result?.lastModified
)
} else {
throw TodoNightscoutException() // TODO: react to response errors (offline, ...)
}
}
override suspend fun updateTreatment(nsTreatment: NSTreatment): CreateUpdateResponse = callWrapper(dispatcher) {
val remoteTreatment = nsTreatment.toRemoteTreatment() ?: throw InvalidFormatNightscoutException()
val response = api.updateTreatment(remoteTreatment)
if (response.isSuccessful) {
return@callWrapper CreateUpdateResponse(
response = response.code(),
identifier = response.body()?.result?.identifier ?: throw UnknownResponseNightscoutException(),
isDeduplication = response.body()?.result?.isDeduplication ?: false,
deduplicatedIdentifier = response.body()?.result?.deduplicatedIdentifier,
lastModified = response.body()?.result?.lastModified
)
} else {
throw TodoNightscoutException() // TODO: react to response errors (offline, ...)
}
}
private suspend fun <T> callWrapper(dispatcher: CoroutineDispatcher, block: suspend () -> T): T =
withContext(dispatcher) {
retry(

View file

@ -16,7 +16,7 @@ class NSAndroidRxClientImpl(private val client: NSAndroidClient) : NSAndroidRxCl
override fun getStatus(): Single<Status> = rxSingle { client.getStatus() }
override fun getLastModified(): Single<LastModified> = rxSingle { client.getLastModified() }
override fun getSgvsModifiedSince(from: Long): Single<List<NSSgvV3>> = rxSingle { client.getSgvsModifiedSince(from) }
override fun getTreatmentsModifiedSince(from: Long, limit: Long): Single<List<NSTreatment>> =
override fun getTreatmentsModifiedSince(from: Long, limit: Long): Single<NSAndroidClient.ReadResponse<List<NSTreatment>>> =
rxSingle { client.getTreatmentsModifiedSince(from, limit) }
override fun getDeviceStatusModifiedSince(from: Long): Single<List<RemoteDeviceStatus>> =
rxSingle { client.getDeviceStatusModifiedSince(from) }

View file

@ -0,0 +1,3 @@
package info.nightscout.sdk.exceptions
class InvalidFormatNightscoutException : NightscoutException()

View file

@ -0,0 +1,3 @@
package info.nightscout.sdk.exceptions
class UnknownResponseNightscoutException : NightscoutException()

View file

@ -2,12 +2,19 @@ package info.nightscout.sdk.interfaces
import info.nightscout.sdk.localmodel.Status
import info.nightscout.sdk.localmodel.entry.NSSgvV3
import info.nightscout.sdk.localmodel.treatment.CreateUpdateResponse
import info.nightscout.sdk.localmodel.treatment.NSTreatment
import info.nightscout.sdk.remotemodel.LastModified
import info.nightscout.sdk.remotemodel.RemoteDeviceStatus
interface NSAndroidClient {
class ReadResponse<T>(
val lastServerModified: Long,
val values: T
)
val lastStatus: Status?
suspend fun getVersion(): String
suspend fun getStatus(): Status
suspend fun getEntries(): String
@ -16,6 +23,8 @@ interface NSAndroidClient {
suspend fun getSgvs(): List<NSSgvV3>
suspend fun getSgvsModifiedSince(from: Long): List<NSSgvV3>
suspend fun getSgvsNewerThan(from: Long, limit: Long): List<NSSgvV3>
suspend fun getTreatmentsModifiedSince(from: Long, limit: Long): List<NSTreatment>
suspend fun getTreatmentsModifiedSince(from: Long, limit: Long): ReadResponse<List<NSTreatment>>
suspend fun getDeviceStatusModifiedSince(from: Long): List<RemoteDeviceStatus>
suspend fun createTreatment(nsTreatment: NSTreatment): CreateUpdateResponse
suspend fun updateTreatment(nsTreatment: NSTreatment): CreateUpdateResponse
}

View file

@ -13,7 +13,7 @@ interface NSAndroidRxClient {
fun getStatus(): Single<Status>
fun getLastModified(): Single<LastModified>
fun getSgvsModifiedSince(from: Long): Single<List<NSSgvV3>>
fun getTreatmentsModifiedSince(from: Long, limit: Long): Single<List<NSTreatment>>
fun getTreatmentsModifiedSince(from: Long, limit: Long): Single<NSAndroidClient.ReadResponse<List<NSTreatment>>>
fun getDeviceStatusModifiedSince(from: Long): Single<List<RemoteDeviceStatus>>
}

View file

@ -7,4 +7,7 @@ data class ApiPermissions(
val profile: ApiPermission,
val settings: ApiPermission,
val treatments: ApiPermission
)
) {
fun isFull() = deviceStatus.full && entries.full && food.full && profile.full && settings.full && treatments.full
fun isRead() = deviceStatus.read && entries.read && food.read && profile.read && settings.read && treatments.read
}

View file

@ -0,0 +1,9 @@
package info.nightscout.sdk.localmodel.treatment
class CreateUpdateResponse(
val response: Int,
val identifier: String?,
val isDeduplication: Boolean? = false,
val deduplicatedIdentifier: String? = null,
val lastModified: Long? = null
)

View file

@ -4,14 +4,14 @@ import info.nightscout.sdk.localmodel.entry.NsUnits
data class NSBolus(
override val date: Long,
override val device: String?,
override val identifier: String,
override val units: NsUnits?,
override val srvModified: Long,
override val srvCreated: Long,
override val device: String?= null,
override val identifier: String?,
override val units: NsUnits?= null,
override val srvModified: Long? = null,
override val srvCreated: Long? = null,
override val utcOffset: Long,
override val subject: String?,
override var isReadOnly: Boolean,
override val subject: String? = null,
override var isReadOnly: Boolean = false,
override val isValid: Boolean,
override val eventType: EventType,
override val notes: String?,
@ -19,6 +19,7 @@ data class NSBolus(
override val endId: Long?,
override val pumpType: String?,
override val pumpSerial: String?,
override var app: String? = null,
val insulin: Double,
val type: BolusType

View file

@ -1,15 +1,14 @@
package info.nightscout.sdk.localmodel.treatment
import info.nightscout.sdk.localmodel.entry.NsUnits
import org.json.JSONObject
data class NSBolusWizard(
override val date: Long,
override val device: String?,
override val identifier: String,
override val identifier: String?,
override val units: NsUnits?,
override val srvModified: Long,
override val srvCreated: Long,
override val srvModified: Long?,
override val srvCreated: Long?,
override val utcOffset: Long,
override val subject: String?,
override var isReadOnly: Boolean,
@ -20,6 +19,7 @@ data class NSBolusWizard(
override val endId: Long?,
override val pumpType: String?,
override val pumpSerial: String?,
override var app: String? = null,
val bolusCalculatorResult: String?,
val glucose: Double?,
) : NSTreatment

View file

@ -5,10 +5,10 @@ import info.nightscout.sdk.localmodel.entry.NsUnits
data class NSCarbs(
override val date: Long,
override val device: String?,
override val identifier: String,
override val identifier: String?,
override val units: NsUnits?,
override val srvModified: Long,
override val srvCreated: Long,
override val srvModified: Long?,
override val srvCreated: Long?,
override val utcOffset: Long,
override val subject: String?,
override var isReadOnly: Boolean,
@ -19,6 +19,7 @@ data class NSCarbs(
override val endId: Long?,
override val pumpType: String?,
override val pumpSerial: String?,
override var app: String? = null,
val carbs: Double,
val duration: Long
) : NSTreatment

View file

@ -6,10 +6,10 @@ import org.json.JSONObject
data class NSEffectiveProfileSwitch(
override val date: Long,
override val device: String?,
override val identifier: String,
override val identifier: String?,
override val units: NsUnits?,
override val srvModified: Long,
override val srvCreated: Long,
override val srvModified: Long?,
override val srvCreated: Long?,
override val utcOffset: Long,
override val subject: String?,
override var isReadOnly: Boolean,
@ -20,6 +20,7 @@ data class NSEffectiveProfileSwitch(
override val endId: Long?,
override val pumpType: String?,
override val pumpSerial: String?,
override var app: String? = null,
val profileJson: JSONObject,
val originalProfileName: String,
val originalCustomizedName: String,

View file

@ -5,10 +5,10 @@ import info.nightscout.sdk.localmodel.entry.NsUnits
data class NSExtendedBolus(
override val date: Long,
override val device: String?,
override val identifier: String,
override val identifier: String?,
override val units: NsUnits?,
override val srvModified: Long,
override val srvCreated: Long,
override val srvModified: Long?,
override val srvCreated: Long?,
override val utcOffset: Long,
override val subject: String?,
override var isReadOnly: Boolean,
@ -19,7 +19,8 @@ data class NSExtendedBolus(
override val endId: Long?,
override val pumpType: String?,
override val pumpSerial: String?,
override var app: String? = null,
val duration: Long,
val enteredinsulin: Double,
val isEmulatingTempbasal: Boolean
val isEmulatingTempBasal: Boolean?
) : NSTreatment

View file

@ -5,10 +5,10 @@ import info.nightscout.sdk.localmodel.entry.NsUnits
data class NSOfflineEvent(
override val date: Long,
override val device: String?,
override val identifier: String,
override val identifier: String?,
override val units: NsUnits?,
override val srvModified: Long,
override val srvCreated: Long,
override val srvModified: Long?,
override val srvCreated: Long?,
override val utcOffset: Long,
override val subject: String?,
override var isReadOnly: Boolean,
@ -19,6 +19,7 @@ data class NSOfflineEvent(
override val endId: Long?,
override val pumpType: String?,
override val pumpSerial: String?,
override var app: String? = null,
val duration: Long,
val reason: Reason
) : NSTreatment {

View file

@ -6,10 +6,10 @@ import org.json.JSONObject
data class NSProfileSwitch(
override val date: Long,
override val device: String?,
override val identifier: String,
override val identifier: String?,
override val units: NsUnits?,
override val srvModified: Long,
override val srvCreated: Long,
override val srvModified: Long?,
override val srvCreated: Long?,
override val utcOffset: Long,
override val subject: String?,
override var isReadOnly: Boolean,
@ -20,6 +20,7 @@ data class NSProfileSwitch(
override val endId: Long?,
override val pumpType: String?,
override val pumpSerial: String?,
override var app: String? = null,
val profileJson: JSONObject?,
val profileName: String,
val originalProfileName: String?,

View file

@ -1,15 +1,14 @@
package info.nightscout.sdk.localmodel.treatment
import info.nightscout.sdk.localmodel.entry.NsUnits
import org.json.JSONObject
data class NSTemporaryBasal(
override val date: Long,
override val device: String?,
override val identifier: String,
override val identifier: String?,
override val units: NsUnits?,
override val srvModified: Long,
override val srvCreated: Long,
override val srvModified: Long?,
override val srvCreated: Long?,
override val utcOffset: Long,
override val subject: String?,
override var isReadOnly: Boolean,
@ -20,10 +19,13 @@ data class NSTemporaryBasal(
override val endId: Long?,
override val pumpType: String?,
override val pumpSerial: String?,
override var app: String? = null,
val duration: Long,
val rate: Double,
val rate: Double, // when sending to NS always convertedToAbsolute(timestamp, profile)
val isAbsolute: Boolean,
val type: Type
val type: Type,
val percent: Double? = null, // when sending to NS (rate - 100)
val absolute: Double? = null // when sending to NS (rate)
) : NSTreatment {
enum class Type {

View file

@ -5,10 +5,10 @@ import info.nightscout.sdk.localmodel.entry.NsUnits
data class NSTemporaryTarget(
override val date: Long,
override val device: String?,
override val identifier: String,
override val identifier: String?,
override val units: NsUnits?,
override val srvModified: Long,
override val srvCreated: Long,
override val srvModified: Long?,
override val srvCreated: Long?,
override val utcOffset: Long,
override val subject: String?,
override var isReadOnly: Boolean,
@ -19,6 +19,7 @@ data class NSTemporaryTarget(
override val endId: Long?,
override val pumpType: String?,
override val pumpSerial: String?,
override var app: String? = null,
val duration: Long,
val targetBottom: Double,
val targetTop: Double,

View file

@ -6,10 +6,10 @@ import info.nightscout.sdk.localmodel.entry.NsUnits
data class NSTherapyEvent(
override val date: Long,
override val device: String?,
override val identifier: String,
override val identifier: String?,
override val units: NsUnits?,
override val srvModified: Long,
override val srvCreated: Long,
override val srvModified: Long?,
override val srvCreated: Long?,
override val utcOffset: Long,
override val subject: String?,
override var isReadOnly: Boolean,
@ -20,6 +20,7 @@ data class NSTherapyEvent(
override val endId: Long?,
override val pumpType: String?,
override val pumpSerial: String?,
override var app: String? = null,
val duration: Long,
var enteredBy: String? = null,
var glucose: Double? = null,

View file

@ -5,11 +5,11 @@ import info.nightscout.sdk.localmodel.entry.NsUnits
interface NSTreatment {
val date: Long
val device: String?
val identifier: String
val identifier: String?
val units: NsUnits?
val eventType: EventType
val srvModified: Long
val srvCreated: Long
val srvModified: Long?
val srvCreated: Long?
val utcOffset: Long
val subject: String?
var isReadOnly: Boolean
@ -19,6 +19,7 @@ interface NSTreatment {
val endId: Long?
val pumpType: String?
val pumpSerial: String?
var app: String?
fun Double.asMgdl() =
when (units) {

View file

@ -117,7 +117,7 @@ internal fun RemoteTreatment.toTreatment(): NSTreatment? {
pumpSerial = extendedEmulated.pumpSerial,
enteredinsulin = extendedEmulated.enteredinsulin ?: 0.0,
duration = extendedEmulated.durationInMilliseconds ?: TimeUnit.MINUTES.toMillis(extendedEmulated.duration ?: 0L),
isEmulatingTempbasal = extendedEmulated.isEmulatingTempBasal
isEmulatingTempBasal = extendedEmulated.isEmulatingTempBasal
)
}
@ -329,10 +329,270 @@ internal fun RemoteTreatment.toTreatment(): NSTreatment? {
pumpSerial = this.pumpSerial,
enteredinsulin = this.enteredinsulin,
duration = this.durationInMilliseconds ?: TimeUnit.MINUTES.toMillis(this.duration ?: 0L),
isEmulatingTempbasal = this.isEmulatingTempBasal
isEmulatingTempBasal = this.isEmulatingTempBasal
)
}
}
return null
}
internal fun NSTreatment.toRemoteTreatment(): RemoteTreatment? =
when (this) {
is NSBolus -> RemoteTreatment(
date = date,
device = device,
identifier = identifier,
units = units?.value,
utcOffset = utcOffset,
subject = subject,
isReadOnly = isReadOnly,
isValid = isValid,
eventType = eventType,
notes = notes,
pumpId = pumpId,
endId = endId,
pumpType = pumpType,
pumpSerial = pumpSerial,
insulin = insulin,
type = type.name
)
is NSCarbs -> RemoteTreatment(
date = date,
device = device,
identifier = identifier,
units = units?.value,
utcOffset = utcOffset,
subject = subject,
isReadOnly = isReadOnly,
isValid = isValid,
eventType = eventType,
notes = notes,
pumpId = pumpId,
endId = endId,
pumpType = pumpType,
pumpSerial = pumpSerial,
carbs = carbs,
duration = duration
)
is NSTemporaryTarget -> RemoteTreatment(
date = date,
device = device,
identifier = identifier,
units = units?.value,
srvModified = srvModified,
srvCreated = srvCreated,
utcOffset = utcOffset,
subject = subject,
isReadOnly = isReadOnly,
isValid = isValid,
eventType = eventType,
notes = notes,
pumpId = pumpId,
endId = endId,
pumpType = pumpType,
pumpSerial = pumpSerial,
duration = TimeUnit.MILLISECONDS.toMinutes(duration),
durationInMilliseconds = duration,
targetBottom = targetBottom,
targetTop = targetTop,
reason = reason.text
)
/*
// Convert back emulated TBR -> EB
eventType == EventType.TEMPORARY_BASAL && extendedEmulated != null ->
return RemoteTreatment(
date = treatmentTimestamp,
device = device,
identifier = identifier,
units = NsUnits.fromString(extendedEmulated.units),
srvModified = srvModified,
srvCreated = srvCreated,
utcOffset = utcOffset ?: 0,
subject = subject,
isReadOnly = extendedEmulated.isReadOnly ?: false,
isValid = extendedEmulated.isValid ?: true,
eventType = extendedEmulated.eventType,
notes = extendedEmulated.notes,
pumpId = extendedEmulated.pumpId,
endId = extendedEmulated.endId,
pumpType = extendedEmulated.pumpType,
pumpSerial = extendedEmulated.pumpSerial,
enteredinsulin = extendedEmulated.enteredinsulin ?: 0.0,
duration = extendedEmulated.durationInMilliseconds ?: TimeUnit.MINUTES.toMillis(extendedEmulated.duration ?: 0L),
isEmulatingTempbasal = extendedEmulated.isEmulatingTempBasal
)
}
*/
is NSTemporaryBasal -> RemoteTreatment(
date = date,
device = device,
identifier = identifier,
units = units?.value,
srvModified = srvModified,
srvCreated = srvCreated,
utcOffset = utcOffset,
subject = subject,
isReadOnly = isReadOnly,
isValid = isValid,
eventType = eventType,
notes = notes,
pumpId = pumpId,
endId = endId,
pumpType = pumpType,
pumpSerial = pumpSerial,
duration = TimeUnit.MILLISECONDS.toMinutes(duration),
durationInMilliseconds = duration,
absolute = absolute,
percent = percent,
rate = absolute,
type = type.name
)
is NSEffectiveProfileSwitch -> RemoteTreatment(
date = date,
device = device,
identifier = identifier,
units = units?.value,
srvModified = srvModified,
srvCreated = srvCreated,
utcOffset = utcOffset,
subject = subject,
isReadOnly = isReadOnly,
isValid = isValid,
eventType = eventType,
notes = notes,
pumpId = pumpId,
endId = endId,
pumpType = pumpType,
pumpSerial = pumpSerial,
profileJson = profileJson.toString(),
originalProfileName = originalProfileName,
originalCustomizedName = originalCustomizedName,
originalTimeshift = originalTimeshift,
originalPercentage = originalPercentage,
originalDuration = originalDuration,
originalEnd = originalEnd
)
is NSProfileSwitch -> RemoteTreatment(
date = date,
device = device,
identifier = identifier,
units = units?.value,
srvModified = srvModified,
srvCreated = srvCreated,
utcOffset = utcOffset,
subject = subject,
isReadOnly = isReadOnly,
isValid = isValid,
eventType = eventType,
notes = notes,
pumpId = pumpId,
endId = endId,
pumpType = pumpType,
pumpSerial = pumpSerial,
profileJson = profileJson.toString(), // must be de-customized
profile = profileName,
originalProfileName = originalProfileName,
originalDuration = originalDuration,
duration = duration,
timeshift = timeShift,
percentage = percentage,
)
is NSBolusWizard -> RemoteTreatment(
date = date,
device = device,
identifier = identifier,
units = units?.value,
srvModified = srvModified,
srvCreated = srvCreated,
utcOffset = utcOffset,
subject = subject,
isReadOnly = isReadOnly,
isValid = isValid,
eventType = eventType,
notes = notes,
pumpId = pumpId,
endId = endId,
pumpType = pumpType,
pumpSerial = pumpSerial,
bolusCalculatorResult = bolusCalculatorResult,
glucose = glucose
)
is NSTherapyEvent -> RemoteTreatment(
date = date,
device = device,
identifier = identifier,
units = units?.value,
srvModified = srvModified,
srvCreated = srvCreated,
utcOffset = utcOffset,
subject = subject,
isReadOnly = isReadOnly,
isValid = isValid,
eventType = eventType,
notes = notes,
pumpId = pumpId,
endId = endId,
pumpType = pumpType,
pumpSerial = pumpSerial,
duration = TimeUnit.MILLISECONDS.toMinutes(duration),
durationInMilliseconds = duration,
glucose = glucose,
enteredBy = enteredBy,
glucoseType = glucoseType?.text
)
is NSOfflineEvent -> RemoteTreatment(
date = date,
device = device,
identifier = identifier,
units = units?.value,
srvModified = srvModified,
srvCreated = srvCreated,
utcOffset = utcOffset,
subject = subject,
isReadOnly = isReadOnly,
isValid = isValid,
eventType = eventType,
notes = notes,
pumpId = pumpId,
endId = endId,
pumpType = pumpType,
pumpSerial = pumpSerial,
duration = TimeUnit.MILLISECONDS.toMinutes(duration),
durationInMilliseconds = duration,
reason = reason.name
)
is NSExtendedBolus -> RemoteTreatment(
date = date,
device = device,
identifier = identifier,
units = units?.value,
srvModified = srvModified,
srvCreated = srvCreated,
utcOffset = utcOffset,
subject = subject,
isReadOnly = isReadOnly,
isValid = isValid,
eventType = eventType,
notes = notes,
pumpId = pumpId,
endId = endId,
pumpType = pumpType,
pumpSerial = pumpSerial,
enteredinsulin = enteredinsulin,
duration = TimeUnit.MILLISECONDS.toMinutes(duration),
durationInMilliseconds = duration,
isEmulatingTempBasal = isEmulatingTempBasal
)
else -> null
}

View file

@ -2,13 +2,17 @@ package info.nightscout.sdk.networking
import com.google.gson.JsonElement
import info.nightscout.sdk.remotemodel.LastModified
import info.nightscout.sdk.remotemodel.RemoteDeviceStatus
import info.nightscout.sdk.remotemodel.NSResponse
import info.nightscout.sdk.remotemodel.RemoteCreateUpdateResponse
import info.nightscout.sdk.remotemodel.RemoteDeviceStatus
import info.nightscout.sdk.remotemodel.RemoteEntry
import info.nightscout.sdk.remotemodel.RemoteStatusResponse
import info.nightscout.sdk.remotemodel.RemoteTreatment
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.PUT
import retrofit2.http.Path
import retrofit2.http.Query
@ -48,4 +52,11 @@ internal interface NightscoutRemoteService {
@GET("v3/devicestatus/history/{from}")
suspend fun getDeviceStatusModifiedSince(@Path("from") from: Long): Response<NSResponse<List<RemoteDeviceStatus>>>
@POST("v3/treatments")
suspend fun createTreatment(@Body remoteTreatment: RemoteTreatment): Response<NSResponse<RemoteCreateUpdateResponse>>
@PUT("v3/treatments")
suspend fun updateTreatment(@Body remoteTreatment: RemoteTreatment): Response<NSResponse<RemoteCreateUpdateResponse>>
}

View file

@ -17,6 +17,13 @@ internal data class RemoteStorage(
@SerializedName("version") val version: String
)
internal data class RemoteCreateUpdateResponse(
@SerializedName("identifier") val identifier: String?,
@SerializedName("isDeduplication") val isDeduplication: Boolean?,
@SerializedName("deduplicatedIdentifier") val deduplicatedIdentifier: String?,
@SerializedName("lastModified") val lastModified: Long?
)
internal data class RemoteApiPermissions(
@SerializedName("devicestatus") val deviceStatus: RemoteApiPermission,
@SerializedName("entries") val entries: RemoteApiPermission,

View file

@ -1,11 +1,9 @@
package info.nightscout.sdk.remotemodel
import com.google.gson.Gson
import com.google.gson.annotations.SerializedName
import info.nightscout.sdk.localmodel.treatment.EventType
import org.joda.time.DateTime
import org.joda.time.format.ISODateTimeFormat
import org.json.JSONObject
/*
* Depending on the type, different other fields are present.
@ -17,73 +15,72 @@ import org.json.JSONObject
*
* */
internal data class RemoteTreatment(
@SerializedName("identifier") val identifier: String, // string Main addressing, required field that identifies document in the collection. The client should not create the identifier, the server automatically assigns it when the document is inserted.
@SerializedName("date") val date: Long?, // integer($int64) or string required timestamp when the record or event occurred, you can choose from three input formats Unix epoch in milliseconds (1525383610088), Unix epoch in seconds (1525383610), ISO 8601 with optional timezone ('2018-05-03T21:40:10.088Z' or '2018-05-03T23:40:10.088+02:00')
@SerializedName("mills") val mills: Long?, // integer($int64) or string required timestamp when the record or event occurred, you can choose from three input formats Unix
@SerializedName("timestamp") val timestamp: Long?, // integer($int64) or string required timestamp when the record or event occurred, you can choose from three input formats Unix epoch in milliseconds (1525383610088), Unix epoch in seconds (1525383610), ISO 8601 with optional timezone ('2018-05-03T21:40:10.088Z' or '2018-05-03T23:40:10.088+02:00')
@SerializedName("created_at") val created_at: String, // integer($int64) or string timestamp on previous version of api, in my examples, a lot of treatments don't have date, only created_at, some of them with string others with long...
@SerializedName("utcOffset") val utcOffset: Long?, // integer Local UTC offset (timezone) of the event in minutes. This field can be set either directly by the client (in the incoming
// document) or it is automatically parsed from the date field.
// @SerializedName("app") val app : String, // TODO required ? Application or system in which the record was entered by human or device for the first time.
@SerializedName("device") val device: String?, // string The device from which the data originated (including serial number of the device, if it is relevant and safe).
@SerializedName("srvCreated") val srvCreated: Long, // integer($int64) example: 1525383610088 The server's timestamp of document insertion into the database (Unix epoch in ms). This field appears only for documents which were inserted by API v3.
@SerializedName("subject") val subject: String?, // string Name of the security subject (within Nightscout scope) which has created the document. This field is automatically set by the server from the passed token or JWT.
@SerializedName("srvModified") val srvModified: Long, // integer($int64) example: 1525383610088 The server's timestamp of the last document modification in the database (Unix epoch in ms). This field appears only for documents which were somehow modified by API v3 (inserted, updated or deleted).
@SerializedName("modifiedBy") val modifiedBy: String?, // string Name of the security subject (within Nightscout scope) which has patched or deleted the document for the last time. This field is automatically set by the server.
@SerializedName("isValid") val isValid: Boolean?, // boolean A flag set by the server only for deleted documents. This field appears only within history operation and for documents which were deleted by API v3 (and they always have a false value)
@SerializedName("isReadOnly") val isReadOnly: Boolean?, // boolean A flag set by client that locks the document from any changes. Every document marked with isReadOnly=true is forever immutable and cannot even be deleted.
@SerializedName("identifier") val identifier: String?, // string Main addressing, required field that identifies document in the collection. The client should not create the identifier, the server automatically assigns it when the document is inserted.
@SerializedName("date") val date: Long? = null, // integer($int64) or string required timestamp when the record or event occurred, you can choose from three input formats Unix epoch in milliseconds (1525383610088), Unix epoch in seconds (1525383610), ISO 8601 with optional timezone ('2018-05-03T21:40:10.088Z' or '2018-05-03T23:40:10.088+02:00')
@SerializedName("mills") val mills: Long? = null, // integer($int64) or string required timestamp when the record or event occurred, you can choose from three input formats Unix
@SerializedName("timestamp") val timestamp: Long? = null, // integer($int64) or string required timestamp when the record or event occurred, you can choose from three input formats Unix epoch in milliseconds (1525383610088), Unix epoch in seconds (1525383610), ISO 8601 with optional timezone ('2018-05-03T21:40:10.088Z' or '2018-05-03T23:40:10.088+02:00')
@SerializedName("created_at") val created_at: String? = null, // integer($int64) or string timestamp on previous version of api, in my examples, a lot of treatments don't have date, only created_at, some of them with string others with long...
@SerializedName("utcOffset") val utcOffset: Long? = null, // integer Local UTC offset (timezone) of the event in minutes. This field can be set either directly by the client (in the incoming document) or it is automatically parsed from the date field.
@SerializedName("app") var app : String? = null, // Application or system in which the record was entered by human or device for the first time.
@SerializedName("device") val device: String? = null, // string The device from which the data originated (including serial number of the device, if it is relevant and safe).
@SerializedName("srvCreated") val srvCreated: Long? = null, // integer($int64) example: 1525383610088 The server's timestamp of document insertion into the database (Unix epoch in ms). This field appears only for documents which were inserted by API v3.
@SerializedName("subject") val subject: String? = null, // string Name of the security subject (within Nightscout scope) which has created the document. This field is automatically set by the server from the passed token or JWT.
@SerializedName("srvModified") val srvModified: Long? = null, // integer($int64) example: 1525383610088 The server's timestamp of the last document modification in the database (Unix epoch in ms). This field appears only for documents which were somehow modified by API v3 (inserted, updated or deleted).
@SerializedName("modifiedBy") val modifiedBy: String? = null, // string Name of the security subject (within Nightscout scope) which has patched or deleted the document for the last time. This field is automatically set by the server.
@SerializedName("isValid") val isValid: Boolean? = null, // boolean A flag set by the server only for deleted documents. This field appears only within history operation and for documents which were deleted by API v3 (and they always have a false value)
@SerializedName("isReadOnly") val isReadOnly: Boolean? = null, // boolean A flag set by client that locks the document from any changes. Every document marked with isReadOnly=true is forever immutable and cannot even be deleted.
@SerializedName("eventType") val eventType: EventType, // string "BG Check", "Snack Bolus", "Meal Bolus", "Correction Bolus", "Carb Correction", "Combo Bolus", "Announcement", "Note", "Question", "Exercise", "Site Change", "Sensor Start", "Sensor Change", "Pump Battery Change", "Insulin Change", "Temp Basal", "Profile Switch", "D.A.D. Alert", "Temporary Target", "OpenAPS Offline", "Bolus Wizard"
@SerializedName("glucose") val glucose: Double?, // double Current glucose
@SerializedName("glucoseType") val glucoseType: String?, // string example: "Sensor", "Finger", "Manual"
@SerializedName("units") val units: String?, // string The units for the glucose value, mg/dl or mmol/l. It is strongly recommended to fill in this field.
@SerializedName("carbs") val carbs: Double?, // number... Amount of carbs given.
@SerializedName("protein") val protein: Int?, // number... Amount of protein given.
@SerializedName("fat") val fat: Int?, // number... Amount of fat given.
@SerializedName("insulin") val insulin: Double?, // number... Amount of insulin, if any.
@SerializedName("duration") val duration: Long?, // number... Duration in minutes.
@SerializedName("durationInMilliseconds") val durationInMilliseconds: Long?, // number... Duration in milliseconds.
@SerializedName("preBolus") val preBolus: Int?, // number... How many minutes the bolus was given before the meal started.
@SerializedName("splitNow") val splitNow: Int?, // number... Immediate part of combo bolus (in percent).
@SerializedName("splitExt") val splitExt: Int?, // number... Extended part of combo bolus (in percent).
@SerializedName("percent") val percent: Double?, // number... Eventual basal change in percent.
@SerializedName("absolute") val absolute: Double?, // number... Eventual basal change in absolute value (insulin units per hour).
@SerializedName("targetTop") val targetTop: Double?, // number... Top limit of temporary target.
@SerializedName("targetBottom") val targetBottom: Double?, // number... Bottom limit of temporary target.
@SerializedName("profile") val profile: String?, // string Name of the profile to which the pump has been switched.
@SerializedName("reason") val reason: String?, // string For example the reason why the profile has been switched or why the temporary target has been set.
@SerializedName("notes") val notes: String?, // string Description/notes of treatment.
@SerializedName("enteredBy") val enteredBy: String?, // string Who entered the treatment.
@SerializedName("glucose") val glucose: Double? = null, // double Current glucose
@SerializedName("glucoseType") val glucoseType: String? = null, // string example: "Sensor", "Finger", "Manual"
@SerializedName("units") val units: String? = null, // string The units for the glucose value, mg/dl or mmol/l. It is strongly recommended to fill in this field.
@SerializedName("carbs") val carbs: Double? = null, // number... Amount of carbs given.
@SerializedName("protein") val protein: Int? = null, // number... Amount of protein given.
@SerializedName("fat") val fat: Int? = null, // number... Amount of fat given.
@SerializedName("insulin") val insulin: Double? = null, // number... Amount of insulin, if any.
@SerializedName("duration") val duration: Long? = null, // number... Duration in minutes.
@SerializedName("durationInMilliseconds") val durationInMilliseconds: Long? = null, // number... Duration in milliseconds.
@SerializedName("preBolus") val preBolus: Int? = null, // number... How many minutes the bolus was given before the meal started.
@SerializedName("splitNow") val splitNow: Int? = null, // number... Immediate part of combo bolus (in percent).
@SerializedName("splitExt") val splitExt: Int? = null, // number... Extended part of combo bolus (in percent).
@SerializedName("percent") val percent: Double? = null, // number... Eventual basal change in percent.
@SerializedName("absolute") val absolute: Double? = null, // number... Eventual basal change in absolute value (insulin units per hour).
@SerializedName("targetTop") val targetTop: Double? = null, // number... Top limit of temporary target.
@SerializedName("targetBottom") val targetBottom: Double? = null, // number... Bottom limit of temporary target.
@SerializedName("profile") val profile: String? = null, // string Name of the profile to which the pump has been switched.
@SerializedName("reason") val reason: String? = null, // string For example the reason why the profile has been switched or why the temporary target has been set.
@SerializedName("notes") val notes: String? = null, // string Description/notes of treatment.
@SerializedName("enteredBy") val enteredBy: String? = null, // string Who entered the treatment.
@SerializedName("endId") val endId: Long?, // long id of record which ended this
@SerializedName("pumpId") val pumpId: Long?, // long or "Meal Bolus", "Correction Bolus", "Combo Bolus" ex 4102 not sure if long or int
@SerializedName("pumpType") val pumpType: String?, // string "Meal Bolus", "Correction Bolus", "Combo Bolus" ex "ACCU_CHEK_INSIGHT_BLUETOOTH",
@SerializedName("pumpSerial") val pumpSerial: String?, // string "Meal Bolus", "Correction Bolus", "Combo Bolus" "33013206",
@SerializedName("endId") val endId: Long? = null, // long id of record which ended this
@SerializedName("pumpId") val pumpId: Long? = null, // long or "Meal Bolus", "Correction Bolus", "Combo Bolus" ex 4102 not sure if long or int
@SerializedName("pumpType") val pumpType: String? = null, // string "Meal Bolus", "Correction Bolus", "Combo Bolus" ex "ACCU_CHEK_INSIGHT_BLUETOOTH",
@SerializedName("pumpSerial") val pumpSerial: String? = null, // string "Meal Bolus", "Correction Bolus", "Combo Bolus" "33013206",
// other fields found in examples but not in documentation
@SerializedName("profileJson") val profileJson: String?, // string "Profile Switch" ex json toString "{\"units\":\"mg\\/dl\",\"dia\":5,\"timezone\":\"Africa\\/Cairo\",
@SerializedName("profileJson") val profileJson: String? = null, // string "Profile Switch" ex json toString "{\"units\":\"mg\\/dl\",\"dia\":5,\"timezone\":\"Africa\\/Cairo\",
// \"sens\":[{\"time\":\"00:00\",\"timeAsSeconds\":0,\"value\":60},{\"time\":\"07:00\",\"timeAsSeconds\":25200,\"value\":60},{\"time\":\"08:00\",\"timeAsSeconds\":28800,\"value\":61.33333333333333},{\"time\":\"09:00\",\"timeAsSeconds\":32400,\"value\":65.33333333333333},{\"time\":\"10:00\",\"timeAsSeconds\":36000,\"value\":69.33333333333333},{\"time\":\"11:00\",\"timeAsSeconds\":39600,\"value\":73.33333333333333},{\"time\":\"13:00\",\"timeAsSeconds\":46800,\"value\":72},{\"time\":\"14:00\",\"timeAsSeconds\":50400,\"value\":68},{\"time\":\"15:00\",\"timeAsSeconds\":54000,\"value\":65.33333333333333},{\"time\":\"16:00\",\"timeAsSeconds\":57600,\"value\":65.33333333333333}],\"carbratio\":[{\"time\":\"00:00\",\"timeAsSeconds\":0,\"value\":5.7333333333333325},{\"time\":\"11:00\",\"timeAsSeconds\":39600,\"value\":7.333333333333333},{\"time\":\"16:00\",\"timeAsSeconds\":57600,\"value\":6.666666666666666}],\"basal\":[{\"time\":\"00:00\",\"timeAsSeconds\":0,\"value\":0.5249999999999999},{\"time\":\"01:00\",\"timeAsSeconds\":3600,\"value\":0.585},{\"time\":\"02:00\",\"timeAsSeconds\":7200,\"value\":0.6375},{\"time\":\"03:00\",\"timeAsSeconds\":10800,\"value\":0.5625},{\"time\":\"04:00\",\"timeAsSeconds\":14400,\"value\":0.4575},{\"time\":\"05:00\",\"timeAsSeconds\":18000,\"value\":0.5175},{\"time\":\"06:00\",\"timeAsSeconds\":21600,\"value\":0.48},{\"time\":\"07:00\",\"timeAsSeconds\":25200,\"value\":0.51},{\"time\":\"08:00\",\"timeAsSeconds\":28800,\"value\":0.48750000000000004},{\"time\":\"09:00\",\"timeAsSeconds\":32400,\"value\":0.48},{\"time\":\"10:00\",\"timeAsSeconds\":36000,\"value\":0.48750000000000004},{\"time\":\"11:00\",\"timeAsSeconds\":39600,\"value\":0.5025000000000001},{\"time\":\"12:00\",\"timeAsSeconds\":43200,\"value\":0.5549999999999999},{\"time\":\"13:00\",\"timeAsSeconds\":46800,\"value\":0.5700000000000001},{\"time\":\"14:00\",\"timeAsSeconds\":50400,\"value\":0.5700000000000001},{\"time\":\"15:00\",\"timeAsSeconds\":54000,\"value\":0.5775},{\"time\":\"16:00\",\"timeAsSeconds\":57600,\"value\":0.51},{\"time\":\"17:00\",\"timeAsSeconds\":61200,\"value\":0.54},{\"time\":\"18:00\",\"timeAsSeconds\":64800,\"value\":0.48750000000000004},{\"time\":\"19:00\",\"timeAsSeconds\":68400,\"value\":0.5249999999999999},{\"time\":\"20:00\",\"timeAsSeconds\":72000,\"value\":0.46499999999999997},{\"time\":\"21:00\",\"timeAsSeconds\":75600,\"value\":0.46499999999999997},{\"time\":\"22:00\",\"timeAsSeconds\":79200,\"value\":0.43499999999999994},{\"time\":\"23:00\",\"timeAsSeconds\":82800,\"value\":0.41250000000000003}],\"target_low\":[{\"time\":\"00:00\",\"timeAsSeconds\":0,\"value\":100},{\"time\":\"06:00\",\"timeAsSeconds\":21600,\"value\":90},{\"time\":\"09:00\",\"timeAsSeconds\":32400,\"value\":100},{\"time\":\"11:00\",\"timeAsSeconds\":39600,\"value\":90},{\"time\":\"14:00\",\"timeAsSeconds\":50400,\"value\":100},{\"time\":\"18:00\",\"timeAsSeconds\":64800,\"value\":90},{\"time\":\"21:00\",\"timeAsSeconds\":75600,\"value\":100}],\"target_high\":[{\"time\":\"00:00\",\"timeAsSeconds\":0,\"value\":100},{\"time\":\"06:00\",\"timeAsSeconds\":21600,\"value\":90},{\"time\":\"09:00\",\"timeAsSeconds\":32400,\"value\":100},{\"time\":\"11:00\",\"timeAsSeconds\":39600,\"value\":90},{\"time\":\"14:00\",\"timeAsSeconds\":50400,\"value\":100},{\"time\":\"18:00\",\"timeAsSeconds\":64800,\"value\":90},{\"time\":\"21:00\",\"timeAsSeconds\":75600,\"value\":100}]}",
@SerializedName("originalProfileName") val originalProfileName: String?, // string "Effective Profile Switch"
@SerializedName("originalCustomizedName") val originalCustomizedName: String?, // string "Effective Profile Switch"
@SerializedName("originalTimeshift") val originalTimeshift: Long?, // long "Effective Profile Switch"
@SerializedName("originalPercentage") val originalPercentage: Int?, // int "Effective Profile Switch"
@SerializedName("originalDuration") val originalDuration: Long?, // long "Effective Profile Switch"
@SerializedName("originalEnd") val originalEnd: Long?, // long "Effective Profile Switch"
@SerializedName("originalProfileName") val originalProfileName: String? = null, // string "Effective Profile Switch"
@SerializedName("originalCustomizedName") val originalCustomizedName: String? = null, // string "Effective Profile Switch"
@SerializedName("originalTimeshift") val originalTimeshift: Long? = null, // long "Effective Profile Switch"
@SerializedName("originalPercentage") val originalPercentage: Int? = null, // int "Effective Profile Switch"
@SerializedName("originalDuration") val originalDuration: Long? = null, // long "Effective Profile Switch"
@SerializedName("originalEnd") val originalEnd: Long? = null, // long "Effective Profile Switch"
@SerializedName("bolusCalculatorResult") val bolusCalculatorResult: String?, // string "Bolus Wizard" json toString ex "bolusCalculatorResult": "{\"basalIOB\":-0.247,\"bolusIOB\":-1.837,\"carbs\":45.0,\"carbsInsulin\":9.0,\"cob\":0.0,\"cobInsulin\":0.0,\"dateCreated\":1626202788810,\"glucoseDifference\":44.0,\"glucoseInsulin\":0.8979591836734694,\"glucoseTrend\":5.5,\"glucoseValue\":134.0,\"ic\":5.0,\"id\":331,\"interfaceIDs_backing\":{\"nightscoutId\":\"60ede2a4c574da0004a3869d\"},\"isValid\":true,\"isf\":49.0,\"note\":\"\",\"otherCorrection\":0.0,\"percentageCorrection\":90,\"profileName\":\"Tuned 13/01 90%Lyum\",\"superbolusInsulin\":0.0,\"targetBGHigh\":90.0,\"targetBGLow\":90.0,\"timestamp\":1626202783325,\"totalInsulin\":7.34,\"trendInsulin\":0.336734693877551,\"utcOffset\":7200000,\"version\":1,\"wasBasalIOBUsed\":true,\"wasBolusIOBUsed\":true,\"wasCOBUsed\":true,\"wasGlucoseUsed\":true,\"wasSuperbolusUsed\":false,\"wasTempTargetUsed\":false,\"wasTrendUsed\":true,\"wereCarbsUsed\":false}",
@SerializedName("type") val type: String?, // string "Meal Bolus", "Correction Bolus", "Combo Bolus", "Temp Basal" type of bolus "NORMAL", "SMB", "FAKE_EXTENDED"
@SerializedName("isSMB") val isSMB: Boolean, // boolean "Meal Bolus", "Correction Bolus", "Combo Bolus"
@SerializedName("enteredinsulin") val enteredinsulin: Double?, // number... "Combo Bolus" insulin is missing only enteredinsulin field found
@SerializedName("relative") val relative: Double?, // number... "Combo Bolus", "extendedEmulated" (not in doc see below)
@SerializedName("isEmulatingTempBasal") val isEmulatingTempBasal: Boolean, // boolean "Combo Bolus", "extendedEmulated" (not in doc see below)
@SerializedName("isAnnouncement") val isAnnouncement: Boolean, // boolean "Announcement"
@SerializedName("rate") val rate: Double?, // Double "Temp Basal" absolute rate (could be calculated with percent and profile information...)
@SerializedName("extendedEmulated") val extendedEmulated: RemoteTreatment?, // Gson of emulated EB
@SerializedName("timeshift") val timeshift: Long, // integer "Profile Switch"
@SerializedName("percentage") val percentage: Int?, // integer "Profile Switch"
@SerializedName("bolusCalculatorResult") val bolusCalculatorResult: String? = null, // string "Bolus Wizard" json toString ex "bolusCalculatorResult": "{\"basalIOB\":-0.247,\"bolusIOB\":-1.837,\"carbs\":45.0,\"carbsInsulin\":9.0,\"cob\":0.0,\"cobInsulin\":0.0,\"dateCreated\":1626202788810,\"glucoseDifference\":44.0,\"glucoseInsulin\":0.8979591836734694,\"glucoseTrend\":5.5,\"glucoseValue\":134.0,\"ic\":5.0,\"id\":331,\"interfaceIDs_backing\":{\"nightscoutId\":\"60ede2a4c574da0004a3869d\"},\"isValid\":true,\"isf\":49.0,\"note\":\"\",\"otherCorrection\":0.0,\"percentageCorrection\":90,\"profileName\":\"Tuned 13/01 90%Lyum\",\"superbolusInsulin\":0.0,\"targetBGHigh\":90.0,\"targetBGLow\":90.0,\"timestamp\":1626202783325,\"totalInsulin\":7.34,\"trendInsulin\":0.336734693877551,\"utcOffset\":7200000,\"version\":1,\"wasBasalIOBUsed\":true,\"wasBolusIOBUsed\":true,\"wasCOBUsed\":true,\"wasGlucoseUsed\":true,\"wasSuperbolusUsed\":false,\"wasTempTargetUsed\":false,\"wasTrendUsed\":true,\"wereCarbsUsed\":false}",
@SerializedName("type") val type: String? = null, // string "Meal Bolus", "Correction Bolus", "Combo Bolus", "Temp Basal" type of bolus "NORMAL", "SMB", "FAKE_EXTENDED"
@SerializedName("isSMB") val isSMB: Boolean? = null, // boolean "Meal Bolus", "Correction Bolus", "Combo Bolus"
@SerializedName("enteredinsulin") val enteredinsulin: Double? = null, // number... "Combo Bolus" insulin is missing only enteredinsulin field found
@SerializedName("relative") val relative: Double? = null, // number... "Combo Bolus", "extendedEmulated" (not in doc see below)
@SerializedName("isEmulatingTempBasal") val isEmulatingTempBasal: Boolean? = null, // boolean "Combo Bolus", "extendedEmulated" (not in doc see below)
@SerializedName("isAnnouncement") val isAnnouncement: Boolean? = null, // boolean "Announcement"
@SerializedName("rate") val rate: Double? = null, // Double "Temp Basal" absolute rate (could be calculated with percent and profile information...)
@SerializedName("extendedEmulated") val extendedEmulated: RemoteTreatment? = null, // Gson of emulated EB
@SerializedName("timeshift") val timeshift: Long? = null, // integer "Profile Switch"
@SerializedName("percentage") val percentage: Int? = null // integer "Profile Switch"
) {
fun timestamp(): Long {
return date ?: mills ?: timestamp ?: fromISODateString(created_at)
return date ?: mills ?: timestamp ?: created_at?. let { fromISODateString(created_at) } ?: 0L
}
private fun fromISODateString(isoDateString: String): Long =

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.utils.extensions
package info.nightscout.core.ui.extensions
import android.widget.RadioGroup
import androidx.appcompat.widget.AppCompatRadioButton

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:tint="?attr/defaultTextColor"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="?attr/defaultTextColor"
android:pathData="M22,12l-4,-4v3H3v2h15v3z" />
</vector>

View file

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View file

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View file

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

View file

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View file

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View file

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View file

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View file

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View file

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Some files were not shown because too many files have changed in this diff Show more