Files
DexcomBluetoothUploader/lib/nightscout/com/eveningoutpost/dexdrip/Models/Accuracy.java
2020-07-18 21:44:27 -04:00

194 lines
7.5 KiB
Java

package com.eveningoutpost.dexdrip.Models;
/**
* Created by jamorham on 01/02/2017.
*/
import android.provider.BaseColumns;
import com.activeandroid.annotation.Column;
import com.activeandroid.annotation.Table;
import com.activeandroid.query.Select;
import com.eveningoutpost.dexdrip.BestGlucose;
import com.eveningoutpost.dexdrip.UtilityModels.Constants;
import com.eveningoutpost.dexdrip.UtilityModels.Pref;
import com.google.gson.annotations.Expose;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Table(name = "Accuracy", id = BaseColumns._ID)
public class Accuracy extends PlusModel {
private static final String TAG = "Accuracy";
private static boolean patched = false;
static final String[] schema = {
"CREATE TABLE Accuracy (_id INTEGER PRIMARY KEY AUTOINCREMENT);",
"ALTER TABLE Accuracy ADD COLUMN timestamp INTEGER;",
"ALTER TABLE Accuracy ADD COLUMN bg REAL;",
"ALTER TABLE Accuracy ADD COLUMN bgtimestamp INTEGER;",
"ALTER TABLE Accuracy ADD COLUMN bgsource TEXT;",
"ALTER TABLE Accuracy ADD COLUMN plugin TEXT;",
"ALTER TABLE Accuracy ADD COLUMN calculated REAL;",
"ALTER TABLE Accuracy ADD COLUMN lag INTEGER;",
"ALTER TABLE Accuracy ADD COLUMN difference REAL;",
"CREATE INDEX index_Accuracy_timestamp on Accuracy(timestamp);",
"CREATE INDEX index_Accuracy_bgtimestamp on Accuracy(bgtimestamp);"
};
@Expose
@Column(name = "timestamp", index = true)
public long timestamp;
@Expose
@Column(name = "bg")
public double bg;
@Expose
@Column(name = "bgtimestamp", index = true)
public long bgtimestamp;
@Expose
@Column(name = "bgsource")
public String bgsource;
@Expose
@Column(name = "plugin")
public String plugin;
@Expose
@Column(name = "calculated")
public double calculated;
@Expose
@Column(name = "lag")
public boolean lag;
@Expose
@Column(name = "difference")
public double difference;
private static final boolean d = false;
public static Accuracy create(BloodTest bloodTest, BestGlucose.DisplayGlucose dg) {
if (dg == null) return null;
final BgReading from_dg = new BgReading();
from_dg.timestamp = dg.timestamp;
from_dg.calculated_value = dg.mgdl;
return create(bloodTest, from_dg, dg.plugin_name);
}
public static Accuracy create(BloodTest bloodTest, BgReading bgReading, String plugin) {
if ((bloodTest == null) || (bgReading == null)) return null;
patched = fixUpTable(schema, patched);
if (getForPreciseTimestamp(bgReading.timestamp, Constants.MINUTE_IN_MS, plugin) != null) {
UserError.Log.d(TAG, "Duplicate accuracy timestamp for: " + JoH.dateTimeText(bgReading.timestamp));
return null;
}
final Accuracy ac = new Accuracy();
ac.timestamp = bgReading.timestamp;
ac.bg = bloodTest.mgdl;
ac.bgtimestamp = bloodTest.timestamp;
ac.bgsource = bloodTest.source;
ac.plugin = plugin;
ac.calculated = bgReading.calculated_value;
//ac.lag = bgReading.timestamp-bloodTest.timestamp;
ac.difference = bgReading.calculated_value - bloodTest.mgdl;
ac.save();
return ac;
}
static Accuracy getForPreciseTimestamp(double timestamp, double precision, String plugin) {
patched = fixUpTable(schema, patched);
final Accuracy accuracy = new Select()
.from(Accuracy.class)
.where("timestamp <= ?", (timestamp + precision))
.where("timestamp >= ?", (timestamp - precision))
.where("plugin = ?", plugin)
.orderBy("abs(timestamp - " + timestamp + ") asc")
.executeSingle();
if (accuracy != null && Math.abs(accuracy.timestamp - timestamp) < precision) {
return accuracy;
}
return null;
}
public static List<Accuracy> latestForGraph(int number, long startTime, long endTime) {
try {
return new Select()
.from(Accuracy.class)
.where("timestamp >= " + Math.max(startTime, 0))
.where("timestamp <= " + endTime)
.orderBy("timestamp desc, _id asc")
.limit(number)
.execute();
} catch (android.database.sqlite.SQLiteException e) {
patched = fixUpTable(schema, patched);
return new ArrayList<>();
}
}
public static String evaluateAccuracy(long period) {
// TODO CACHE ?
final boolean domgdl = Pref.getString("units", "mgdl").equals("mgdl");
final Map<String, Double> totals = new HashMap<>();
final Map<String, Double> signed_totals = new HashMap<>();
final Map<String, Integer> count = new HashMap<>();
final List<Accuracy> alist = latestForGraph(500, JoH.tsl() - period, JoH.tsl());
// total up differences
for (Accuracy entry : alist) {
if (totals.containsKey(entry.plugin)) {
totals.put(entry.plugin, totals.get(entry.plugin) + Math.abs(entry.difference));
signed_totals.put(entry.plugin, signed_totals.get(entry.plugin) + entry.difference);
count.put(entry.plugin, count.get(entry.plugin) + 1);
} else {
totals.put(entry.plugin, Math.abs(entry.difference));
signed_totals.put(entry.plugin, entry.difference);
count.put(entry.plugin, 1);
}
}
String result = "";
int plugin_count = 0;
for (Map.Entry<String, Double> total : totals.entrySet()) {
plugin_count++;
final String plugin = total.getKey();
final int this_count = count.get(plugin);
final double this_total = total.getValue();
// calculate the abs mean, 0 = perfect
final double this_mean = this_total / this_count;
final double signed_total = signed_totals.get(plugin);
final double signed_mean = signed_total / this_count;
// calculate the bias ratio. 0% means totally unbiased, 100% means all data skewed towards signed mean
final double signed_ratio = (Math.abs(signed_mean) / this_mean) * 100;
if (d) UserError.Log.d(TAG, plugin + ": total: " + JoH.qs(this_total) + " count: " + this_count + " avg: " + JoH.qs(this_mean) + " mmol: " + JoH.qs((this_mean) * Constants.MGDL_TO_MMOLL) + " bias: " + JoH.qs(signed_mean) + " " + JoH.qs(signed_ratio, 0) + "%");
String plugin_result = plugin.substring(0, 1).toLowerCase() + ": " + asString(this_mean, signed_mean, signed_ratio, domgdl);
UserError.Log.d(TAG, plugin_result);
if (result.length() > 0) result += " ";
result += plugin_result;
}
return plugin_count == 1 ? result : result.replaceFirst(" mmol", "").replaceFirst(" mgdl", " ");
}
private static String asString(double mean, double signed_mean, double signed_ratio, boolean domgdl) {
String symbol = "err";
if (signed_ratio < 90) {
symbol = "\u00B1"; // +- symbol
} else {
if (signed_mean < 0) {
symbol = "\u207B"; // superscript minus
} else {
symbol = "\u207A"; // superscript plus
}
}
return symbol + (!domgdl ? JoH.qs(mean * Constants.MGDL_TO_MMOLL, 2) + " mmol" : JoH.qs(mean, 1) + " mgdl");
}
}