Fix Open Humans Uploader

This commit is contained in:
TebbeUbben 2020-05-28 15:19:35 +02:00
parent 5533f87bbc
commit fa8e784733
5 changed files with 65 additions and 35 deletions

View file

@ -106,7 +106,7 @@ public class DetermineBasalAdapterMAJS {
log.debug("Result: " + result); log.debug("Result: " + result);
try { try {
JSONObject resultJson = new JSONObject(result); JSONObject resultJson = new JSONObject(result);
OpenHumansUploader.INSTANCE.enqueueAMAData(mProfile, mGlucoseStatus, mIobData, mMealData, mCurrentTemp, resultJson); OpenHumansUploader.INSTANCE.enqueueMAData(mProfile, mGlucoseStatus, mIobData, mMealData, mCurrentTemp, resultJson);
determineBasalResultMA = new DetermineBasalResultMA(jsResult, resultJson); determineBasalResultMA = new DetermineBasalResultMA(jsResult, resultJson);
} catch (JSONException e) { } catch (JSONException e) {
log.error("Unhandled exception", e); log.error("Unhandled exception", e);

View file

@ -13,8 +13,10 @@ class OHUploadWorker(
) : RxWorker(context, workerParameters) { ) : RxWorker(context, workerParameters) {
override fun createWork() = Single.defer { override fun createWork() = Single.defer {
val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as? WifiManager
if (SP.getBoolean("key_oh_wifi_only", true) && wifiManager.isWifiEnabled && wifiManager.connectionInfo.networkId != -1) { val wifiOnly = SP.getBoolean("key_oh_wifi_only", true)
val isConnectedToWifi = wifiManager?.isWifiEnabled ?: false && wifiManager?.connectionInfo?.networkId != -1
if (!wifiOnly || (wifiOnly && isConnectedToWifi)) {
OpenHumansUploader.uploadData() OpenHumansUploader.uploadData()
.andThen(Single.just(Result.success())) .andThen(Single.just(Result.success()))
.onErrorResumeNext { Single.just(Result.retry()) } .onErrorResumeNext { Single.just(Result.retry()) }

View file

@ -6,12 +6,12 @@ import io.reactivex.Completable
import io.reactivex.Single import io.reactivex.Single
import io.reactivex.disposables.Disposables import io.reactivex.disposables.Disposables
import okhttp3.* import okhttp3.*
import okio.BufferedSink
import org.json.JSONArray import org.json.JSONArray
import org.json.JSONObject import org.json.JSONObject
import java.io.IOException import java.io.IOException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
import okio.BufferedSink
class OpenHumansAPI( class OpenHumansAPI(
private val baseUrl: String, private val baseUrl: String,
@ -32,7 +32,7 @@ class OpenHumansAPI(
fun refreshAccessToken(refreshToken: String): Single<OAuthTokens> = sendTokenRequest(FormBody.Builder() fun refreshAccessToken(refreshToken: String): Single<OAuthTokens> = sendTokenRequest(FormBody.Builder()
.add("grant_type", "refresh_token") .add("grant_type", "refresh_token")
.add("redirect_uri", redirectUri) .add("redirect_uri", redirectUri)
.add("code", refreshToken) .add("refresh_token", refreshToken)
.build()) .build())
private fun sendTokenRequest(body: FormBody) = Request.Builder() private fun sendTokenRequest(body: FormBody) = Request.Builder()
@ -49,8 +49,10 @@ class OpenHumansAPI(
if (jsonObject == null) throw OHHttpException(response.code, response.message, "No body") if (jsonObject == null) throw OHHttpException(response.code, response.message, "No body")
if (!jsonObject.has("expires_in")) throw OHMissingFieldException("expires_in") if (!jsonObject.has("expires_in")) throw OHMissingFieldException("expires_in")
OAuthTokens( OAuthTokens(
accessToken = jsonObject.getString("access_token") ?: throw OHMissingFieldException("access_token"), accessToken = jsonObject.getString("access_token")
refreshToken = jsonObject.getString("refresh_token") ?: throw OHMissingFieldException("refresh_token"), ?: throw OHMissingFieldException("access_token"),
refreshToken = jsonObject.getString("refresh_token")
?: throw OHMissingFieldException("refresh_token"),
expiresAt = response.sentRequestAtMillis + jsonObject.getInt("expires_in") * 1000L expiresAt = response.sentRequestAtMillis + jsonObject.getInt("expires_in") * 1000L
) )
} }
@ -61,7 +63,10 @@ class OpenHumansAPI(
.get() .get()
.build() .build()
.toSingle() .toSingle()
.map { it.jsonBody.getString("project_member_id") ?: throw OHMissingFieldException("project_member_id") } .map {
it.jsonBody.getString("project_member_id")
?: throw OHMissingFieldException("project_member_id")
}
fun prepareFileUpload(accessToken: String, fileName: String, metadata: FileMetadata): Single<PreparedUpload> = Request.Builder() fun prepareFileUpload(accessToken: String, fileName: String, metadata: FileMetadata): Single<PreparedUpload> = Request.Builder()
.url("$baseUrl/api/direct-sharing/project/files/upload/direct/?access_token=$accessToken") .url("$baseUrl/api/direct-sharing/project/files/upload/direct/?access_token=$accessToken")
@ -109,7 +114,6 @@ class OpenHumansAPI(
.doOnSuccess { it.jsonBody } .doOnSuccess { it.jsonBody }
.ignoreElement() .ignoreElement()
private fun Request.toSingle() = Single.create<Response> { private fun Request.toSingle() = Single.create<Response> {
val call = client.newCall(this) val call = client.newCall(this)
call.enqueue(object : Callback { call.enqueue(object : Callback {
@ -124,11 +128,13 @@ class OpenHumansAPI(
it.setDisposable(Disposables.fromRunnable { call.cancel() }) it.setDisposable(Disposables.fromRunnable { call.cancel() })
} }
private val Response.jsonBody get() = use { _ -> private val Response.jsonBody
val jsonObject = body?.let { JSONObject(it.string()) } ?: throw OHHttpException(code, message, null) get() = use { _ ->
if (!isSuccessful) throw OHHttpException(code, message, jsonObject.getString("detail")) val jsonObject = body?.let { JSONObject(it.string()) }
jsonObject ?: throw OHHttpException(code, message, null)
} if (!isSuccessful) throw OHHttpException(code, message, jsonObject.getString("detail"))
jsonObject
}
data class OAuthTokens( data class OAuthTokens(
val accessToken: String, val accessToken: String,
@ -144,6 +150,7 @@ class OpenHumansAPI(
val startDate: Long? = null, val startDate: Long? = null,
val endDate: Long? = null val endDate: Long? = null
) { ) {
fun toJSON(): JSONObject { fun toJSON(): JSONObject {
val jsonObject = JSONObject() val jsonObject = JSONObject()
jsonObject.put("tags", JSONArray().apply { tags.forEach { put(it) } }) jsonObject.put("tags", JSONArray().apply { tags.forEach { put(it) } })
@ -166,12 +173,14 @@ class OpenHumansAPI(
val meaning: String, val meaning: String,
val detail: String? val detail: String?
) : RuntimeException() { ) : RuntimeException() {
override val message: String get() = toString() override val message: String get() = toString()
} }
data class OHMissingFieldException( data class OHMissingFieldException(
val name: String val name: String
) : RuntimeException() { ) : RuntimeException() {
override val message: String get() = toString() override val message: String get() = toString()
} }

View file

@ -52,16 +52,16 @@ object OpenHumansUploader : PluginBase(
private val log = LoggerFactory.getLogger(L.OPENHUMANS) private val log = LoggerFactory.getLogger(L.OPENHUMANS)
const val OPEN_HUMANS_URL = "https://www.openhumans.org" private const val OPEN_HUMANS_URL = "https://www.openhumans.org"
const val CLIENT_ID = "oie6DvnaEOagTxSoD6BukkLPwDhVr6cMlN74Ihz1" private const val CLIENT_ID = "oie6DvnaEOagTxSoD6BukkLPwDhVr6cMlN74Ihz1"
const val CLIENT_SECRET = "jR0N8pkH1jOwtozHc7CsB1UPcJzFN95ldHcK4VGYIApecr8zGJox0v06xLwPLMASScngT12aIaIHXAVCJeKquEXAWG1XekZdbubSpccgNiQBmuVmIF8nc1xSKSNJltCf" private const val CLIENT_SECRET = "jR0N8pkH1jOwtozHc7CsB1UPcJzFN95ldHcK4VGYIApecr8zGJox0v06xLwPLMASScngT12aIaIHXAVCJeKquEXAWG1XekZdbubSpccgNiQBmuVmIF8nc1xSKSNJltCf"
const val REDIRECT_URL = "androidaps://setup-openhumans" private const val REDIRECT_URL = "androidaps://setup-openhumans"
const val AUTH_URL = "https://www.openhumans.org/direct-sharing/projects/oauth2/authorize/?client_id=$CLIENT_ID&response_type=code" const val AUTH_URL = "https://www.openhumans.org/direct-sharing/projects/oauth2/authorize/?client_id=$CLIENT_ID&response_type=code"
const val WORK_NAME = "Open Humans" const val WORK_NAME = "Open Humans"
const val COPY_NOTIFICATION_ID = 3122 private const val COPY_NOTIFICATION_ID = 3122
const val FAILURE_NOTIFICATION_ID = 3123 private const val FAILURE_NOTIFICATION_ID = 3123
const val SUCCESS_NOTIFICATION_ID = 3124 private const val SUCCESS_NOTIFICATION_ID = 3124
const val SIGNED_OUT_NOTIFICATION_ID = 3125 private const val SIGNED_OUT_NOTIFICATION_ID = 3125
private val openHumansAPI = OpenHumansAPI(OPEN_HUMANS_URL, CLIENT_ID, CLIENT_SECRET, REDIRECT_URL) private val openHumansAPI = OpenHumansAPI(OPEN_HUMANS_URL, CLIENT_ID, CLIENT_SECRET, REDIRECT_URL)
private val FILE_NAME_DATE_FORMAT = SimpleDateFormat("yyyyMMdd'T'HHmmss", Locale.US).apply { timeZone = TimeZone.getTimeZone("UTC") } private val FILE_NAME_DATE_FORMAT = SimpleDateFormat("yyyyMMdd'T'HHmmss", Locale.US).apply { timeZone = TimeZone.getTimeZone("UTC") }
@ -123,13 +123,13 @@ object OpenHumansUploader : PluginBase(
super.onStart() super.onStart()
setupNotificationChannel() setupNotificationChannel()
if (isSetup) scheduleWorker(false) if (isSetup) scheduleWorker(false)
SP.sharedPreferences.registerOnSharedPreferenceChangeListener(this) SP.registerListener(this)
} }
override fun onStop() { override fun onStop() {
copyDisposable?.dispose() copyDisposable?.dispose()
cancelWorker() cancelWorker()
SP.sharedPreferences.unregisterOnSharedPreferenceChangeListener(this) SP.unregisterListener(this)
super.onStop() super.onStop()
} }
@ -250,7 +250,7 @@ object OpenHumansUploader : PluginBase(
put("result", result) put("result", result)
} }
fun enqueueAMAData(profile: JSONObject, glucoseStatus: JSONObject, iobData: JSONObject, mealData: JSONObject, currentTemp: JSONObject, result: JSONObject) = insertQueueItem("APSData") { fun enqueueMAData(profile: JSONObject, glucoseStatus: JSONObject, iobData: JSONObject, mealData: JSONObject, currentTemp: JSONObject, result: JSONObject) = insertQueueItem("APSData") {
put("algorithm", "MA") put("algorithm", "MA")
put("profile", profile) put("profile", profile)
put("glucoseStatus", glucoseStatus) put("glucoseStatus", glucoseStatus)
@ -290,6 +290,9 @@ object OpenHumansUploader : PluginBase(
copyExistingDataToQueue() copyExistingDataToQueue()
RxBus.send(OpenHumansFragment.UpdateViewEvent) RxBus.send(OpenHumansFragment.UpdateViewEvent)
} }
.doOnError {
log.error("Failed to login to Open Humans", it)
}
.ignoreElement() .ignoreElement()
fun logout() { fun logout() {
@ -318,22 +321,22 @@ object OpenHumansUploader : PluginBase(
.map { enqueueBGReading(it); increaseCounter() } .map { enqueueBGReading(it); increaseCounter() }
.ignoreElements() .ignoreElements()
.andThen(Observable.defer { Observable.fromIterable(MainApp.getDbHelper().allCareportalEvents) }) .andThen(Observable.defer { Observable.fromIterable(MainApp.getDbHelper().allCareportalEvents) })
.map { enqueueCareportalEvent(it); increaseCounter() } .map { enqueueCareportalEvent(it); increaseCounter() }
.ignoreElements() .ignoreElements()
.andThen(Observable.defer { Observable.fromIterable(MainApp.getDbHelper().allExtendedBoluses) }) .andThen(Observable.defer { Observable.fromIterable(MainApp.getDbHelper().allExtendedBoluses) })
.map { enqueueExtendedBolus(it); increaseCounter() } .map { enqueueExtendedBolus(it); increaseCounter() }
.ignoreElements() .ignoreElements()
.andThen(Observable.defer { Observable.fromIterable(MainApp.getDbHelper().allProfileSwitches) }) .andThen(Observable.defer { Observable.fromIterable(MainApp.getDbHelper().allProfileSwitches) })
.map { enqueueProfileSwitch(it); increaseCounter() } .map { enqueueProfileSwitch(it); increaseCounter() }
.ignoreElements() .ignoreElements()
.andThen(Observable.defer { Observable.fromIterable(MainApp.getDbHelper().allTDDs) }) .andThen(Observable.defer { Observable.fromIterable(MainApp.getDbHelper().allTDDs) })
.map { enqueueTotalDailyDose(it); increaseCounter() } .map { enqueueTotalDailyDose(it); increaseCounter() }
.ignoreElements() .ignoreElements()
.andThen(Observable.defer { Observable.fromIterable(MainApp.getDbHelper().allTemporaryBasals) }) .andThen(Observable.defer { Observable.fromIterable(MainApp.getDbHelper().allTemporaryBasals) })
.map { enqueueTemporaryBasal(it); increaseCounter() } .map { enqueueTemporaryBasal(it); increaseCounter() }
.ignoreElements() .ignoreElements()
.andThen(Observable.defer { Observable.fromIterable(MainApp.getDbHelper().allTempTargets) }) .andThen(Observable.defer { Observable.fromIterable(MainApp.getDbHelper().allTempTargets) })
.map { enqueueTempTarget(it); increaseCounter() } .map { enqueueTempTarget(it); increaseCounter() }
.ignoreElements() .ignoreElements()
.doOnSubscribe { .doOnSubscribe {
wakeLock.acquire(TimeUnit.MINUTES.toMillis(20)) wakeLock.acquire(TimeUnit.MINUTES.toMillis(20))
@ -362,7 +365,8 @@ object OpenHumansUploader : PluginBase(
.setContentTitle(MainApp.gs(R.string.finishing_open_humans_setup)) .setContentTitle(MainApp.gs(R.string.finishing_open_humans_setup))
.setContentText(MainApp.gs(R.string.this_may_take_a_while)) .setContentText(MainApp.gs(R.string.this_may_take_a_while))
.setStyle(NotificationCompat.BigTextStyle()) .setStyle(NotificationCompat.BigTextStyle())
.setProgress(maxProgress?.toInt() ?: 0, currentProgress?.toInt() ?: 0, maxProgress == null || currentProgress == null) .setProgress(maxProgress?.toInt() ?: 0, currentProgress?.toInt()
?: 0, maxProgress == null || currentProgress == null)
.setOngoing(true) .setOngoing(true)
.setAutoCancel(false) .setAutoCancel(false)
.setSmallIcon(R.drawable.notif_icon) .setSmallIcon(R.drawable.notif_icon)
@ -406,8 +410,15 @@ object OpenHumansUploader : PluginBase(
if (it is OpenHumansAPI.OHHttpException && it.code == 401 && it.detail == "Invalid token.") { if (it is OpenHumansAPI.OHHttpException && it.code == 401 && it.detail == "Invalid token.") {
handleSignOut() handleSignOut()
} }
log.error("Error while uploading to Open Humans", it)
}
.doOnComplete {
log.info("Upload successful")
RxBus.send(OpenHumansFragment.UpdateQueueEvent)
}
.doOnSubscribe {
log.info("Starting upload")
} }
.doOnComplete { RxBus.send(OpenHumansFragment.UpdateQueueEvent) }
private fun uploadFile(accessToken: String, uploadData: UploadData) = Completable.defer { private fun uploadFile(accessToken: String, uploadData: UploadData) = Completable.defer {
openHumansAPI.prepareFileUpload(accessToken, uploadData.fileName, uploadData.metadata) openHumansAPI.prepareFileUpload(accessToken, uploadData.fileName, uploadData.metadata)
@ -450,7 +461,7 @@ object OpenHumansUploader : PluginBase(
zos.writeFile("ApplicationInfo.json", applicationInfo.toString().toByteArray()) zos.writeFile("ApplicationInfo.json", applicationInfo.toString().toByteArray())
tags.add("ApplicationInfo") tags.add("ApplicationInfo")
val preferences = JSONObject(SP.sharedPreferences.all.filterKeys { it.isAllowedKey() }) val preferences = JSONObject(SP.getAll().filterKeys { it.isAllowedKey() })
zos.writeFile("Preferences.json", preferences.toString().toByteArray()) zos.writeFile("Preferences.json", preferences.toString().toByteArray())
tags.add("Preferences") tags.add("Preferences")

View file

@ -12,7 +12,7 @@ import info.nightscout.androidaps.MainApp;
*/ */
public class SP { public class SP {
public static SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(MainApp.instance().getApplicationContext()); private static final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(MainApp.instance().getApplicationContext());
static public Map<String, ?> getAll() { static public Map<String, ?> getAll() {
return sharedPreferences.getAll(); return sharedPreferences.getAll();
@ -26,6 +26,14 @@ public class SP {
return sharedPreferences.contains(key); return sharedPreferences.contains(key);
} }
static public void registerListener(SharedPreferences.OnSharedPreferenceChangeListener listener) {
sharedPreferences.registerOnSharedPreferenceChangeListener(listener);
}
static public void unregisterListener(SharedPreferences.OnSharedPreferenceChangeListener listener) {
sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener);
}
static public boolean contains(int resourceId) { static public boolean contains(int resourceId) {
return sharedPreferences.contains(MainApp.gs(resourceId)); return sharedPreferences.contains(MainApp.gs(resourceId));
} }