package info.nightscout.androidaps.db; import java.sql.SQLException; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import org.json.JSONException; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import android.content.Context; import android.database.DatabaseUtils; import android.database.sqlite.SQLiteDatabase; import android.support.annotation.Nullable; import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper; import com.j256.ormlite.dao.CloseableIterator; import com.j256.ormlite.dao.Dao; import com.j256.ormlite.stmt.PreparedQuery; import com.j256.ormlite.stmt.QueryBuilder; import com.j256.ormlite.stmt.Where; import com.j256.ormlite.support.ConnectionSource; import com.j256.ormlite.table.TableUtils; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.data.OverlappingIntervals; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.ProfileStore; import info.nightscout.androidaps.events.EventCareportalEventChange; import info.nightscout.androidaps.events.EventExtendedBolusChange; import info.nightscout.androidaps.events.EventNewBG; import info.nightscout.androidaps.events.EventProfileSwitchChange; import info.nightscout.androidaps.events.EventRefreshOverview; import info.nightscout.androidaps.events.EventReloadProfileSwitchData; import info.nightscout.androidaps.events.EventReloadTempBasalData; import info.nightscout.androidaps.events.EventReloadTreatmentData; import info.nightscout.androidaps.events.EventTempBasalChange; import info.nightscout.androidaps.events.EventTempTargetChange; import info.nightscout.androidaps.interfaces.ProfileInterface; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventNewHistoryData; import info.nightscout.androidaps.plugins.NSClientInternal.NSUpload; import info.nightscout.androidaps.plugins.PumpDanaR.activities.DanaRNSHistorySync; import info.nightscout.androidaps.plugins.PumpDanaR.comm.RecordTypes; import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin; import info.nightscout.utils.JsonHelper; import info.nightscout.utils.PercentageSplitter; import info.nightscout.utils.ToastUtils; /** * This Helper contains all resource to provide a central DB management functionality. Only methods handling * data-structure (and not the DB content) should be contained in here (meaning DDL and not SQL). *

* This class can safely be called from Services, but should not call Services to avoid circular dependencies. One major * issue with this (right now) are the scheduled events, which are put into the service. Therefor all direct calls to * the corresponding methods (eg. resetDatabases) should be done by a central service. */ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { private static Logger log = LoggerFactory.getLogger(L.DATABASE); public static final String DATABASE_NAME = "AndroidAPSDb"; public static final String DATABASE_BGREADINGS = "BgReadings"; public static final String DATABASE_TEMPORARYBASALS = "TemporaryBasals"; public static final String DATABASE_EXTENDEDBOLUSES = "ExtendedBoluses"; public static final String DATABASE_TEMPTARGETS = "TempTargets"; public static final String DATABASE_DANARHISTORY = "DanaRHistory"; public static final String DATABASE_DBREQUESTS = "DBRequests"; public static final String DATABASE_CAREPORTALEVENTS = "CareportalEvents"; public static final String DATABASE_PROFILESWITCHES = "ProfileSwitches"; public static final String DATABASE_TDDS = "TDDs"; private static final int DATABASE_VERSION = 9; public static Long earliestDataChange = null; private static final ScheduledExecutorService bgWorker = Executors.newSingleThreadScheduledExecutor(); private static ScheduledFuture scheduledBgPost = null; private static final ScheduledExecutorService tempBasalsWorker = Executors.newSingleThreadScheduledExecutor(); private static ScheduledFuture scheduledTemBasalsPost = null; private static final ScheduledExecutorService tempTargetWorker = Executors.newSingleThreadScheduledExecutor(); private static ScheduledFuture scheduledTemTargetPost = null; private static final ScheduledExecutorService extendedBolusWorker = Executors.newSingleThreadScheduledExecutor(); private static ScheduledFuture scheduledExtendedBolusPost = null; private static final ScheduledExecutorService careportalEventWorker = Executors.newSingleThreadScheduledExecutor(); private static ScheduledFuture scheduledCareportalEventPost = null; private static final ScheduledExecutorService profileSwitchEventWorker = Executors .newSingleThreadScheduledExecutor(); private static ScheduledFuture scheduledProfileSwitchEventPost = null; private int oldVersion = 0; private int newVersion = 0; public DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); onCreate(getWritableDatabase(), getConnectionSource()); // onUpgrade(getWritableDatabase(), getConnectionSource(), 1,1); } @Override public void onCreate(SQLiteDatabase database, ConnectionSource connectionSource) { try { if (L.isEnabled(L.DATABASE)) log.info("onCreate"); TableUtils.createTableIfNotExists(connectionSource, TempTarget.class); TableUtils.createTableIfNotExists(connectionSource, BgReading.class); TableUtils.createTableIfNotExists(connectionSource, DanaRHistoryRecord.class); TableUtils.createTableIfNotExists(connectionSource, DbRequest.class); TableUtils.createTableIfNotExists(connectionSource, TemporaryBasal.class); TableUtils.createTableIfNotExists(connectionSource, ExtendedBolus.class); TableUtils.createTableIfNotExists(connectionSource, CareportalEvent.class); TableUtils.createTableIfNotExists(connectionSource, ProfileSwitch.class); TableUtils.createTableIfNotExists(connectionSource, TDD.class); } catch (SQLException e) { log.error("Can't create database", e); throw new RuntimeException(e); } } @Override public void onUpgrade(SQLiteDatabase database, ConnectionSource connectionSource, int oldVersion, int newVersion) { try { this.oldVersion = oldVersion; this.newVersion = newVersion; if (oldVersion == 7 && newVersion == 8) { log.debug("Upgrading database from v7 to v8"); } else if (oldVersion == 8 && newVersion == 9) { log.debug("Upgrading database from v8 to v9"); } else { log.info(DatabaseHelper.class.getName(), "onUpgrade"); TableUtils.dropTable(connectionSource, TempTarget.class, true); TableUtils.dropTable(connectionSource, BgReading.class, true); TableUtils.dropTable(connectionSource, DanaRHistoryRecord.class, true); TableUtils.dropTable(connectionSource, DbRequest.class, true); TableUtils.dropTable(connectionSource, TemporaryBasal.class, true); TableUtils.dropTable(connectionSource, ExtendedBolus.class, true); TableUtils.dropTable(connectionSource, CareportalEvent.class, true); TableUtils.dropTable(connectionSource, ProfileSwitch.class, true); onCreate(database, connectionSource); } } catch (SQLException e) { log.error("Can't drop databases", e); throw new RuntimeException(e); } } public int getOldVersion() { return oldVersion; } public int getNewVersion() { return newVersion; } /** * Close the database connections and clear any cached DAOs. */ @Override public void close() { super.close(); } public long size(String database) { return DatabaseUtils.queryNumEntries(getReadableDatabase(), database); } // --------------------- DB resets --------------------- public void resetDatabases() { try { TableUtils.dropTable(connectionSource, TempTarget.class, true); TableUtils.dropTable(connectionSource, BgReading.class, true); TableUtils.dropTable(connectionSource, DanaRHistoryRecord.class, true); TableUtils.dropTable(connectionSource, DbRequest.class, true); TableUtils.dropTable(connectionSource, TemporaryBasal.class, true); TableUtils.dropTable(connectionSource, ExtendedBolus.class, true); TableUtils.dropTable(connectionSource, CareportalEvent.class, true); TableUtils.dropTable(connectionSource, ProfileSwitch.class, true); TableUtils.dropTable(connectionSource, TDD.class, true); TableUtils.createTableIfNotExists(connectionSource, TempTarget.class); TableUtils.createTableIfNotExists(connectionSource, BgReading.class); TableUtils.createTableIfNotExists(connectionSource, DanaRHistoryRecord.class); TableUtils.createTableIfNotExists(connectionSource, DbRequest.class); TableUtils.createTableIfNotExists(connectionSource, TemporaryBasal.class); TableUtils.createTableIfNotExists(connectionSource, ExtendedBolus.class); TableUtils.createTableIfNotExists(connectionSource, CareportalEvent.class); TableUtils.createTableIfNotExists(connectionSource, ProfileSwitch.class); TableUtils.createTableIfNotExists(connectionSource, TDD.class); updateEarliestDataChange(0); } catch (SQLException e) { log.error("Unhandled exception", e); } VirtualPumpPlugin.getPlugin().setFakingStatus(true); scheduleBgChange(null); // trigger refresh scheduleTemporaryBasalChange(); scheduleExtendedBolusChange(); scheduleTemporaryTargetChange(); scheduleCareportalEventChange(); scheduleProfileSwitchChange(); new java.util.Timer().schedule(new java.util.TimerTask() { @Override public void run() { MainApp.bus().post(new EventRefreshOverview("resetDatabases")); } }, 3000); } public void resetTempTargets() { try { TableUtils.dropTable(connectionSource, TempTarget.class, true); TableUtils.createTableIfNotExists(connectionSource, TempTarget.class); } catch (SQLException e) { log.error("Unhandled exception", e); } scheduleTemporaryTargetChange(); } public void resetTemporaryBasals() { try { TableUtils.dropTable(connectionSource, TemporaryBasal.class, true); TableUtils.createTableIfNotExists(connectionSource, TemporaryBasal.class); updateEarliestDataChange(0); } catch (SQLException e) { log.error("Unhandled exception", e); } VirtualPumpPlugin.getPlugin().setFakingStatus(false); scheduleTemporaryBasalChange(); } public void resetExtededBoluses() { try { TableUtils.dropTable(connectionSource, ExtendedBolus.class, true); TableUtils.createTableIfNotExists(connectionSource, ExtendedBolus.class); updateEarliestDataChange(0); } catch (SQLException e) { log.error("Unhandled exception", e); } scheduleExtendedBolusChange(); } public void resetCareportalEvents() { try { TableUtils.dropTable(connectionSource, CareportalEvent.class, true); TableUtils.createTableIfNotExists(connectionSource, CareportalEvent.class); } catch (SQLException e) { log.error("Unhandled exception", e); } scheduleCareportalEventChange(); } public void resetProfileSwitch() { try { TableUtils.dropTable(connectionSource, ProfileSwitch.class, true); TableUtils.createTableIfNotExists(connectionSource, ProfileSwitch.class); } catch (SQLException e) { log.error("Unhandled exception", e); } scheduleProfileSwitchChange(); } public void resetTDDs() { try { TableUtils.dropTable(connectionSource, TDD.class, true); TableUtils.createTableIfNotExists(connectionSource, TDD.class); } catch (SQLException e) { log.error("Unhandled exception", e); } } // ------------------ getDao ------------------------------------------- private Dao getDaoTempTargets() throws SQLException { return getDao(TempTarget.class); } private Dao getDaoBgReadings() throws SQLException { return getDao(BgReading.class); } private Dao getDaoDanaRHistory() throws SQLException { return getDao(DanaRHistoryRecord.class); } private Dao getDaoTDD() throws SQLException { return getDao(TDD.class); } private Dao getDaoDbRequest() throws SQLException { return getDao(DbRequest.class); } private Dao getDaoTemporaryBasal() throws SQLException { return getDao(TemporaryBasal.class); } private Dao getDaoExtendedBolus() throws SQLException { return getDao(ExtendedBolus.class); } private Dao getDaoCareportalEvents() throws SQLException { return getDao(CareportalEvent.class); } private Dao getDaoProfileSwitch() throws SQLException { return getDao(ProfileSwitch.class); } public static long roundDateToSec(long date) { long rounded = date - date % 1000; if (rounded != date) if (L.isEnabled(L.DATABASE)) log.debug("Rounding " + date + " to " + rounded); return rounded; } // ------------------- BgReading handling ----------------------- public boolean createIfNotExists(BgReading bgReading, String from) { try { bgReading.date = roundDateToSec(bgReading.date); BgReading old = getDaoBgReadings().queryForId(bgReading.date); if (old == null) { getDaoBgReadings().create(bgReading); if (L.isEnabled(L.DATABASE)) log.debug("BG: New record from: " + from + " " + bgReading.toString()); scheduleBgChange(bgReading); return true; } if (!old.isEqual(bgReading)) { if (L.isEnabled(L.DATABASE)) log.debug("BG: Similiar found: " + old.toString()); old.copyFrom(bgReading); getDaoBgReadings().update(old); if (L.isEnabled(L.DATABASE)) log.debug("BG: Updating record from: " + from + " New data: " + old.toString()); scheduleBgChange(bgReading); return false; } } catch (SQLException e) { log.error("Unhandled exception", e); } return false; } public void update(BgReading bgReading) { bgReading.date = roundDateToSec(bgReading.date); try { getDaoBgReadings().update(bgReading); } catch (SQLException e) { log.error("Unhandled exception", e); } } private static void scheduleBgChange(@Nullable final BgReading bgReading) { class PostRunnable implements Runnable { public void run() { if (L.isEnabled(L.DATABASE)) log.debug("Firing EventNewBg"); MainApp.bus().post(new EventNewBG(bgReading)); scheduledBgPost = null; } } // prepare task for execution in 1 sec // cancel waiting task to prevent sending multiple posts if (scheduledBgPost != null) scheduledBgPost.cancel(false); Runnable task = new PostRunnable(); final int sec = 1; scheduledBgPost = bgWorker.schedule(task, sec, TimeUnit.SECONDS); } /* * Return last BgReading from database or null if db is empty */ @Nullable public static BgReading lastBg() { List bgList = null; try { Dao daoBgReadings = MainApp.getDbHelper().getDaoBgReadings(); QueryBuilder queryBuilder = daoBgReadings.queryBuilder(); queryBuilder.orderBy("date", false); queryBuilder.limit(1L); queryBuilder.where().gt("value", 38).and().eq("isValid", true); PreparedQuery preparedQuery = queryBuilder.prepare(); bgList = daoBgReadings.query(preparedQuery); } catch (SQLException e) { log.error("Unhandled exception", e); } if (bgList != null && bgList.size() > 0) return bgList.get(0); else return null; } /* * Return bg reading if not old ( <9 min ) * or null if older */ @Nullable public static BgReading actualBg() { BgReading lastBg = lastBg(); if (lastBg == null) return null; if (lastBg.date > System.currentTimeMillis() - 9 * 60 * 1000) return lastBg; return null; } public List getBgreadingsDataFromTime(long mills, boolean ascending) { try { Dao daoBgreadings = getDaoBgReadings(); List bgReadings; QueryBuilder queryBuilder = daoBgreadings.queryBuilder(); queryBuilder.orderBy("date", ascending); Where where = queryBuilder.where(); where.ge("date", mills).and().gt("value", 38).and().eq("isValid", true); PreparedQuery preparedQuery = queryBuilder.prepare(); bgReadings = daoBgreadings.query(preparedQuery); return bgReadings; } catch (SQLException e) { log.error("Unhandled exception", e); } return new ArrayList(); } public List getBgreadingsDataFromTime(long start, long end, boolean ascending) { try { Dao daoBgreadings = getDaoBgReadings(); List bgReadings; QueryBuilder queryBuilder = daoBgreadings.queryBuilder(); queryBuilder.orderBy("date", ascending); Where where = queryBuilder.where(); where.between("date", start, end).and().gt("value", 38).and().eq("isValid", true); PreparedQuery preparedQuery = queryBuilder.prepare(); bgReadings = daoBgreadings.query(preparedQuery); return bgReadings; } catch (SQLException e) { log.error("Unhandled exception", e); } return new ArrayList(); } public List getAllBgreadingsDataFromTime(long mills, boolean ascending) { try { Dao daoBgreadings = getDaoBgReadings(); List bgReadings; QueryBuilder queryBuilder = daoBgreadings.queryBuilder(); queryBuilder.orderBy("date", ascending); Where where = queryBuilder.where(); where.ge("date", mills); PreparedQuery preparedQuery = queryBuilder.prepare(); bgReadings = daoBgreadings.query(preparedQuery); return bgReadings; } catch (SQLException e) { log.error("Unhandled exception", e); } return new ArrayList(); } // ------------------- TDD handling ----------------------- public void createOrUpdateTDD(TDD tdd) { try { Dao dao = getDaoTDD(); dao.createOrUpdate(tdd); } catch (SQLException e) { ToastUtils.showToastInUiThread(MainApp.instance(), "createOrUpdate-Exception"); log.error("Unhandled exception", e); } } public List getTDDs() { List tddList; try { QueryBuilder queryBuilder = getDaoTDD().queryBuilder(); queryBuilder.orderBy("date", false); queryBuilder.limit(10L); PreparedQuery preparedQuery = queryBuilder.prepare(); tddList = getDaoTDD().query(preparedQuery); } catch (SQLException e) { log.error("Unhandled exception", e); tddList = new ArrayList<>(); } return tddList; } // FIXME public List getTDDsForLastXDays(int days) { List tddList; GregorianCalendar gc = new GregorianCalendar(); gc.add(Calendar.DAY_OF_YEAR, (-1) * days); Date d = new Date(gc.get(Calendar.YEAR), gc.get(Calendar.MONTH), gc.get(Calendar.DAY_OF_MONTH), 0, 0, 0); try { QueryBuilder queryBuilder = getDaoTDD().queryBuilder(); queryBuilder.orderBy("date", false); Where where = queryBuilder.where(); where.ge("date", d.getTime()); queryBuilder.limit(10L); PreparedQuery preparedQuery = queryBuilder.prepare(); tddList = getDaoTDD().query(preparedQuery); } catch (SQLException e) { log.error("Unhandled exception", e); tddList = new ArrayList<>(); } return tddList; } // ------------- DbRequests handling ------------------- public void create(DbRequest dbr) { try { getDaoDbRequest().create(dbr); } catch (SQLException e) { log.error("Unhandled exception", e); } } public int delete(DbRequest dbr) { try { return getDaoDbRequest().delete(dbr); } catch (SQLException e) { log.error("Unhandled exception", e); } return 0; } public int deleteDbRequest(String nsClientId) { try { return getDaoDbRequest().deleteById(nsClientId); } catch (SQLException e) { log.error("Unhandled exception", e); } return 0; } public void deleteDbRequestbyMongoId(String action, String id) { try { QueryBuilder queryBuilder = getDaoDbRequest().queryBuilder(); Where where = queryBuilder.where(); where.eq("_id", id).and().eq("action", action); queryBuilder.limit(10L); PreparedQuery preparedQuery = queryBuilder.prepare(); List dbList = getDaoDbRequest().query(preparedQuery); for (DbRequest r : dbList) { delete(r); } } catch (SQLException e) { log.error("Unhandled exception", e); } } public void deleteAllDbRequests() { try { TableUtils.clearTable(connectionSource, DbRequest.class); } catch (SQLException e) { log.error("Unhandled exception", e); } } public CloseableIterator getDbRequestInterator() { try { return getDaoDbRequest().closeableIterator(); } catch (SQLException e) { log.error("Unhandled exception", e); return null; } } // -------------------- TREATMENT HANDLING ------------------- public static void updateEarliestDataChange(long newDate) { if (earliestDataChange == null) { earliestDataChange = newDate; return; } if (newDate < earliestDataChange) { earliestDataChange = newDate; } } // ---------------- TempTargets handling --------------- public List getTemptargetsDataFromTime(long mills, boolean ascending) { try { Dao daoTempTargets = getDaoTempTargets(); List tempTargets; QueryBuilder queryBuilder = daoTempTargets.queryBuilder(); queryBuilder.orderBy("date", ascending); Where where = queryBuilder.where(); where.ge("date", mills); PreparedQuery preparedQuery = queryBuilder.prepare(); tempTargets = daoTempTargets.query(preparedQuery); return tempTargets; } catch (SQLException e) { log.error("Unhandled exception", e); } return new ArrayList(); } public boolean createOrUpdate(TempTarget tempTarget) { try { TempTarget old; tempTarget.date = roundDateToSec(tempTarget.date); if (tempTarget.source == Source.NIGHTSCOUT) { old = getDaoTempTargets().queryForId(tempTarget.date); if (old != null) { if (!old.isEqual(tempTarget)) { getDaoTempTargets().delete(old); // need to delete/create because date may change too old.copyFrom(tempTarget); getDaoTempTargets().create(old); if (L.isEnabled(L.DATABASE)) log.debug("TEMPTARGET: Updating record by date from: " + Source.getString(tempTarget.source) + " " + old.toString()); scheduleTemporaryTargetChange(); return true; } return false; } // find by NS _id if (tempTarget._id != null) { QueryBuilder queryBuilder = getDaoTempTargets().queryBuilder(); Where where = queryBuilder.where(); where.eq("_id", tempTarget._id); PreparedQuery preparedQuery = queryBuilder.prepare(); List trList = getDaoTempTargets().query(preparedQuery); if (trList.size() > 0) { old = trList.get(0); if (!old.isEqual(tempTarget)) { getDaoTempTargets().delete(old); // need to delete/create because date may change too old.copyFrom(tempTarget); getDaoTempTargets().create(old); if (L.isEnabled(L.DATABASE)) log.debug("TEMPTARGET: Updating record by _id from: " + Source.getString(tempTarget.source) + " " + old.toString()); scheduleTemporaryTargetChange(); return true; } } } getDaoTempTargets().create(tempTarget); if (L.isEnabled(L.DATABASE)) log.debug("TEMPTARGET: New record from: " + Source.getString(tempTarget.source) + " " + tempTarget.toString()); scheduleTemporaryTargetChange(); return true; } if (tempTarget.source == Source.USER) { getDaoTempTargets().create(tempTarget); if (L.isEnabled(L.DATABASE)) log.debug("TEMPTARGET: New record from: " + Source.getString(tempTarget.source) + " " + tempTarget.toString()); scheduleTemporaryTargetChange(); return true; } } catch (SQLException e) { log.error("Unhandled exception", e); } return false; } public void delete(TempTarget tempTarget) { try { getDaoTempTargets().delete(tempTarget); scheduleTemporaryTargetChange(); } catch (SQLException e) { log.error("Unhandled exception", e); } } private static void scheduleTemporaryTargetChange() { class PostRunnable implements Runnable { public void run() { if (L.isEnabled(L.DATABASE)) log.debug("Firing EventTempTargetChange"); MainApp.bus().post(new EventTempTargetChange()); scheduledTemTargetPost = null; } } // prepare task for execution in 1 sec // cancel waiting task to prevent sending multiple posts if (scheduledTemTargetPost != null) scheduledTemTargetPost.cancel(false); Runnable task = new PostRunnable(); final int sec = 1; scheduledTemTargetPost = tempTargetWorker.schedule(task, sec, TimeUnit.SECONDS); } /* * { * "_id": "58795998aa86647ba4d68ce7", * "enteredBy": "", * "eventType": "Temporary Target", * "reason": "Eating Soon", * "targetTop": 80, * "targetBottom": 80, * "duration": 120, * "created_at": "2017-01-13T22:50:00.782Z", * "carbs": null, * "insulin": null * } */ public void createTemptargetFromJsonIfNotExists(JSONObject trJson) { try { String units = JsonHelper.safeGetString(trJson, "units", Constants.MGDL); TempTarget tempTarget = new TempTarget().date(trJson.getLong("mills")).duration(trJson.getInt("duration")) .low(Profile.toMgdl(trJson.getDouble("targetBottom"), units)) .high(Profile.toMgdl(trJson.getDouble("targetTop"), units)) .reason(JsonHelper.safeGetString(trJson, "reason", ""))._id(trJson.getString("_id")) .source(Source.NIGHTSCOUT); createOrUpdate(tempTarget); } catch (JSONException e) { log.error("Unhandled exception: " + trJson.toString(), e); } } public void deleteTempTargetById(String _id) { TempTarget stored = findTempTargetById(_id); if (stored != null) { log.debug("TEMPTARGET: Removing TempTarget record from database: " + stored.toString()); delete(stored); scheduleTemporaryTargetChange(); } } public TempTarget findTempTargetById(String _id) { try { QueryBuilder queryBuilder = getDaoTempTargets().queryBuilder(); Where where = queryBuilder.where(); where.eq("_id", _id); PreparedQuery preparedQuery = queryBuilder.prepare(); List list = getDaoTempTargets().query(preparedQuery); if (list.size() == 1) { return list.get(0); } else { return null; } } catch (SQLException e) { log.error("Unhandled exception", e); } return null; } // ----------------- DanaRHistory handling -------------------- public void createOrUpdate(DanaRHistoryRecord record) { try { getDaoDanaRHistory().createOrUpdate(record); // If it is a TDD, store it for stats also. if (record.recordCode == RecordTypes.RECORD_TYPE_DAILY) { createOrUpdateTDD(new TDD(record.recordDate, record.recordDailyBolus, record.recordDailyBasal, 0)); } } catch (SQLException e) { log.error("Unhandled exception", e); } } public List getDanaRHistoryRecordsByType(byte type) { List historyList; try { QueryBuilder queryBuilder = getDaoDanaRHistory().queryBuilder(); queryBuilder.orderBy("recordDate", false); Where where = queryBuilder.where(); where.eq("recordCode", type); queryBuilder.limit(200L); PreparedQuery preparedQuery = queryBuilder.prepare(); historyList = getDaoDanaRHistory().query(preparedQuery); } catch (SQLException e) { log.error("Unhandled exception", e); historyList = new ArrayList<>(); } return historyList; } public void updateDanaRHistoryRecordId(JSONObject trJson) { try { QueryBuilder queryBuilder = getDaoDanaRHistory().queryBuilder(); Where where = queryBuilder.where(); where.ge("bytes", trJson.get(DanaRNSHistorySync.DANARSIGNATURE)); PreparedQuery preparedQuery = queryBuilder.prepare(); List list = getDaoDanaRHistory().query(preparedQuery); if (list.size() == 0) { // Record does not exists. Ignore } else if (list.size() == 1) { DanaRHistoryRecord record = list.get(0); if (record._id == null || !record._id.equals(trJson.getString("_id"))) { if (L.isEnabled(L.DATABASE)) log.debug("Updating _id in DanaR history database: " + trJson.getString("_id")); record._id = trJson.getString("_id"); getDaoDanaRHistory().update(record); } else { // already set } } } catch (SQLException | JSONException e) { log.error("Unhandled exception: " + trJson.toString(), e); } } // ------------ TemporaryBasal handling --------------- // return true if new record was created public boolean createOrUpdate(TemporaryBasal tempBasal) { try { TemporaryBasal old; tempBasal.date = roundDateToSec(tempBasal.date); if (tempBasal.source == Source.PUMP) { // check for changed from pump change in NS QueryBuilder queryBuilder = getDaoTemporaryBasal().queryBuilder(); Where where = queryBuilder.where(); where.eq("pumpId", tempBasal.pumpId); PreparedQuery preparedQuery = queryBuilder.prepare(); List trList = getDaoTemporaryBasal().query(preparedQuery); if (trList.size() > 0) { // do nothing, pump history record cannot be changed if (L.isEnabled(L.DATABASE)) log.debug("TEMPBASAL: Already exists from: " + Source.getString(tempBasal.source) + " " + tempBasal.toString()); return false; } getDaoTemporaryBasal().create(tempBasal); if (L.isEnabled(L.DATABASE)) log.debug("TEMPBASAL: New record from: " + Source.getString(tempBasal.source) + " " + tempBasal.toString()); updateEarliestDataChange(tempBasal.date); scheduleTemporaryBasalChange(); return true; } if (tempBasal.source == Source.NIGHTSCOUT) { old = getDaoTemporaryBasal().queryForId(tempBasal.date); if (old != null) { if (!old.isAbsolute && tempBasal.isAbsolute) { // converted to absolute by "ns_sync_use_absolute" // so far ignore, do not convert back because it may not be accurate return false; } if (!old.isEqual(tempBasal)) { long oldDate = old.date; getDaoTemporaryBasal().delete(old); // need to delete/create because date may change too old.copyFrom(tempBasal); getDaoTemporaryBasal().create(old); if (L.isEnabled(L.DATABASE)) log.debug("TEMPBASAL: Updating record by date from: " + Source.getString(tempBasal.source) + " " + old.toString()); updateEarliestDataChange(oldDate); updateEarliestDataChange(old.date); scheduleTemporaryBasalChange(); return true; } return false; } // find by NS _id if (tempBasal._id != null) { QueryBuilder queryBuilder = getDaoTemporaryBasal().queryBuilder(); Where where = queryBuilder.where(); where.eq("_id", tempBasal._id); PreparedQuery preparedQuery = queryBuilder.prepare(); List trList = getDaoTemporaryBasal().query(preparedQuery); if (trList.size() > 0) { old = trList.get(0); if (!old.isEqual(tempBasal)) { long oldDate = old.date; getDaoTemporaryBasal().delete(old); // need to delete/create because date may change too old.copyFrom(tempBasal); getDaoTemporaryBasal().create(old); if (L.isEnabled(L.DATABASE)) log.debug("TEMPBASAL: Updating record by _id from: " + Source.getString(tempBasal.source) + " " + old.toString()); updateEarliestDataChange(oldDate); updateEarliestDataChange(old.date); scheduleTemporaryBasalChange(); return true; } } } getDaoTemporaryBasal().create(tempBasal); if (L.isEnabled(L.DATABASE)) log.debug("TEMPBASAL: New record from: " + Source.getString(tempBasal.source) + " " + tempBasal.toString()); updateEarliestDataChange(tempBasal.date); scheduleTemporaryBasalChange(); return true; } if (tempBasal.source == Source.USER) { getDaoTemporaryBasal().create(tempBasal); if (L.isEnabled(L.DATABASE)) log.debug("TEMPBASAL: New record from: " + Source.getString(tempBasal.source) + " " + tempBasal.toString()); updateEarliestDataChange(tempBasal.date); scheduleTemporaryBasalChange(); return true; } } catch (SQLException e) { log.error("Unhandled exception", e); } return false; } public void delete(TemporaryBasal tempBasal) { try { getDaoTemporaryBasal().delete(tempBasal); updateEarliestDataChange(tempBasal.date); } catch (SQLException e) { log.error("Unhandled exception", e); } scheduleTemporaryBasalChange(); } public List getTemporaryBasalsDataFromTime(long mills, boolean ascending) { try { List tempbasals; QueryBuilder queryBuilder = getDaoTemporaryBasal().queryBuilder(); queryBuilder.orderBy("date", ascending); Where where = queryBuilder.where(); where.ge("date", mills); PreparedQuery preparedQuery = queryBuilder.prepare(); tempbasals = getDaoTemporaryBasal().query(preparedQuery); return tempbasals; } catch (SQLException e) { log.error("Unhandled exception", e); } return new ArrayList(); } private static void scheduleTemporaryBasalChange() { class PostRunnable implements Runnable { public void run() { if (L.isEnabled(L.DATABASE)) log.debug("Firing EventTempBasalChange"); MainApp.bus().post(new EventReloadTempBasalData()); MainApp.bus().post(new EventTempBasalChange()); if (earliestDataChange != null) MainApp.bus().post(new EventNewHistoryData(earliestDataChange)); earliestDataChange = null; scheduledTemBasalsPost = null; } } // prepare task for execution in 1 sec // cancel waiting task to prevent sending multiple posts if (scheduledTemBasalsPost != null) scheduledTemBasalsPost.cancel(false); Runnable task = new PostRunnable(); final int sec = 1; scheduledTemBasalsPost = tempBasalsWorker.schedule(task, sec, TimeUnit.SECONDS); } /* * { * "_id": "59232e1ddd032d04218dab00", * "eventType": "Temp Basal", * "duration": 60, * "percent": -50, * "created_at": "2017-05-22T18:29:57Z", * "enteredBy": "AndroidAPS", * "notes": "Basal Temp Start 50% 60.0 min", * "NSCLIENT_ID": 1495477797863, * "mills": 1495477797000, * "mgdl": 194.5, * "endmills": 1495481397000 * } */ public void createTempBasalFromJsonIfNotExists(JSONObject trJson) { try { if (trJson.has("originalExtendedAmount")) { // extended bolus uploaded as temp basal ExtendedBolus extendedBolus = new ExtendedBolus().source(Source.NIGHTSCOUT) .date(trJson.getLong("mills")).pumpId(trJson.has("pumpId") ? trJson.getLong("pumpId") : 0) .durationInMinutes(trJson.getInt("duration")).insulin(trJson.getDouble("originalExtendedAmount")) ._id(trJson.getString("_id")); // if faking found in NS, adapt AAPS to use it too if (!VirtualPumpPlugin.getPlugin().getFakingStatus()) { VirtualPumpPlugin.getPlugin().setFakingStatus(true); updateEarliestDataChange(0); scheduleTemporaryBasalChange(); } createOrUpdate(extendedBolus); } else if (trJson.has("isFakedTempBasal")) { // extended bolus end uploaded as temp basal end ExtendedBolus extendedBolus = new ExtendedBolus(); extendedBolus.source = Source.NIGHTSCOUT; extendedBolus.date = trJson.getLong("mills"); extendedBolus.pumpId = trJson.has("pumpId") ? trJson.getLong("pumpId") : 0; extendedBolus.durationInMinutes = 0; extendedBolus.insulin = 0; extendedBolus._id = trJson.getString("_id"); // if faking found in NS, adapt AAPS to use it too if (!VirtualPumpPlugin.getPlugin().getFakingStatus()) { VirtualPumpPlugin.getPlugin().setFakingStatus(true); updateEarliestDataChange(0); scheduleTemporaryBasalChange(); } createOrUpdate(extendedBolus); } else { TemporaryBasal tempBasal = new TemporaryBasal().date(trJson.getLong("mills")).source(Source.NIGHTSCOUT) .pumpId(trJson.has("pumpId") ? trJson.getLong("pumpId") : 0); if (trJson.has("duration")) { tempBasal.durationInMinutes = trJson.getInt("duration"); } if (trJson.has("percent")) { tempBasal.percentRate = trJson.getInt("percent") + 100; tempBasal.isAbsolute = false; } if (trJson.has("absolute")) { tempBasal.absoluteRate = trJson.getDouble("absolute"); tempBasal.isAbsolute = true; } tempBasal._id = trJson.getString("_id"); createOrUpdate(tempBasal); } } catch (JSONException e) { log.error("Unhandled exception: " + trJson.toString(), e); } } public void deleteTempBasalById(String _id) { TemporaryBasal stored = findTempBasalById(_id); if (stored != null) { if (L.isEnabled(L.DATABASE)) log.debug("TEMPBASAL: Removing TempBasal record from database: " + stored.toString()); delete(stored); updateEarliestDataChange(stored.date); scheduleTemporaryBasalChange(); } } public TemporaryBasal findTempBasalById(String _id) { try { QueryBuilder queryBuilder = null; queryBuilder = getDaoTemporaryBasal().queryBuilder(); Where where = queryBuilder.where(); where.eq("_id", _id); PreparedQuery preparedQuery = queryBuilder.prepare(); List list = getDaoTemporaryBasal().query(preparedQuery); if (list.size() != 1) { return null; } else { return list.get(0); } } catch (SQLException e) { log.error("Unhandled exception", e); } return null; } // ------------ ExtendedBolus handling --------------- public boolean createOrUpdate(ExtendedBolus extendedBolus) { try { if (L.isEnabled(L.DATABASE)) log.debug("EXTENDEDBOLUS: createOrUpdate: " + Source.getString(extendedBolus.source) + " " + extendedBolus.log()); ExtendedBolus old; extendedBolus.date = roundDateToSec(extendedBolus.date); if (extendedBolus.source == Source.PUMP) { // if pumpId == 0 do not check for existing pumpId // used with pumps without history // and insight where record as added first without pumpId // and then is record updated with pumpId if (extendedBolus.pumpId == 0) { getDaoExtendedBolus().createOrUpdate(extendedBolus); } else { QueryBuilder queryBuilder = getDaoExtendedBolus().queryBuilder(); Where where = queryBuilder.where(); where.eq("pumpId", extendedBolus.pumpId); PreparedQuery preparedQuery = queryBuilder.prepare(); List trList = getDaoExtendedBolus().query(preparedQuery); if (trList.size() > 1) { log.error("EXTENDEDBOLUS: Multiple records found for pumpId: " + extendedBolus.pumpId); return false; } getDaoExtendedBolus().createOrUpdate(extendedBolus); } if (L.isEnabled(L.DATABASE)) log.debug("EXTENDEDBOLUS: New record from: " + Source.getString(extendedBolus.source) + " " + extendedBolus.log()); updateEarliestDataChange(extendedBolus.date); scheduleExtendedBolusChange(); return true; } if (extendedBolus.source == Source.NIGHTSCOUT) { old = getDaoExtendedBolus().queryForId(extendedBolus.date); if (old != null) { if (!old.isEqual(extendedBolus)) { long oldDate = old.date; getDaoExtendedBolus().delete(old); // need to delete/create because date may change too old.copyFrom(extendedBolus); getDaoExtendedBolus().create(old); if (L.isEnabled(L.DATABASE)) log.debug("EXTENDEDBOLUS: Updating record by date from: " + Source.getString(extendedBolus.source) + " " + old.log()); updateEarliestDataChange(oldDate); updateEarliestDataChange(old.date); scheduleExtendedBolusChange(); return true; } return false; } // find by NS _id if (extendedBolus._id != null) { QueryBuilder queryBuilder = getDaoExtendedBolus().queryBuilder(); Where where = queryBuilder.where(); where.eq("_id", extendedBolus._id); PreparedQuery preparedQuery = queryBuilder.prepare(); List trList = getDaoExtendedBolus().query(preparedQuery); if (trList.size() > 0) { old = trList.get(0); if (!old.isEqual(extendedBolus)) { long oldDate = old.date; getDaoExtendedBolus().delete(old); // need to delete/create because date may change too old.copyFrom(extendedBolus); getDaoExtendedBolus().create(old); if (L.isEnabled(L.DATABASE)) log.debug("EXTENDEDBOLUS: Updating record by _id from: " + Source.getString(extendedBolus.source) + " " + old.log()); updateEarliestDataChange(oldDate); updateEarliestDataChange(old.date); scheduleExtendedBolusChange(); return true; } } } getDaoExtendedBolus().create(extendedBolus); if (L.isEnabled(L.DATABASE)) log.debug("EXTENDEDBOLUS: New record from: " + Source.getString(extendedBolus.source) + " " + extendedBolus.log()); updateEarliestDataChange(extendedBolus.date); scheduleExtendedBolusChange(); return true; } if (extendedBolus.source == Source.USER) { getDaoExtendedBolus().create(extendedBolus); if (L.isEnabled(L.DATABASE)) log.debug("EXTENDEDBOLUS: New record from: " + Source.getString(extendedBolus.source) + " " + extendedBolus.log()); updateEarliestDataChange(extendedBolus.date); scheduleExtendedBolusChange(); return true; } } catch (SQLException e) { log.error("Unhandled exception", e); } return false; } public void delete(ExtendedBolus extendedBolus) { try { getDaoExtendedBolus().delete(extendedBolus); updateEarliestDataChange(extendedBolus.date); } catch (SQLException e) { log.error("Unhandled exception", e); } scheduleExtendedBolusChange(); } public List getExtendedBolusDataFromTime(long mills, boolean ascending) { try { List extendedBoluses; QueryBuilder queryBuilder = getDaoExtendedBolus().queryBuilder(); queryBuilder.orderBy("date", ascending); Where where = queryBuilder.where(); where.ge("date", mills); PreparedQuery preparedQuery = queryBuilder.prepare(); extendedBoluses = getDaoExtendedBolus().query(preparedQuery); return extendedBoluses; } catch (SQLException e) { log.error("Unhandled exception", e); } return new ArrayList(); } public void deleteExtendedBolusById(String _id) { ExtendedBolus stored = findExtendedBolusById(_id); if (stored != null) { if (L.isEnabled(L.DATABASE)) log.debug("EXTENDEDBOLUS: Removing ExtendedBolus record from database: " + stored.toString()); delete(stored); updateEarliestDataChange(stored.date); scheduleExtendedBolusChange(); } } public ExtendedBolus findExtendedBolusById(String _id) { try { QueryBuilder queryBuilder = null; queryBuilder = getDaoExtendedBolus().queryBuilder(); Where where = queryBuilder.where(); where.eq("_id", _id); PreparedQuery preparedQuery = queryBuilder.prepare(); List list = getDaoExtendedBolus().query(preparedQuery); if (list.size() == 1) { return list.get(0); } else { return null; } } catch (SQLException e) { log.error("Unhandled exception", e); } return null; } /* * { * "_id": "5924898d577eb0880e355337", * "eventType": "Combo Bolus", * "duration": 120, * "splitNow": 0, * "splitExt": 100, * "enteredinsulin": 1, * "relative": 1, * "created_at": "2017-05-23T19:12:14Z", * "enteredBy": "AndroidAPS", * "NSCLIENT_ID": 1495566734628, * "mills": 1495566734000, * "mgdl": 106 * } */ public void createExtendedBolusFromJsonIfNotExists(JSONObject json) { ExtendedBolus extendedBolus = ExtendedBolus.createFromJson(json); if (extendedBolus != null) createOrUpdate(extendedBolus); } private static void scheduleExtendedBolusChange() { class PostRunnable implements Runnable { public void run() { if (L.isEnabled(L.DATABASE)) log.debug("Firing EventExtendedBolusChange"); MainApp.bus().post(new EventReloadTreatmentData(new EventExtendedBolusChange())); if (earliestDataChange != null) MainApp.bus().post(new EventNewHistoryData(earliestDataChange)); earliestDataChange = null; scheduledExtendedBolusPost = null; } } // prepare task for execution in 1 sec // cancel waiting task to prevent sending multiple posts if (scheduledExtendedBolusPost != null) scheduledExtendedBolusPost.cancel(false); Runnable task = new PostRunnable(); final int sec = 1; scheduledExtendedBolusPost = extendedBolusWorker.schedule(task, sec, TimeUnit.SECONDS); } // ------------ CareportalEvent handling --------------- public void createOrUpdate(CareportalEvent careportalEvent) { careportalEvent.date = careportalEvent.date - careportalEvent.date % 1000; try { getDaoCareportalEvents().createOrUpdate(careportalEvent); } catch (SQLException e) { log.error("Unhandled exception", e); } scheduleCareportalEventChange(); } public void delete(CareportalEvent careportalEvent) { try { getDaoCareportalEvents().delete(careportalEvent); } catch (SQLException e) { log.error("Unhandled exception", e); } scheduleCareportalEventChange(); } public CareportalEvent getCareportalEventFromTimestamp(long timestamp) { try { return getDaoCareportalEvents().queryForId(timestamp); } catch (SQLException e) { log.error("Unhandled exception", e); } return null; } @Nullable public CareportalEvent getLastCareportalEvent(String event) { try { List careportalEvents; QueryBuilder queryBuilder = getDaoCareportalEvents().queryBuilder(); queryBuilder.orderBy("date", false); Where where = queryBuilder.where(); where.eq("eventType", event); queryBuilder.limit(1L); PreparedQuery preparedQuery = queryBuilder.prepare(); careportalEvents = getDaoCareportalEvents().query(preparedQuery); if (careportalEvents.size() == 1) return careportalEvents.get(0); else return null; } catch (SQLException e) { log.error("Unhandled exception", e); } return null; } public List getCareportalEventsFromTime(long mills, boolean ascending) { try { List careportalEvents; QueryBuilder queryBuilder = getDaoCareportalEvents().queryBuilder(); queryBuilder.orderBy("date", ascending); Where where = queryBuilder.where(); where.ge("date", mills); PreparedQuery preparedQuery = queryBuilder.prepare(); careportalEvents = getDaoCareportalEvents().query(preparedQuery); preprocessOpenAPSOfflineEvents(careportalEvents); return careportalEvents; } catch (SQLException e) { log.error("Unhandled exception", e); } return new ArrayList<>(); } public void preprocessOpenAPSOfflineEvents(List list) { OverlappingIntervals offlineEvents = new OverlappingIntervals(); for (int i = 0; i < list.size(); i++) { CareportalEvent event = list.get(i); if (!event.eventType.equals(CareportalEvent.OPENAPSOFFLINE)) continue; offlineEvents.add(event); } } public List getCareportalEventsFromTime(long mills, String type, boolean ascending) { try { List careportalEvents; QueryBuilder queryBuilder = getDaoCareportalEvents().queryBuilder(); queryBuilder.orderBy("date", ascending); Where where = queryBuilder.where(); where.ge("date", mills).and().eq("eventType", type); PreparedQuery preparedQuery = queryBuilder.prepare(); careportalEvents = getDaoCareportalEvents().query(preparedQuery); preprocessOpenAPSOfflineEvents(careportalEvents); return careportalEvents; } catch (SQLException e) { log.error("Unhandled exception", e); } return new ArrayList<>(); } public List getCareportalEvents(boolean ascending) { try { List careportalEvents; QueryBuilder queryBuilder = getDaoCareportalEvents().queryBuilder(); queryBuilder.orderBy("date", ascending); PreparedQuery preparedQuery = queryBuilder.prepare(); careportalEvents = getDaoCareportalEvents().query(preparedQuery); preprocessOpenAPSOfflineEvents(careportalEvents); return careportalEvents; } catch (SQLException e) { log.error("Unhandled exception", e); } return new ArrayList<>(); } public void deleteCareportalEventById(String _id) { try { QueryBuilder queryBuilder; queryBuilder = getDaoCareportalEvents().queryBuilder(); Where where = queryBuilder.where(); where.eq("_id", _id); PreparedQuery preparedQuery = queryBuilder.prepare(); List list = getDaoCareportalEvents().query(preparedQuery); if (list.size() == 1) { CareportalEvent record = list.get(0); if (L.isEnabled(L.DATABASE)) log.debug("Removing CareportalEvent record from database: " + record.toString()); delete(record); } else { if (L.isEnabled(L.DATABASE)) log.debug("CareportalEvent not found database: " + _id); } } catch (SQLException e) { log.error("Unhandled exception", e); } } public void createCareportalEventFromJsonIfNotExists(JSONObject trJson) { try { QueryBuilder queryBuilder; queryBuilder = getDaoCareportalEvents().queryBuilder(); Where where = queryBuilder.where(); where.eq("_id", trJson.getString("_id")).or().eq("date", trJson.getLong("mills")); PreparedQuery preparedQuery = queryBuilder.prepare(); List list = getDaoCareportalEvents().query(preparedQuery); CareportalEvent careportalEvent; if (list.size() == 0) { careportalEvent = new CareportalEvent(); careportalEvent.source = Source.NIGHTSCOUT; if (L.isEnabled(L.DATABASE)) log.debug("Adding CareportalEvent record to database: " + trJson.toString()); // Record does not exists. add } else if (list.size() == 1) { careportalEvent = list.get(0); if (L.isEnabled(L.DATABASE)) log.debug("Updating CareportalEvent record in database: " + trJson.toString()); } else { log.error("Something went wrong"); return; } careportalEvent.date = trJson.getLong("mills"); careportalEvent.eventType = trJson.getString("eventType"); careportalEvent.json = trJson.toString(); careportalEvent._id = trJson.getString("_id"); createOrUpdate(careportalEvent); } catch (SQLException | JSONException e) { log.error("Unhandled exception: " + trJson.toString(), e); } } private static void scheduleCareportalEventChange() { class PostRunnable implements Runnable { public void run() { if (L.isEnabled(L.DATABASE)) log.debug("Firing scheduleCareportalEventChange"); MainApp.bus().post(new EventCareportalEventChange()); scheduledCareportalEventPost = null; } } // prepare task for execution in 1 sec // cancel waiting task to prevent sending multiple posts if (scheduledCareportalEventPost != null) scheduledCareportalEventPost.cancel(false); Runnable task = new PostRunnable(); final int sec = 1; scheduledCareportalEventPost = careportalEventWorker.schedule(task, sec, TimeUnit.SECONDS); } // ---------------- ProfileSwitch handling --------------- public List getProfileSwitchData(boolean ascending) { try { Dao daoProfileSwitch = getDaoProfileSwitch(); List profileSwitches; QueryBuilder queryBuilder = daoProfileSwitch.queryBuilder(); queryBuilder.orderBy("date", ascending); queryBuilder.limit(100L); PreparedQuery preparedQuery = queryBuilder.prepare(); profileSwitches = daoProfileSwitch.query(preparedQuery); return profileSwitches; } catch (SQLException e) { log.error("Unhandled exception", e); } return new ArrayList<>(); } public List getProfileSwitchEventsFromTime(long mills, boolean ascending) { try { Dao daoProfileSwitch = getDaoProfileSwitch(); List profileSwitches; QueryBuilder queryBuilder = daoProfileSwitch.queryBuilder(); queryBuilder.orderBy("date", ascending); queryBuilder.limit(100L); Where where = queryBuilder.where(); where.ge("date", mills); PreparedQuery preparedQuery = queryBuilder.prepare(); profileSwitches = daoProfileSwitch.query(preparedQuery); return profileSwitches; } catch (SQLException e) { log.error("Unhandled exception", e); } return new ArrayList<>(); } public boolean createOrUpdate(ProfileSwitch profileSwitch) { try { ProfileSwitch old; profileSwitch.date = roundDateToSec(profileSwitch.date); if (profileSwitch.source == Source.NIGHTSCOUT) { old = getDaoProfileSwitch().queryForId(profileSwitch.date); if (old != null) { if (!old.isEqual(profileSwitch)) { profileSwitch.source = old.source; profileSwitch.profileName = old.profileName; // preserver profileName to prevent multiple CPP // extension getDaoProfileSwitch().delete(old); // need to delete/create because date may change too getDaoProfileSwitch().create(profileSwitch); if (L.isEnabled(L.DATABASE)) log.debug("PROFILESWITCH: Updating record by date from: " + Source.getString(profileSwitch.source) + " " + old.toString()); scheduleProfileSwitchChange(); return true; } return false; } // find by NS _id if (profileSwitch._id != null) { QueryBuilder queryBuilder = getDaoProfileSwitch().queryBuilder(); Where where = queryBuilder.where(); where.eq("_id", profileSwitch._id); PreparedQuery preparedQuery = queryBuilder.prepare(); List trList = getDaoProfileSwitch().query(preparedQuery); if (trList.size() > 0) { old = trList.get(0); if (!old.isEqual(profileSwitch)) { getDaoProfileSwitch().delete(old); // need to delete/create because date may change too old.copyFrom(profileSwitch); getDaoProfileSwitch().create(old); if (L.isEnabled(L.DATABASE)) log.debug("PROFILESWITCH: Updating record by _id from: " + Source.getString(profileSwitch.source) + " " + old.toString()); scheduleProfileSwitchChange(); return true; } } } // look for already added percentage from NS profileSwitch.profileName = PercentageSplitter.pureName(profileSwitch.profileName); getDaoProfileSwitch().create(profileSwitch); if (L.isEnabled(L.DATABASE)) log.debug("PROFILESWITCH: New record from: " + Source.getString(profileSwitch.source) + " " + profileSwitch.toString()); scheduleProfileSwitchChange(); return true; } if (profileSwitch.source == Source.USER) { getDaoProfileSwitch().create(profileSwitch); if (L.isEnabled(L.DATABASE)) log.debug("PROFILESWITCH: New record from: " + Source.getString(profileSwitch.source) + " " + profileSwitch.toString()); scheduleProfileSwitchChange(); return true; } } catch (SQLException e) { log.error("Unhandled exception", e); } return false; } public void delete(ProfileSwitch profileSwitch) { try { getDaoProfileSwitch().delete(profileSwitch); scheduleProfileSwitchChange(); } catch (SQLException e) { log.error("Unhandled exception", e); } } private static void scheduleProfileSwitchChange() { class PostRunnable implements Runnable { public void run() { if (L.isEnabled(L.DATABASE)) log.debug("Firing EventProfileSwitchChange"); MainApp.bus().post(new EventReloadProfileSwitchData()); MainApp.bus().post(new EventProfileSwitchChange()); scheduledProfileSwitchEventPost = null; } } // prepare task for execution in 1 sec // cancel waiting task to prevent sending multiple posts if (scheduledProfileSwitchEventPost != null) scheduledProfileSwitchEventPost.cancel(false); Runnable task = new PostRunnable(); final int sec = 1; scheduledProfileSwitchEventPost = profileSwitchEventWorker.schedule(task, sec, TimeUnit.SECONDS); } /* * { * "_id":"592fa43ed97496a80da913d2", * "created_at":"2017-06-01T05:20:06Z", * "eventType":"Profile Switch", * "profile":"2016 +30%", * "units":"mmol", * "enteredBy":"sony", * "NSCLIENT_ID":1496294454309, * } */ public void createProfileSwitchFromJsonIfNotExists(JSONObject trJson) { try { ProfileSwitch profileSwitch = new ProfileSwitch(); profileSwitch.date = trJson.getLong("mills"); if (trJson.has("duration")) profileSwitch.durationInMinutes = trJson.getInt("duration"); profileSwitch._id = trJson.getString("_id"); profileSwitch.profileName = trJson.getString("profile"); profileSwitch.isCPP = trJson.has("CircadianPercentageProfile"); profileSwitch.source = Source.NIGHTSCOUT; if (trJson.has("timeshift")) profileSwitch.timeshift = trJson.getInt("timeshift"); if (trJson.has("percentage")) profileSwitch.percentage = trJson.getInt("percentage"); if (trJson.has("profileJson")) profileSwitch.profileJson = trJson.getString("profileJson"); else { ProfileInterface profileInterface = ConfigBuilderPlugin.getPlugin().getActiveProfileInterface(); if (profileInterface != null) { ProfileStore store = profileInterface.getProfile(); if (store != null) { Profile profile = store.getSpecificProfile(profileSwitch.profileName); if (profile != null) { profileSwitch.profileJson = profile.getData().toString(); if (L.isEnabled(L.DATABASE)) log.debug("Profile switch prefilled with JSON from local store"); // Update data in NS NSUpload.updateProfileSwitch(profileSwitch); } else { if (L.isEnabled(L.DATABASE)) log.debug("JSON for profile switch doesn't exist. Ignoring: " + trJson.toString()); return; } } else { if (L.isEnabled(L.DATABASE)) log.debug("Store for profile switch doesn't exist. Ignoring: " + trJson.toString()); return; } } else { if (L.isEnabled(L.DATABASE)) log.debug("No active profile interface. Ignoring: " + trJson.toString()); return; } } if (trJson.has("profilePlugin")) profileSwitch.profilePlugin = trJson.getString("profilePlugin"); createOrUpdate(profileSwitch); } catch (JSONException e) { log.error("Unhandled exception: " + trJson.toString(), e); } } public void deleteProfileSwitchById(String _id) { ProfileSwitch stored = findProfileSwitchById(_id); if (stored != null) { if (L.isEnabled(L.DATABASE)) log.debug("PROFILESWITCH: Removing ProfileSwitch record from database: " + stored.toString()); delete(stored); scheduleTemporaryTargetChange(); } } public ProfileSwitch findProfileSwitchById(String _id) { try { QueryBuilder queryBuilder = getDaoProfileSwitch().queryBuilder(); Where where = queryBuilder.where(); where.eq("_id", _id); PreparedQuery preparedQuery = queryBuilder.prepare(); List list = getDaoProfileSwitch().query(preparedQuery); if (list.size() == 1) { return list.get(0); } else { return null; } } catch (SQLException e) { log.error("Unhandled exception", e); } return null; } // ---------------- Food handling --------------- }