NSCv3: process ProfileStore
This commit is contained in:
parent
9412f88672
commit
3bc62e9a01
16 changed files with 344 additions and 51 deletions
|
@ -180,7 +180,7 @@ class MainApp : DaggerApplication() {
|
|||
Thread.currentThread().uncaughtExceptionHandler?.uncaughtException(Thread.currentThread(), e)
|
||||
return@setErrorHandler
|
||||
}
|
||||
aapsLogger.warn(LTag.CORE, "Undeliverable exception received, not sure what to do", e.toString())
|
||||
aapsLogger.warn(LTag.CORE, "Undeliverable exception received, not sure what to do", e.localizedMessage)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,9 +25,9 @@ import info.nightscout.interfaces.iob.IobTotal
|
|||
interface OverviewData {
|
||||
|
||||
var rangeToDisplay: Int // for graph
|
||||
var toTime: Long
|
||||
var fromTime: Long
|
||||
var endTime: Long
|
||||
var toTime: Long // current time rounded up to 1 hour
|
||||
var fromTime: Long // toTime - range
|
||||
var endTime: Long // toTime + predictions
|
||||
|
||||
fun reset()
|
||||
fun initRange()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package info.nightscout.sdk
|
||||
|
||||
import android.content.Context
|
||||
import com.google.gson.JsonParser
|
||||
import info.nightscout.sdk.exceptions.DateHeaderOutOfToleranceException
|
||||
import info.nightscout.sdk.exceptions.InvalidAccessTokenException
|
||||
import info.nightscout.sdk.exceptions.InvalidFormatNightscoutException
|
||||
|
@ -30,6 +31,7 @@ import info.nightscout.sdk.utils.toNotNull
|
|||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.json.JSONObject
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -190,6 +192,14 @@ class NSAndroidClientImpl(
|
|||
deduplicatedIdentifier = null,
|
||||
lastModified = null
|
||||
)
|
||||
} else if (response.code() == 404) { // not found
|
||||
return@callWrapper CreateUpdateResponse(
|
||||
response = response.code(),
|
||||
identifier = null,
|
||||
isDeduplication = false,
|
||||
deduplicatedIdentifier = null,
|
||||
lastModified = null
|
||||
)
|
||||
} else {
|
||||
throw UnsuccessfullNightscoutException()
|
||||
}
|
||||
|
@ -307,6 +317,14 @@ class NSAndroidClientImpl(
|
|||
deduplicatedIdentifier = null,
|
||||
lastModified = null
|
||||
)
|
||||
} else if (response.code() == 404) { // not found
|
||||
return@callWrapper CreateUpdateResponse(
|
||||
response = response.code(),
|
||||
identifier = null,
|
||||
isDeduplication = false,
|
||||
deduplicatedIdentifier = null,
|
||||
lastModified = null
|
||||
)
|
||||
} else {
|
||||
throw UnsuccessfullNightscoutException()
|
||||
}
|
||||
|
@ -375,11 +393,58 @@ class NSAndroidClientImpl(
|
|||
deduplicatedIdentifier = null,
|
||||
lastModified = null
|
||||
)
|
||||
} else if (response.code() == 404) { // not found
|
||||
return@callWrapper CreateUpdateResponse(
|
||||
response = response.code(),
|
||||
identifier = null,
|
||||
isDeduplication = false,
|
||||
deduplicatedIdentifier = null,
|
||||
lastModified = null
|
||||
)
|
||||
} else {
|
||||
throw UnsuccessfullNightscoutException()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun createProfileStore(remoteProfileStore: JSONObject): CreateUpdateResponse = callWrapper(dispatcher) {
|
||||
remoteProfileStore.put("app", "AAPS")
|
||||
val response = api.createProfile(JsonParser.parseString(remoteProfileStore.toString()).asJsonObject)
|
||||
if (response.isSuccessful) {
|
||||
if (response.code() == 200) {
|
||||
return@callWrapper CreateUpdateResponse(
|
||||
response = response.code(),
|
||||
identifier = null,
|
||||
isDeduplication = true,
|
||||
deduplicatedIdentifier = null,
|
||||
lastModified = null
|
||||
)
|
||||
} else if (response.code() == 201) {
|
||||
return@callWrapper CreateUpdateResponse(
|
||||
response = response.code(),
|
||||
identifier = response.body()?.result?.identifier,
|
||||
isDeduplication = response.body()?.result?.isDeduplication ?: false,
|
||||
deduplicatedIdentifier = response.body()?.result?.deduplicatedIdentifier,
|
||||
lastModified = response.body()?.result?.lastModified
|
||||
)
|
||||
} else throw UnsuccessfullNightscoutException()
|
||||
} else {
|
||||
throw UnsuccessfullNightscoutException()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getLastProfileStore(): NSAndroidClient.ReadResponse<List<JSONObject>> = callWrapper(dispatcher) {
|
||||
|
||||
val response = api.getLastProfile()
|
||||
if (response.isSuccessful) {
|
||||
val eTagString = response.headers()["ETag"]
|
||||
val eTag = eTagString?.substring(3, eTagString.length - 1)?.toLong()
|
||||
return@callWrapper NSAndroidClient.ReadResponse(eTag, response.body()?.result.toNotNull())
|
||||
} else {
|
||||
throw UnsuccessfullNightscoutException()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private suspend fun <T> callWrapper(dispatcher: CoroutineDispatcher, block: suspend () -> T): T =
|
||||
withContext(dispatcher) {
|
||||
retry(
|
||||
|
|
|
@ -7,6 +7,7 @@ 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
|
||||
import org.json.JSONObject
|
||||
|
||||
interface NSAndroidClient {
|
||||
|
||||
|
@ -32,6 +33,9 @@ interface NSAndroidClient {
|
|||
suspend fun createDeviceStatus(remoteDeviceStatus: RemoteDeviceStatus): CreateUpdateResponse
|
||||
suspend fun getDeviceStatusModifiedSince(from: Long): List<RemoteDeviceStatus>
|
||||
|
||||
suspend fun createProfileStore(remoteProfileStore: JSONObject): CreateUpdateResponse
|
||||
suspend fun getLastProfileStore(): ReadResponse<List<JSONObject>>
|
||||
|
||||
suspend fun createTreatment(nsTreatment: NSTreatment): CreateUpdateResponse
|
||||
suspend fun updateTreatment(nsTreatment: NSTreatment): CreateUpdateResponse
|
||||
suspend fun getFoods(limit: Long): List<NSFood>
|
||||
|
|
|
@ -3,9 +3,11 @@ package info.nightscout.sdk.networking
|
|||
import android.content.Context
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonDeserializer
|
||||
import okhttp3.Cache
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import org.json.JSONObject
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
@ -85,7 +87,13 @@ internal object NetworkStackBuilder {
|
|||
return build()
|
||||
}
|
||||
|
||||
private fun provideGson(): Gson = GsonBuilder().create()
|
||||
private val deserializer: JsonDeserializer<JSONObject?> =
|
||||
JsonDeserializer<JSONObject?> { json, _, _ ->
|
||||
JSONObject(json.asJsonObject.toString())
|
||||
}
|
||||
private fun provideGson(): Gson = GsonBuilder().also {
|
||||
it.registerTypeAdapter(JSONObject::class.java, deserializer)
|
||||
}.create()
|
||||
|
||||
private const val OK_HTTP_CACHE_SIZE = 10L * 1024 * 1024
|
||||
private const val OK_HTTP_READ_TIMEOUT = 60L * 1000
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package info.nightscout.sdk.networking
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import info.nightscout.sdk.remotemodel.LastModified
|
||||
import info.nightscout.sdk.remotemodel.NSResponse
|
||||
import info.nightscout.sdk.remotemodel.RemoteCreateUpdateResponse
|
||||
|
@ -8,6 +9,7 @@ import info.nightscout.sdk.remotemodel.RemoteEntry
|
|||
import info.nightscout.sdk.remotemodel.RemoteFood
|
||||
import info.nightscout.sdk.remotemodel.RemoteStatusResponse
|
||||
import info.nightscout.sdk.remotemodel.RemoteTreatment
|
||||
import org.json.JSONObject
|
||||
import retrofit2.Response
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.GET
|
||||
|
@ -70,14 +72,21 @@ internal interface NightscoutRemoteService {
|
|||
|
||||
@GET("v3/food")
|
||||
suspend fun getFoods(@Query("limit") limit: Long): Response<NSResponse<List<RemoteFood>>>
|
||||
/*
|
||||
@GET("v3/food/history/{from}")
|
||||
suspend fun getFoodsModifiedSince(@Path("from") from: Long, @Query("limit") limit: Long): Response<NSResponse<List<RemoteFood>>>
|
||||
*/
|
||||
|
||||
/*
|
||||
@GET("v3/food/history/{from}")
|
||||
suspend fun getFoodsModifiedSince(@Path("from") from: Long, @Query("limit") limit: Long): Response<NSResponse<List<RemoteFood>>>
|
||||
*/
|
||||
@POST("v3/food")
|
||||
suspend fun createFood(@Body remoteFood: RemoteFood): Response<NSResponse<RemoteCreateUpdateResponse>>
|
||||
|
||||
@PATCH("v3/food")
|
||||
suspend fun updateFood(@Body remoteFood: RemoteFood): Response<NSResponse<RemoteCreateUpdateResponse>>
|
||||
|
||||
@GET("v3/profile?sort\$desc=date&limit=1")
|
||||
suspend fun getLastProfile(): Response<NSResponse<List<JSONObject>>>
|
||||
|
||||
@POST("v3/profile")
|
||||
suspend fun createProfile(@Body profile: JsonObject): Response<NSResponse<RemoteCreateUpdateResponse>>
|
||||
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ data class LastModified(
|
|||
@SerializedName("entries") var entries: Long = 0, // entries collection
|
||||
@SerializedName("profile") var profile: Long = 0, // profile collection
|
||||
@SerializedName("treatments") var treatments: Long = 0, // treatments collection
|
||||
@SerializedName("foods") var foods: Long = 0 // foods collection
|
||||
@SerializedName("foods") var foods: Long = 0, // foods collection
|
||||
@SerializedName("settings") var settings: Long = 0 // settings collection
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package info.nightscout.sdk.remotemodel
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.json.JSONObject
|
||||
|
||||
/**
|
||||
* DeviceStatus coming from uploader or AAPS
|
||||
*
|
||||
**/
|
||||
@Serializable
|
||||
data class RemoteProfileStore(
|
||||
@SerializedName("app") var app: String? = null,
|
||||
@SerializedName("identifier") val identifier: String? = null, // 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("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("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("created_at") val createdAt: String? = null, // string 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("date") val date: Long?, // date as milliseconds
|
||||
@SerializedName("startDate") val startDate: Long?, // record valid from
|
||||
@SerializedName("defaultProfile") val defaultProfile: String,// default profile in store
|
||||
|
||||
//@Serializable(with = JSONSerializer::class)
|
||||
@Contextual @SerializedName("store") val store: JSONObject
|
||||
) {
|
||||
/*
|
||||
@Serializable data class Store(
|
||||
val names: ArrayList<String>,
|
||||
val profiles: ArrayList<SimpleProfile>
|
||||
)
|
||||
|
||||
@Serializable data class SimpleProfile(
|
||||
@SerializedName("dia") val dia: Double,
|
||||
@SerializedName("carbratio") val carbratio: ArrayList<ProfileEntry>,
|
||||
@SerializedName("sens") val sens: ArrayList<ProfileEntry>,
|
||||
@SerializedName("basal") val basal: ArrayList<ProfileEntry>,
|
||||
@SerializedName("target_low") val target_low: ArrayList<ProfileEntry>,
|
||||
@SerializedName("target_high") val target_high: ArrayList<ProfileEntry>,
|
||||
@SerializedName("units") val units: String, // string The units for the glucose value, mg/dl or mmoll
|
||||
@SerializedName("timezone") val timezone: String
|
||||
)
|
||||
|
||||
@Serializable data class ProfileEntry(
|
||||
@SerializedName("time") val time: String,
|
||||
@SerializedName("timeAsSeconds") val timeAsSeconds: Long?,
|
||||
@SerializedName("value") val value: Double
|
||||
)
|
||||
*/
|
||||
}
|
|
@ -22,9 +22,9 @@ import info.nightscout.interfaces.plugin.ActivePlugin
|
|||
import info.nightscout.interfaces.plugin.PluginBase
|
||||
import info.nightscout.interfaces.plugin.PluginDescription
|
||||
import info.nightscout.interfaces.plugin.PluginType
|
||||
import info.nightscout.interfaces.profile.Instantiator
|
||||
import info.nightscout.interfaces.profile.Profile
|
||||
import info.nightscout.interfaces.profile.ProfileFunction
|
||||
import info.nightscout.interfaces.profile.Instantiator
|
||||
import info.nightscout.interfaces.profile.ProfileSource
|
||||
import info.nightscout.interfaces.profile.ProfileStore
|
||||
import info.nightscout.interfaces.profile.PureProfile
|
||||
|
@ -413,6 +413,7 @@ class ProfilePlugin @Inject constructor(
|
|||
}
|
||||
if (numOfProfiles > 0) json.put("defaultProfile", currentProfile()?.name)
|
||||
val startDate = sp.getLong(info.nightscout.core.utils.R.string.key_local_profile_last_change, dateUtil.now())
|
||||
json.put("date", startDate)
|
||||
json.put("startDate", dateUtil.toISOAsUTC(startDate))
|
||||
json.put("store", store)
|
||||
} catch (e: JSONException) {
|
||||
|
|
|
@ -22,6 +22,7 @@ import info.nightscout.plugins.sync.nsclientV3.workers.LoadBgWorker
|
|||
import info.nightscout.plugins.sync.nsclientV3.workers.LoadDeviceStatusWorker
|
||||
import info.nightscout.plugins.sync.nsclientV3.workers.LoadFoodsWorker
|
||||
import info.nightscout.plugins.sync.nsclientV3.workers.LoadLastModificationWorker
|
||||
import info.nightscout.plugins.sync.nsclientV3.workers.LoadProfileStoreWorker
|
||||
import info.nightscout.plugins.sync.nsclientV3.workers.LoadStatusWorker
|
||||
import info.nightscout.plugins.sync.nsclientV3.workers.LoadTreatmentsWorker
|
||||
import info.nightscout.plugins.sync.nsclientV3.workers.ProcessFoodWorker
|
||||
|
@ -49,6 +50,7 @@ abstract class SyncModule {
|
|||
@ContributesAndroidInjector abstract fun contributesLoadLastModificationWorker(): LoadLastModificationWorker
|
||||
@ContributesAndroidInjector abstract fun contributesLoadBgWorker(): LoadBgWorker
|
||||
@ContributesAndroidInjector abstract fun contributesLoadFoodsWorker(): LoadFoodsWorker
|
||||
@ContributesAndroidInjector abstract fun contributesLoadProfileStoreWorker(): LoadProfileStoreWorker
|
||||
@ContributesAndroidInjector abstract fun contributesStoreBgWorker(): StoreDataForDbImpl.StoreBgWorker
|
||||
@ContributesAndroidInjector abstract fun contributesStoreFoodWorker(): StoreDataForDbImpl.StoreFoodWorker
|
||||
@ContributesAndroidInjector abstract fun contributesTreatmentWorker(): LoadTreatmentsWorker
|
||||
|
|
|
@ -5,6 +5,7 @@ import info.nightscout.database.impl.AppRepository
|
|||
import info.nightscout.interfaces.plugin.ActivePlugin
|
||||
import info.nightscout.interfaces.profile.ProfileFunction
|
||||
import info.nightscout.interfaces.sync.DataSyncSelector
|
||||
import info.nightscout.interfaces.utils.JsonHelper
|
||||
import info.nightscout.plugins.sync.R
|
||||
import info.nightscout.rx.logging.AAPSLogger
|
||||
import info.nightscout.rx.logging.LTag
|
||||
|
@ -764,7 +765,11 @@ class DataSyncSelectorImplementation @Inject constructor(
|
|||
if (lastChange == 0L) return
|
||||
if (lastChange > lastSync) {
|
||||
if (activePlugin.activeProfileSource.profile?.allProfilesValid != true) return
|
||||
val profileJson = activePlugin.activeProfileSource.profile?.data ?: return
|
||||
val profileStore = activePlugin.activeProfileSource.profile
|
||||
val profileJson = profileStore?.data ?: return
|
||||
// add for v3
|
||||
if (JsonHelper.safeGetLongAllowNull(profileJson, "date") == null)
|
||||
profileJson.put("date", profileStore.getStartDate())
|
||||
activePlugin.activeNsClient?.nsAdd("profile", DataSyncSelector.PairProfileStore(profileJson, dateUtil.now()), "")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,11 +16,12 @@ import com.google.gson.GsonBuilder
|
|||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.core.utils.fabric.FabricPrivacy
|
||||
import info.nightscout.core.validators.ValidatingEditTextPreference
|
||||
import info.nightscout.database.ValueWrapper
|
||||
import info.nightscout.database.entities.interfaces.TraceableDBEntry
|
||||
import info.nightscout.database.impl.AppRepository
|
||||
import info.nightscout.interfaces.Config
|
||||
import info.nightscout.interfaces.Constants
|
||||
import info.nightscout.interfaces.nsclient.NSAlarm
|
||||
import info.nightscout.interfaces.nsclient.StoreDataForDb
|
||||
import info.nightscout.interfaces.plugin.PluginBase
|
||||
import info.nightscout.interfaces.plugin.PluginDescription
|
||||
import info.nightscout.interfaces.plugin.PluginType
|
||||
|
@ -98,9 +99,9 @@ class NSClientV3Plugin @Inject constructor(
|
|||
private val config: Config,
|
||||
private val dateUtil: DateUtil,
|
||||
private val uiInteraction: UiInteraction,
|
||||
private val storeDataForDb: StoreDataForDb,
|
||||
private val dataSyncSelector: DataSyncSelector,
|
||||
private val profileFunction: ProfileFunction
|
||||
private val profileFunction: ProfileFunction,
|
||||
private val repository: AppRepository
|
||||
) : NsClient, Sync, PluginBase(
|
||||
PluginDescription()
|
||||
.mainType(PluginType.SYNC)
|
||||
|
@ -116,7 +117,7 @@ class NSClientV3Plugin @Inject constructor(
|
|||
companion object {
|
||||
|
||||
val JOB_NAME: String = this::class.java.simpleName
|
||||
val REFRESH_INTERVAL = T.mins(5).msecs()
|
||||
val REFRESH_INTERVAL = T.secs(30).msecs()
|
||||
}
|
||||
|
||||
private val disposable = CompositeDisposable()
|
||||
|
@ -196,18 +197,26 @@ class NSClientV3Plugin @Inject constructor(
|
|||
disposable += rxBus
|
||||
.toObservable(EventNewBG::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({ scheduleExecution("NEW_BG") }, fabricPrivacy::logException)
|
||||
.subscribe({ delayAndScheduleExecution("NEW_BG") }, fabricPrivacy::logException)
|
||||
disposable += rxBus
|
||||
.toObservable(EventNewHistoryData::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({ scheduleExecution("NEW_DATA") }, fabricPrivacy::logException)
|
||||
.subscribe({ delayAndScheduleExecution("NEW_DATA") }, fabricPrivacy::logException)
|
||||
|
||||
runLoop = Runnable {
|
||||
handler.postDelayed(runLoop, REFRESH_INTERVAL)
|
||||
executeLoop("MAIN_LOOP")
|
||||
repository.getLastGlucoseValueWrapped().blockingGet().let {
|
||||
// if last value is older than 5 min or there is no bg
|
||||
if (it is ValueWrapper.Existing) {
|
||||
if (it.value.timestamp < dateUtil.now() - T.mins(5).plus(T.secs(20)).msecs())
|
||||
executeLoop("MAIN_LOOP", forceNew = false)
|
||||
else
|
||||
rxBus.send(EventNSClientNewLog("RECENT", "No need to load"))
|
||||
} else executeLoop("MAIN_LOOP", forceNew = false)
|
||||
}
|
||||
}
|
||||
handler.postDelayed(runLoop, REFRESH_INTERVAL)
|
||||
executeLoop("START")
|
||||
executeLoop("START", forceNew = false)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
|
@ -273,7 +282,7 @@ class NSClientV3Plugin @Inject constructor(
|
|||
}
|
||||
|
||||
override fun resend(reason: String) {
|
||||
executeLoop("RESEND")
|
||||
executeLoop("RESEND", forceNew = false)
|
||||
}
|
||||
|
||||
override fun pause(newState: Boolean) {
|
||||
|
@ -331,6 +340,36 @@ class NSClientV3Plugin @Inject constructor(
|
|||
enum class Operation { CREATE, UPDATE }
|
||||
|
||||
private val gson: Gson = GsonBuilder().create()
|
||||
private fun dbOperationProfileStore(collection: String = "profile", dataPair: DataSyncSelector.DataPair, progress: String) {
|
||||
val data = (dataPair as DataSyncSelector.PairProfileStore).value
|
||||
scope.launch {
|
||||
try {
|
||||
rxBus.send(EventNSClientNewLog("ADD $collection", "Sent ${dataPair.javaClass.simpleName} $data $progress"))
|
||||
nsAndroidClient?.createProfileStore(data)?.let { result ->
|
||||
when (result.response) {
|
||||
200 -> rxBus.send(EventNSClientNewLog("UPDATED", "OK ProfileStore"))
|
||||
201 -> rxBus.send(EventNSClientNewLog("ADDED", "OK ProfileStore"))
|
||||
404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
|
||||
|
||||
else -> {
|
||||
rxBus.send(EventNSClientNewLog("ERROR", "ProfileStore"))
|
||||
return@launch
|
||||
}
|
||||
}
|
||||
// if (result.response == 201) { // created
|
||||
// dataPair.value.interfaceIDs.nightscoutId = result.identifier
|
||||
// storeDataForDb.nsIdDeviceStatuses.add(dataPair.value)
|
||||
// storeDataForDb.scheduleNsIdUpdate()
|
||||
// }
|
||||
dataSyncSelector.confirmLastProfileStore(dataPair.id)
|
||||
dataSyncSelector.processChangedProfileStore()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
aapsLogger.error(LTag.NSCLIENT, "Upload exception", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun dbOperationDeviceStatus(collection: String = "devicestatus", dataPair: DataSyncSelector.DataPair, progress: String) {
|
||||
val data = (dataPair as DataSyncSelector.PairDeviceStatus).value.toRemoteDeviceStatus()
|
||||
scope.launch {
|
||||
|
@ -340,6 +379,7 @@ class NSClientV3Plugin @Inject constructor(
|
|||
when (result.response) {
|
||||
200 -> rxBus.send(EventNSClientNewLog("UPDATED", "OK ${dataPair.value.javaClass.simpleName}"))
|
||||
201 -> rxBus.send(EventNSClientNewLog("ADDED", "OK ${dataPair.value.javaClass.simpleName} ${result.identifier}"))
|
||||
404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
|
||||
|
||||
else -> {
|
||||
rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} "))
|
||||
|
@ -389,6 +429,7 @@ class NSClientV3Plugin @Inject constructor(
|
|||
200 -> rxBus.send(EventNSClientNewLog("UPDATED", "OK ${dataPair.value.javaClass.simpleName}"))
|
||||
201 -> rxBus.send(EventNSClientNewLog("ADDED", "OK ${dataPair.value.javaClass.simpleName}"))
|
||||
400 -> rxBus.send(EventNSClientNewLog("FAIL", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
|
||||
404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
|
||||
|
||||
else -> {
|
||||
rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} "))
|
||||
|
@ -443,6 +484,7 @@ class NSClientV3Plugin @Inject constructor(
|
|||
200 -> rxBus.send(EventNSClientNewLog("UPDATED", "OK ${dataPair.value.javaClass.simpleName}"))
|
||||
201 -> rxBus.send(EventNSClientNewLog("ADDED", "OK ${dataPair.value.javaClass.simpleName}"))
|
||||
400 -> rxBus.send(EventNSClientNewLog("FAIL", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
|
||||
404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
|
||||
|
||||
else -> {
|
||||
rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} "))
|
||||
|
@ -525,6 +567,7 @@ class NSClientV3Plugin @Inject constructor(
|
|||
200 -> rxBus.send(EventNSClientNewLog("UPDATED", "OK ${dataPair.value.javaClass.simpleName}"))
|
||||
201 -> rxBus.send(EventNSClientNewLog("ADDED", "OK ${dataPair.value.javaClass.simpleName}"))
|
||||
400 -> rxBus.send(EventNSClientNewLog("FAIL", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
|
||||
404 -> rxBus.send(EventNSClientNewLog("NOT_FOUND", "${dataPair.value.javaClass.simpleName} ${result.errorResponse}"))
|
||||
|
||||
else -> {
|
||||
rxBus.send(EventNSClientNewLog("ERROR", "${dataPair.value.javaClass.simpleName} "))
|
||||
|
@ -642,6 +685,7 @@ class NSClientV3Plugin @Inject constructor(
|
|||
|
||||
private fun dbOperation(collection: String, dataPair: DataSyncSelector.DataPair, progress: String, operation: Operation) {
|
||||
when (collection) {
|
||||
"profile" -> dbOperationProfileStore(dataPair = dataPair, progress = progress)
|
||||
"devicestatus" -> dbOperationDeviceStatus(dataPair = dataPair, progress = progress)
|
||||
"entries" -> dbOperationEntries(dataPair = dataPair, progress = progress, operation = operation)
|
||||
"food" -> dbOperationFood(dataPair = dataPair, progress = progress, operation = operation)
|
||||
|
@ -653,18 +697,20 @@ class NSClientV3Plugin @Inject constructor(
|
|||
sp.putString(R.string.key_ns_client_v3_last_modified, Json.encodeToString(LastModified.serializer(), lastLoadedSrvModified))
|
||||
}
|
||||
|
||||
fun scheduleNewExecution() {
|
||||
fun scheduleIrregularExecution() {
|
||||
var origin = "5_MIN_AFTER_BG"
|
||||
var forceNew = true
|
||||
var toTime = lastLoadedSrvModified.collections.entries + T.mins(5).plus(T.secs(10)).msecs()
|
||||
if (toTime < dateUtil.now()) {
|
||||
toTime = dateUtil.now() + T.mins(1).plus(T.secs(0)).msecs()
|
||||
origin = "1_MIN_OLD_DATA"
|
||||
forceNew = false
|
||||
}
|
||||
handler.postDelayed({ executeLoop(origin) }, toTime - dateUtil.now())
|
||||
handler.postDelayed({ executeLoop(origin, forceNew = forceNew) }, toTime - dateUtil.now())
|
||||
rxBus.send(EventNSClientNewLog("NEXT", dateUtil.dateAndTimeAndSecondsString(toTime)))
|
||||
}
|
||||
|
||||
private fun executeLoop(origin: String) {
|
||||
private fun executeLoop(origin: String, forceNew: Boolean) {
|
||||
if (sp.getBoolean(R.string.key_ns_client_paused, false)) {
|
||||
rxBus.send(EventNSClientNewLog("RUN", "paused"))
|
||||
return
|
||||
|
@ -673,25 +719,28 @@ class NSClientV3Plugin @Inject constructor(
|
|||
rxBus.send(EventNSClientNewLog("RUN", blockingReason))
|
||||
return
|
||||
}
|
||||
if (workIsRunning(arrayOf(JOB_NAME)))
|
||||
if (workIsRunning(arrayOf(JOB_NAME))) {
|
||||
rxBus.send(EventNSClientNewLog("RUN", "Already running $origin"))
|
||||
else {
|
||||
rxBus.send(EventNSClientNewLog("RUN", "Starting next round $origin"))
|
||||
WorkManager.getInstance(context)
|
||||
.beginUniqueWork(
|
||||
"NSCv3Load",
|
||||
ExistingWorkPolicy.REPLACE,
|
||||
OneTimeWorkRequest.Builder(LoadStatusWorker::class.java).build()
|
||||
)
|
||||
.then(OneTimeWorkRequest.Builder(LoadLastModificationWorker::class.java).build())
|
||||
.then(OneTimeWorkRequest.Builder(LoadBgWorker::class.java).build())
|
||||
// Other Workers are enqueued after BG finish
|
||||
// LoadTreatmentsWorker
|
||||
// LoadFoodsWorker
|
||||
// LoadDeviceStatusWorker
|
||||
// DataSyncWorker
|
||||
.enqueue()
|
||||
if (!forceNew) return
|
||||
// Wait for end and start new cycle
|
||||
while (workIsRunning(arrayOf(JOB_NAME))) Thread.sleep(5000)
|
||||
}
|
||||
rxBus.send(EventNSClientNewLog("RUN", "Starting next round $origin"))
|
||||
WorkManager.getInstance(context)
|
||||
.beginUniqueWork(
|
||||
JOB_NAME,
|
||||
ExistingWorkPolicy.REPLACE,
|
||||
OneTimeWorkRequest.Builder(LoadStatusWorker::class.java).build()
|
||||
)
|
||||
.then(OneTimeWorkRequest.Builder(LoadLastModificationWorker::class.java).build())
|
||||
.then(OneTimeWorkRequest.Builder(LoadBgWorker::class.java).build())
|
||||
// Other Workers are enqueued after BG finish
|
||||
// LoadTreatmentsWorker
|
||||
// LoadFoodsWorker
|
||||
// LoadProfileStoreWorker
|
||||
// LoadDeviceStatusWorker
|
||||
// DataSyncWorker
|
||||
.enqueue()
|
||||
}
|
||||
|
||||
private fun workIsRunning(workNames: Array<String>): Boolean {
|
||||
|
@ -704,12 +753,12 @@ class NSClientV3Plugin @Inject constructor(
|
|||
|
||||
private val eventWorker = Executors.newSingleThreadScheduledExecutor()
|
||||
private var scheduledEventPost: ScheduledFuture<*>? = null
|
||||
private fun scheduleExecution(origin: String) {
|
||||
private fun delayAndScheduleExecution(origin: String) {
|
||||
class PostRunnable : Runnable {
|
||||
|
||||
override fun run() {
|
||||
scheduledEventPost = null
|
||||
executeLoop(origin)
|
||||
executeLoop(origin, forceNew = true)
|
||||
}
|
||||
}
|
||||
// cancel waiting task to prevent sending multiple posts
|
||||
|
|
|
@ -57,7 +57,7 @@ class LoadBgWorker(
|
|||
sgvs = response.values
|
||||
response.lastServerModified?.let { nsClientV3Plugin.lastLoadedSrvModified.collections.entries = it }
|
||||
nsClientV3Plugin.storeLastLoadedSrvModified()
|
||||
nsClientV3Plugin.scheduleNewExecution() // Idea is to run after 5 min after last BG
|
||||
nsClientV3Plugin.scheduleIrregularExecution() // Idea is to run after 5 min after last BG
|
||||
}
|
||||
aapsLogger.debug("SGVS: $sgvs")
|
||||
if (sgvs.isNotEmpty()) {
|
||||
|
@ -77,8 +77,7 @@ class LoadBgWorker(
|
|||
nsClientV3Plugin.lastLoadedSrvModified.collections.entries = lastLoaded
|
||||
nsClientV3Plugin.storeLastLoadedSrvModified()
|
||||
}
|
||||
rxBus.send(EventNSClientNewLog("RCV END", "No SGVs from ${dateUtil
|
||||
.dateAndTimeAndSecondsString(lastLoaded)}"))
|
||||
rxBus.send(EventNSClientNewLog("RCV END", "No SGVs from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}"))
|
||||
WorkManager.getInstance(context)
|
||||
.beginUniqueWork(
|
||||
NSClientV3Plugin.JOB_NAME,
|
||||
|
|
|
@ -9,7 +9,6 @@ import androidx.work.workDataOf
|
|||
import info.nightscout.core.utils.receivers.DataWorkerStorage
|
||||
import info.nightscout.core.utils.worker.LoggingWorker
|
||||
import info.nightscout.interfaces.nsclient.StoreDataForDb
|
||||
import info.nightscout.interfaces.workflow.WorkerClasses
|
||||
import info.nightscout.plugins.sync.nsShared.StoreDataForDbImpl
|
||||
import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin
|
||||
import info.nightscout.rx.bus.RxBus
|
||||
|
@ -30,7 +29,6 @@ class LoadFoodsWorker(
|
|||
@Inject lateinit var nsClientV3Plugin: NSClientV3Plugin
|
||||
@Inject lateinit var dateUtil: DateUtil
|
||||
@Inject lateinit var storeDataForDb: StoreDataForDb
|
||||
@Inject lateinit var workerClasses: WorkerClasses
|
||||
|
||||
override fun doWorkAndLog(): Result {
|
||||
val nsAndroidClient = nsClientV3Plugin.nsAndroidClient ?: return Result.failure(workDataOf("Error" to "AndroidClient is null"))
|
||||
|
@ -51,7 +49,7 @@ class LoadFoodsWorker(
|
|||
.setInputData(dataWorkerStorage.storeInputData(foods))
|
||||
.build()
|
||||
).then(OneTimeWorkRequest.Builder(StoreDataForDbImpl.StoreFoodWorker::class.java).build())
|
||||
.then(OneTimeWorkRequest.Builder(LoadDeviceStatusWorker::class.java).build())
|
||||
.then(OneTimeWorkRequest.Builder(LoadProfileStoreWorker::class.java).build())
|
||||
.enqueue()
|
||||
} else {
|
||||
rxBus.send(EventNSClientNewLog("RCV", "FOOD skipped"))
|
||||
|
@ -59,7 +57,7 @@ class LoadFoodsWorker(
|
|||
.enqueueUniqueWork(
|
||||
NSClientV3Plugin.JOB_NAME,
|
||||
ExistingWorkPolicy.APPEND_OR_REPLACE,
|
||||
OneTimeWorkRequest.Builder(LoadDeviceStatusWorker::class.java).build()
|
||||
OneTimeWorkRequest.Builder(LoadProfileStoreWorker::class.java).build()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
package info.nightscout.plugins.sync.nsclientV3.workers
|
||||
|
||||
import android.content.Context
|
||||
import androidx.work.ExistingWorkPolicy
|
||||
import androidx.work.OneTimeWorkRequest
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.WorkerParameters
|
||||
import androidx.work.workDataOf
|
||||
import info.nightscout.core.utils.receivers.DataWorkerStorage
|
||||
import info.nightscout.core.utils.worker.LoggingWorker
|
||||
import info.nightscout.interfaces.utils.JsonHelper
|
||||
import info.nightscout.interfaces.workflow.WorkerClasses
|
||||
import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin
|
||||
import info.nightscout.rx.bus.RxBus
|
||||
import info.nightscout.rx.events.EventNSClientNewLog
|
||||
import info.nightscout.sdk.interfaces.NSAndroidClient
|
||||
import info.nightscout.shared.utils.DateUtil
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.json.JSONObject
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.max
|
||||
|
||||
class LoadProfileStoreWorker(
|
||||
context: Context,
|
||||
params: WorkerParameters
|
||||
) : LoggingWorker(context, params) {
|
||||
|
||||
@Inject lateinit var dataWorkerStorage: DataWorkerStorage
|
||||
@Inject lateinit var rxBus: RxBus
|
||||
@Inject lateinit var context: Context
|
||||
@Inject lateinit var nsClientV3Plugin: NSClientV3Plugin
|
||||
@Inject lateinit var dateUtil: DateUtil
|
||||
@Inject lateinit var workerClasses: WorkerClasses
|
||||
|
||||
override fun doWorkAndLog(): Result {
|
||||
val nsAndroidClient = nsClientV3Plugin.nsAndroidClient ?: return Result.failure(workDataOf("Error" to "AndroidClient is null"))
|
||||
var ret = Result.success()
|
||||
|
||||
val lastLoaded = max(nsClientV3Plugin.lastLoadedSrvModified.collections.profile, 0)
|
||||
runBlocking {
|
||||
if ((nsClientV3Plugin.newestDataOnServer?.collections?.profile ?: Long.MAX_VALUE) > lastLoaded)
|
||||
try {
|
||||
val response: NSAndroidClient.ReadResponse<List<JSONObject>> = nsAndroidClient.getLastProfileStore()
|
||||
val profiles = response.values
|
||||
if (profiles.size == 1) {
|
||||
val profile = profiles[0]
|
||||
JsonHelper.safeGetLongAllowNull(profile, "srvModified")?.let { nsClientV3Plugin.lastLoadedSrvModified.collections.profile = it }
|
||||
nsClientV3Plugin.storeLastLoadedSrvModified()
|
||||
aapsLogger.debug("PROFILE: $profile")
|
||||
rxBus.send(EventNSClientNewLog("RCV", "1 PROFILE from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}"))
|
||||
WorkManager.getInstance(context)
|
||||
.beginUniqueWork(
|
||||
NSClientV3Plugin.JOB_NAME,
|
||||
ExistingWorkPolicy.APPEND_OR_REPLACE,
|
||||
OneTimeWorkRequest.Builder((workerClasses.nsProfileWorker))
|
||||
.setInputData(dataWorkerStorage.storeInputData(profile))
|
||||
.build()
|
||||
).then(OneTimeWorkRequest.Builder(LoadDeviceStatusWorker::class.java).build())
|
||||
.enqueue()
|
||||
} else {
|
||||
rxBus.send(EventNSClientNewLog("RCV END", "No new PROFILE from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}"))
|
||||
WorkManager.getInstance(context)
|
||||
.enqueueUniqueWork(
|
||||
NSClientV3Plugin.JOB_NAME,
|
||||
ExistingWorkPolicy.APPEND_OR_REPLACE,
|
||||
OneTimeWorkRequest.Builder(LoadDeviceStatusWorker::class.java).build()
|
||||
)
|
||||
}
|
||||
} catch (error: Exception) {
|
||||
aapsLogger.error("Error: ", error)
|
||||
ret = Result.failure(workDataOf("Error" to error.toString()))
|
||||
}
|
||||
else {
|
||||
rxBus.send(EventNSClientNewLog("RCV END", "No PROFILE from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}"))
|
||||
WorkManager.getInstance(context)
|
||||
.enqueueUniqueWork(
|
||||
NSClientV3Plugin.JOB_NAME,
|
||||
ExistingWorkPolicy.APPEND_OR_REPLACE,
|
||||
OneTimeWorkRequest.Builder(LoadDeviceStatusWorker::class.java).build()
|
||||
)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
}
|
|
@ -69,7 +69,7 @@ internal class NSClientV3PluginTest : TestBaseWithProfile() {
|
|||
sut =
|
||||
NSClientV3Plugin(
|
||||
injector, aapsLogger, aapsSchedulers, rxBus, rh, context, fabricPrivacy,
|
||||
sp, nsClientReceiverDelegate, config, dateUtil, uiInteraction, storeDataForDb, dataSyncSelector, mockedProfileFunction
|
||||
sp, nsClientReceiverDelegate, config, dateUtil, uiInteraction, dataSyncSelector, mockedProfileFunction, repository
|
||||
)
|
||||
sut.nsAndroidClient = nsAndroidClient
|
||||
`when`(mockedProfileFunction.getProfile(anyLong())).thenReturn(validProfile)
|
||||
|
@ -519,4 +519,22 @@ internal class NSClientV3PluginTest : TestBaseWithProfile() {
|
|||
verify(dataSyncSelector, Times(2)).confirmLastTherapyEventIdIfGreater(1000)
|
||||
verify(dataSyncSelector, Times(2)).processChangedTherapyEvents()
|
||||
}
|
||||
|
||||
@Test
|
||||
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
|
||||
fun nsAddProfile() = runTest {
|
||||
sut.scope = CoroutineScope(UnconfinedTestDispatcher(testScheduler))
|
||||
|
||||
val dataPair = DataSyncSelector.PairProfileStore(getValidProfileStore().data, 1000)
|
||||
// create
|
||||
`when`(nsAndroidClient.createProfileStore(anyObject())).thenReturn(CreateUpdateResponse(201, null))
|
||||
sut.nsAdd("profile", dataPair, "1/3")
|
||||
verify(dataSyncSelector, Times(1)).confirmLastProfileStore(1000)
|
||||
verify(dataSyncSelector, Times(1)).processChangedProfileStore()
|
||||
// update
|
||||
`when`(nsAndroidClient.updateTreatment(anyObject())).thenReturn(CreateUpdateResponse(200, null))
|
||||
sut.nsUpdate("profile", dataPair, "1/3")
|
||||
verify(dataSyncSelector, Times(2)).confirmLastProfileStore(1000)
|
||||
verify(dataSyncSelector, Times(2)).processChangedProfileStore()
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue